[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/token/ -> token.module (source)

   1  <?php
   2  // $Id: token.module,v 1.7.4.33 2010/08/10 03:33:32 davereid Exp $
   3  
   4  /**
   5   * @file
   6   * The Token API module.
   7   *
   8   * The Token module provides an API for providing tokens to other modules.
   9   * Tokens are small bits of text that can be placed into larger documents
  10   * via simple placeholders, like %site-name or [user].
  11   *
  12   * @ingroup token
  13   */
  14  
  15  /**
  16   * The default token prefix string.
  17   */
  18  define('TOKEN_PREFIX', '[');
  19  
  20  /**
  21   * The default token suffix string.
  22   */
  23  define('TOKEN_SUFFIX', ']');
  24  
  25  /**
  26   * Implements hook_help().
  27   */
  28  function token_help($path, $arg) {
  29    if ($path == 'admin/help#token') {
  30      $output = '<dl>';
  31      $output .= '<dt>' . t('List of the currently available tokens on this site') . '</dt>';
  32      $output .= '<dd>' . theme('token_tree', 'all', TRUE, FALSE) . '</dd>';
  33      $output .= '</dl>';
  34      return $output;
  35    }
  36  }
  37  
  38  /**
  39   * Implements hook_theme().
  40   */
  41  function token_theme() {
  42    return array(
  43      'token_help' => array(
  44        'arguments' => array('type' => 'all', 'prefix' => TOKEN_PREFIX, 'suffix' => TOKEN_SUFFIX),
  45        'file' => 'token.pages.inc',
  46      ),
  47      'token_tree' => array(
  48        'arguments' => array('token_types' => array(), 'global_types' => TRUE , 'click_insert' => TRUE),
  49        'file' => 'token.pages.inc',
  50      ),
  51    );
  52  }
  53  
  54  /**
  55   * Sample implementation of hook_token_values().
  56   *
  57   * @param type
  58   *   A flag indicating the class of substitution tokens to use. If an
  59   *   object is passed in the second param, 'type' should contain the
  60   *   object's type. For example, 'node', 'comment', or 'user'. If your
  61   *   implementation of the hook inserts globally applicable tokens that
  62   *   do not depend on a particular object, it should only return values
  63   *   when $type is 'global'.
  64   * @param object
  65   *   Optionally, the object to use for building substitution values.
  66   *   A node, comment, user, etc.
  67   * @return
  68   *   A keyed array containing the substitution tokens and the substitution
  69   *   values for the passed-in type and object.
  70   */
  71  function token_token_values($type, $object = NULL) {
  72    global $user;
  73    $values = array();
  74  
  75    switch ($type) {
  76      case 'global':
  77        // Current user tokens.
  78        $values['user-name']    = $user->uid ? $user->name : variable_get('anonymous', t('Anonymous'));
  79        $values['user-id']      = $user->uid ? $user->uid : 0;
  80        $values['user-mail']    = $user->uid ? $user->mail : '';
  81  
  82        // Site information tokens.
  83        $values['site-url']     = url('<front>', array('absolute' => TRUE));
  84        $values['site-name']    = check_plain(variable_get('site_name', t('Drupal')));
  85        $values['site-slogan']  = check_plain(variable_get('site_slogan', ''));
  86        $values['site-mission'] = filter_xss_admin(variable_get('site_mission', ''));
  87        $values['site-mail']    = variable_get('site_mail', '');
  88        $values += token_get_date_token_values(NULL, 'site-date-');
  89        $values['site-date'] = $values['site-date-small'];
  90  
  91        // Current page tokens.
  92        $values['current-page-title'] = drupal_get_title();
  93        $alias = drupal_get_path_alias($_GET['q']);
  94        $values['current-page-path'] = $alias;
  95        $values['current-page-path-raw'] = check_plain($alias);
  96        $values['current-page-url'] = url($_GET['q'], array('absolute' => TRUE));
  97  
  98        $page = isset($_GET['page']) ? $_GET['page'] : '';
  99        $pager_page_array = explode(',', $page);
 100        $page = $pager_page_array[0];
 101        $values['current-page-number'] = (int) $page + 1;
 102        $values['page-number'] = $values['current-page-number'];
 103  
 104        break;
 105    }
 106    return $values;
 107  }
 108  
 109  /**
 110   * Sample implementation of hook_token_list(). Documents the individual
 111   * tokens handled by your module.
 112   *
 113   * @param type
 114   *   A flag indicating the class of substitution tokens to return
 115   *   information on. If this is set to 'all', a complete list is being
 116   *   built and your module should return its full list, regardless of
 117   *   type. Global tokens should always be returned, regardless of the
 118   *   $type passed in.
 119   * @return
 120   *   A keyed array listing the substitution tokens. Elements should be
 121   *   in the form of: $list[$type][$token] = $description
 122   */
 123  function token_token_list($type = 'all') {
 124    $tokens = array();
 125  
 126    if ($type == 'global' || $type == 'all') {
 127      // Current user tokens.
 128      $tokens['global']['user-name']    = t('The name of the currently logged in user.');
 129      $tokens['global']['user-id']      = t('The user ID of the currently logged in user.');
 130      $tokens['global']['user-mail']    = t('The email address of the currently logged in user.');
 131  
 132      // Site information tokens.
 133      $tokens['global']['site-url']     = t("The URL of the site's front page.");
 134      $tokens['global']['site-name']    = t('The name of the site.');
 135      $tokens['global']['site-slogan']  = t('The slogan of the site.');
 136      $tokens['global']['site-mission'] = t("The optional 'mission' of the site.");
 137      $tokens['global']['site-mail']    = t('The administrative email address for the site.');
 138      $tokens['global'] += token_get_date_token_info(t('The current'), 'site-date-');
 139  
 140      // Current page tokens.
 141      $tokens['global']['current-page-title']    = t('The title of the current page.');
 142      $tokens['global']['current-page-path']     = t('The URL alias of the current page.');
 143      $tokens['global']['current-page-path-raw'] = t('The URL alias of the current page.');
 144      $tokens['global']['current-page-url']      = t('The URL of the current page.');
 145      $tokens['global']['current-page-number']   = t('The page number of the current page when viewing paged lists.');
 146    }
 147  
 148    return $tokens;
 149  }
 150  
 151  /**
 152   * General function to include the files that token relies on for the real work.
 153   */
 154  function token_include() {
 155    static $run = FALSE;
 156  
 157    if (!$run) {
 158      $run = TRUE;
 159      $modules_enabled = array_keys(module_list());
 160      $modules = array('node', 'user', 'taxonomy', 'comment');
 161      $modules = array_intersect($modules, $modules_enabled);
 162      foreach ($modules as $module) {
 163        module_load_include('inc', 'token', "token_$module");
 164      }
 165    }
 166  }
 167  
 168  /**
 169   * Return the value of $original, with all instances of placeholder
 170   * tokens replaced by their proper values. To replace multiple types
 171   * at once see token_replace_multiple().
 172   *
 173   * @param original
 174   *  A string, or an array of strings, to perform token substitutions
 175   *  on.
 176   * @param type
 177   *   A flag indicating the class of substitution tokens to use. If an
 178   *   object is passed in the second param, 'type' should contain the
 179   *   object's type. For example, 'node', 'comment', or 'user'. If no
 180   *   type is specified, only 'global' site-wide substitution tokens are
 181   *   built.
 182   * @param object
 183   *   Optionally, the object to use for building substitution values.
 184   *   A node, comment, user, etc.
 185   * @param leading
 186   *    Character(s) to prepend to the token key before searching for
 187   *    matches. Defaults to an open-bracket.
 188   * @param trailing
 189   *    Character(s) to append to the token key before searching for
 190   *    matches. Defaults to a close-bracket.
 191   * @param flush
 192   *    A flag indicating whether or not to flush the token cache.
 193   *    Useful for processes that need to slog through huge numbers
 194   *    of tokens in a single execution cycle. Flushing it will
 195   *    keep them from burning through memory.
 196   *    The default is FALSE.
 197   * @return The modified version of $original, with all substitutions
 198   *   made.
 199   */
 200  function token_replace($original, $type = 'global', $object = NULL, $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX, $options = array(), $flush = FALSE) {
 201    $full = token_get_values($type, $object, $flush, $options);
 202    return _token_replace_tokens($original, $full->tokens, $full->values, $leading, $trailing);
 203  }
 204  
 205  /**
 206   * Return the value of $original, with all instances of placeholder
 207   * tokens replaced by their proper values. Contrary to token_replace(),
 208   * this function supports replacing multiple types.
 209   *
 210   * @param original
 211   *  A string, or an array of strings, to perform token substitutions
 212   *  on.
 213   * @param types
 214   *   An array of substitution classes and optional objects. The key is
 215   *   a flag indicating the class of substitution tokens to use.
 216   *   If an object is passed as value, the key should contain the
 217   *   object's type. For example, 'node', 'comment', or 'user'. The
 218   *   object will be used for building substitution values. If no type
 219   *   is specified, only 'global' site-wide substitution tokens are built.
 220   * @param leading
 221   *    Character(s) to prepend to the token key before searching for
 222   *    matches. Defaults to an open-bracket.
 223   * @param trailing
 224   *    Character(s) to append to the token key before searching for
 225   *    matches. Defaults to a close-bracket.
 226   * @param flush
 227   *    A flag indicating whether or not to flush the token cache.
 228   *    Useful for processes that need to slog through huge numbers
 229   *    of tokens in a single execution cycle. Flushing it will
 230   *    keep them from burning through memory.
 231   *    The default is FALSE.
 232   * @return The modified version of $original, with all substitutions
 233   *   made.
 234   */
 235  function token_replace_multiple($original, $types = array('global' => NULL), $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX, $options = array(), $flush = FALSE) {
 236    $full = new stdClass();
 237    $full->tokens = $full->values = array();
 238    foreach ($types as $type => $object) {
 239      $temp = token_get_values($type, $object, $flush, $options);
 240      $full->tokens = array_merge($full->tokens, $temp->tokens);
 241      $full->values = array_merge($full->values, $temp->values);
 242    }
 243    return _token_replace_tokens($original, $full->tokens, $full->values, $leading, $trailing);
 244  }
 245  
 246  // Internally used function to replace tokens.
 247  function _token_replace_tokens($original, $tokens, $values, $leading, $trailing) {
 248    $tokens = token_prepare_tokens($tokens, $leading, $trailing);
 249    return str_replace($tokens, $values, $original);
 250  }
 251  
 252  /**
 253   * Return a list of valid substitution tokens and their values for
 254   * the specified type.
 255   *
 256   * @param type
 257   *   A flag indicating the class of substitution tokens to use. If an
 258   *   object is passed in the second param, 'type' should contain the
 259   *   object's type. For example, 'node', 'comment', or 'user'. If no
 260   *   type is specified, only 'global' site-wide substitution tokens are
 261   *   built.
 262   * @param object
 263   *   Optionally, the object to use for building substitution values.
 264   *   A node, comment, user, etc.
 265   * @param flush
 266   *   A flag indicating whether or not to flush the token cache.
 267   *   Useful for processes that need to slog through huge numbers
 268   *   of tokens in a single execution cycle. Flushing it will
 269   *   keep them from burning through memory.
 270   *   The default is FALSE.
 271   * @return
 272   *   A keyed array containing the substitution tokens and the substitution
 273   *   values for the passed-in type and object.
 274   */
 275  function token_get_values($type = 'global', $object = NULL, $flush = FALSE, $options = array()) {
 276    static $tokens;
 277    static $running;
 278  
 279    // Flush the static token cache. Useful for processes that need to slog through
 280    // huge numbers of tokens in a single execution cycle. Flushing it will keep
 281    // them from burning through memory.
 282    if ($flush || !isset($tokens)) {
 283      $tokens = array();
 284    }
 285  
 286    // Since objects in PHP5 are always passed by reference, we ensure we're
 287    // working on a copy of the object.
 288    if (is_object($object)) {
 289      $object = drupal_clone($object);
 290    }
 291  
 292    // Simple recursion check. This is to avoid content_view()'s potential
 293    // for endless looping when a filter uses tokens, which load the content
 294    // view, which calls the filter, which uses tokens, which...
 295    if ($running) {
 296      // We'll allow things to get two levels deep, but bail out after that
 297      // without performing any substitutions.
 298      $result = new stdClass();
 299      $result->tokens = array();
 300      $result->values = array();
 301      return $result;
 302    }
 303  
 304    $running = TRUE;
 305  
 306    token_include();
 307  
 308    $id = _token_get_id($type, $object);
 309    if ($id && isset($tokens[$type][$id])) {
 310      $tmp_tokens = $tokens[$type][$id];
 311    }
 312    else {
 313      $tmp_tokens = module_invoke_all('token_values', $type, $object, $options);
 314      $tokens[$type][$id] = $tmp_tokens;
 315    }
 316  
 317    // Special-case global tokens, as we always want to be able to process
 318    // those substitutions.
 319    if (!isset($tokens['global']['default'])) {
 320      $tokens['global']['default'] = module_invoke_all('token_values', 'global');
 321    }
 322  
 323    $all = _token_array_merge($tokens['global']['default'], $tokens[$type][$id]);
 324  
 325    $result = new stdClass();
 326    $result->tokens = array_keys($all);
 327    $result->values = array_values($all);
 328  
 329    $running = FALSE;
 330  
 331    return $result;
 332  }
 333  
 334  /**
 335   * Merge two arrays of token values, checking and warning for duplication.
 336   *
 337   * Because array_merge will combinue elements that have the same key into an
 338   * array instead of overriding the existing element, this causes problems since
 339   * we need the values to always be a string.
 340   *
 341   * @param $array1
 342   *   An array of token values keyed by token name.
 343   * @param $array2
 344   *   An array of token values keyed by token name.
 345   * @return
 346   *   A merged array of token values keyed by token name.
 347   */
 348  function _token_array_merge($array1, $array2) {
 349    static $warned = array();
 350  
 351    $merged = array_merge($array1, $array2);
 352    foreach ($merged as $key => $value) {
 353      if (is_array($value)) {
 354        if (empty($warned[$key])) {
 355          // Only warn once about duplicate token.
 356          watchdog('token', 'More than one module has defined the %key token.', array('%key' => $key), WATCHDOG_WARNING);
 357          $warned[$key] = TRUE;
 358        }
 359        // Use the last-defined value (module with lowest weight).
 360        $merged[$key] = array_pop($value);
 361      }
 362    }
 363  
 364    return $merged;
 365  }
 366  
 367  /**
 368   * A helper function that retrieves all currently exposed tokens,
 369   * and merges them recursively. This is only necessary when building
 370   * the token listing -- during actual value replacement, only tokens
 371   * in a particular domain are requested and a normal array_marge() is
 372   * sufficient.
 373   *
 374   * @param types
 375   *   A flag indicating the class of substitution tokens to use. If an
 376   *   object is passed in the second param, 'types' should contain the
 377   *   object's type. For example, 'node', 'comment', or 'user'. 'types'
 378   *   may also be an array of types of the form array('node','user'). If no
 379   *   type is specified, only 'global' site-wide substitution tokens are
 380   *   built.
 381   * @return
 382   *   The array of usable tokens and their descriptions, organized by
 383   *   token type.
 384   */
 385  function token_get_list($types = 'all') {
 386    token_include();
 387    $return = array();
 388    settype($types, 'array');
 389    foreach (module_implements('token_list') as $module) {
 390      $function = $module .'_token_list';
 391      foreach ($types as $type) {
 392        $result = $function($type);
 393        if (is_array($result)) {
 394          foreach ($result as $category => $tokens) {
 395            foreach ($tokens as $token => $title) {
 396              // Automatically append a raw token warning.
 397              if (substr($token, -4) === '-raw' && strpos($title, t('raw user input')) === FALSE && strpos($title, '1269441371') === FALSE) {
 398                $title .= ' <em>' . t('Warning: Token value contains raw user input.') . '</em>';
 399              }
 400              $return[$category][$token] = $title;
 401            }
 402          }
 403        }
 404      }
 405    }
 406    return $return;
 407  }
 408  
 409  /**
 410   * A helper function that transforms all the elements of an
 411   * array. Used to change the delimiter style from brackets to
 412   * percent symbols etc.
 413   *
 414   * @param tokens
 415   *    The array of tokens keys with no delimiting characters
 416   * @param leading
 417   *    Character(s) to prepend to the token key before searching for
 418   *    matches. Defaults to an open-bracket.
 419   * @param trailing
 420   *    Character(s) to append to the token key before searching for
 421   *    matches. Defaults to a close-bracket.
 422   *  @return
 423   *    The array of token keys, each wrapped in the specified
 424   *    delimiter style.
 425   */
 426  function token_prepare_tokens($tokens = array(), $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX) {
 427    foreach ($tokens as $key => $value) {
 428      $tokens[$key] = $leading . $value . $trailing;
 429    }
 430    return $tokens;
 431  }
 432  
 433  // Internal utility function used for static caching. There are
 434  // almost certainly better ways to do this, but for the moment it's
 435  // sufficient.
 436  function _token_get_id($type = 'global', $object = NULL) {
 437    if (!isset($object)) {
 438      return "default";
 439    }
 440    switch ($type) {
 441      case 'node':
 442        return isset($object->nid) ? $object->nid : 0;
 443      case 'comment':
 444        return isset($object->cid) ? $object->cid : 0;
 445      case 'user':
 446        return isset($object->uid) ? $object->uid : 0;
 447      case 'taxonomy':
 448        return isset($object->tid) ? $object->tid : 0;
 449      default:
 450        return crc32(serialize($object));
 451    }
 452  }
 453  
 454  /**
 455   * Build a list of common date tokens for use in hook_token_list().
 456   *
 457   * @param $description
 458   */
 459  function token_get_date_token_info($description, $token_prefix = '') {
 460    $tokens[$token_prefix . 'small']  = t("!description date in 'small' format. <em>(06/07/2010 - 23:28)</em>", array('!description' => $description));
 461    $tokens[$token_prefix . 'yyyy']   = t("!description year (four digit)", array('!description' => $description));
 462    $tokens[$token_prefix . 'yy']     = t("!description year (two digit)", array('!description' => $description));
 463    $tokens[$token_prefix . 'month']  = t("!description month (full word)", array('!description' => $description));
 464    $tokens[$token_prefix . 'mon']    = t("!description month (abbreviated)", array('!description' => $description));
 465    $tokens[$token_prefix . 'mm']     = t("!description month (two digits with leading zeros)", array('!description' => $description));
 466    $tokens[$token_prefix . 'm']      = t("!description month (one or two digits without leading zeros)", array('!description' => $description));
 467    $tokens[$token_prefix . 'ww']     = t("!description week (two digits with leading zeros)", array('!description' => $description));
 468    if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
 469      $tokens[$token_prefix . 'date'] = t("!description date (numeric representation of the day of the week)", array('!description' => $description));
 470    }
 471    $tokens[$token_prefix . 'day']    = t("!description day (full word)", array('!description' => $description));
 472    $tokens[$token_prefix . 'ddd']    = t("!description day (abbreviation)", array('!description' => $description));
 473    $tokens[$token_prefix . 'dd']     = t("!description day (two digits with leading zeros)", array('!description' => $description));
 474    $tokens[$token_prefix . 'd']      = t("!description day (one or two digits without leading zeros)", array('!description' => $description));
 475    $tokens[$token_prefix . 'raw']    = t("!description in UNIX timestamp format (1269441371)", array('!description' => $description));
 476    $tokens[$token_prefix . 'since']  = t("!description in 'time-since' format. (40 years 3 months)", array('!description' => $description));
 477    return $tokens;
 478  }
 479  
 480  /**
 481   * Build a list of common date tokens for use in hook_token_values().
 482   *
 483   * @param $description
 484   */
 485  function token_get_date_token_values($timestamp = NULL, $token_prefix = '') {
 486    $tokens = array();
 487    $time = time();
 488    if (!isset($timestamp)) {
 489      $timestamp = $time;
 490    }
 491    $timezone = variable_get('date_default_timezone', 0);
 492  
 493    $formats = array(
 494      'yyyy' => 'Y',
 495      'yy' => 'y',
 496      'month' => 'F',
 497      'mon' => 'M',
 498      'mm' => 'm',
 499      'm' => 'n',
 500      'ww' => 'W',
 501      //'date' => 'N', // Provided later since it is PHP 5.1 only.
 502      'day' => 'l',
 503      'ddd' => 'D',
 504      'dd' => 'd',
 505      'd' => 'j',
 506    );
 507    foreach ($formats as $token => $format) {
 508      $tokens[$token_prefix . $token] = format_date($timestamp, 'custom', $format, $timezone);
 509    }
 510  
 511    $tokens[$token_prefix . 'small'] = format_date($timestamp, 'small', '', $timezone);
 512    $tokens[$token_prefix . 'raw'] = $timestamp;
 513    $tokens[$token_prefix . 'since'] = format_interval($time - $timestamp);
 514  
 515    $timestamp += $timezone;
 516    if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
 517      $tokens[$token_prefix . 'date'] = gmdate('N', $timestamp);
 518    }
 519  
 520    return $tokens;
 521  }
 522  
 523  /**
 524   * Validate an tokens in raw text based on possible contexts.
 525   *
 526   * @param $text
 527   *   A string with the raw text containing the raw tokens.
 528   * @param $valid_types
 529   *   An array of token types to validage against.
 530   * @param leading
 531   *    Character(s) to prepend to the token key before searching for
 532   *    matches. Defaults to an open-bracket.
 533   * @param trailing
 534   *    Character(s) to append to the token key before searching for
 535   *    matches. Defaults to a close-bracket.
 536   * @return
 537   *   An array with the invalid tokens in their original raw forms.
 538   */
 539  function token_get_invalid_tokens_by_context($text, $valid_types = array(), $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX) {
 540    // Add the token types that are always valid in global context.
 541    $valid_types[] = 'global';
 542  
 543    $invalid_tokens = array();
 544    $valid_tokens = token_get_list($valid_types);
 545    foreach (token_scan($text, $leading, $trailing) as $token) {
 546      $found = FALSE;
 547      foreach ($valid_tokens as $category => $tokens) {
 548        if (isset($tokens[$token])) {
 549          $found = TRUE;
 550          break;
 551        }
 552      }
 553      if (!$found) {
 554        $invalid_tokens[] = $token;
 555      }
 556    }
 557  
 558    array_unique($invalid_tokens);
 559    $invalid_tokens = token_prepare_tokens($invalid_tokens, $leading, $trailing);
 560    return $invalid_tokens;
 561  }
 562  
 563  /**
 564   * Build a list of all token-like patterns that appear in the text.
 565   *
 566   * @param $text
 567   *   The text to be scanned for possible tokens.
 568   * @param leading
 569   *    Character(s) to prepend to the token key before searching for
 570   *    matches. Defaults to an open-bracket.
 571   * @param trailing
 572   *    Character(s) to append to the token key before searching for
 573   *    matches. Defaults to a close-bracket.
 574   * @return
 575   *   An array of discovered tokens.
 576   */
 577  function token_scan($text, $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX) {
 578    // Matches tokens with the following pattern: [$token]
 579    $leading = preg_quote($leading, '/');
 580    $trailing = preg_quote($trailing, '/');
 581    preg_match_all("/{$leading}([^\s]+?){$trailing}/", $text, $matches);
 582    return $matches[1];
 583  }
 584  
 585  /**
 586   * Validate a form element that should have tokens in it.
 587   *
 588   * Form elements that want to add this validation should have the #token_types
 589   * parameter defined.
 590   *
 591   * For example:
 592   * @code
 593   * $form['my_node_text_element'] = array(
 594   *   '#type' => 'textfield',
 595   *   '#title' => t('Some text to token-ize that has a node context.'),
 596   *   '#default_value' => 'The title of this node is [node:title].',
 597   *   '#element_validate' => array('token_element_validate_token_context'),
 598   *   '#token_types' => array('node'),
 599   * );
 600   * @endcode
 601   */
 602  function token_element_validate_token_context(&$element, &$form_state) {
 603    $value = isset($element['#value']) ? $element['#value'] : $element['#default_value'];
 604    $invalid_tokens = token_get_invalid_tokens_by_context($value, $element['#token_types']);
 605    if ($invalid_tokens) {
 606      form_error($element, t('The %element-title is using the following invalid tokens: @invalid-tokens.', array('%element-title' => $element['#title'], '@invalid-tokens' => implode(', ', $invalid_tokens))));
 607    }
 608    return $element;
 609  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7