Verzeichnisstruktur phpBB-3.0.0


Veröffentlicht
12.12.2007

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

functions_template.php

Zuletzt modifiziert: 09.10.2024, 12:50 - Dateigröße: 20.85 KiB


 001  <?php
 002  /**
 003  *
 004  * @package phpBB3
 005  * @version $Id$
 006  * @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc
 007  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 008  *
 009  */
 010   
 011  /**
 012  * @ignore
 013  */
 014  if (!defined('IN_PHPBB'))
 015  {
 016      exit;
 017  }
 018   
 019  /**
 020  * Extension of template class - Functions needed for compiling templates only.
 021  *
 022  * psoTFX, phpBB Development Team - Completion of file caching, decompilation
 023  * routines and implementation of conditionals/keywords and associated changes
 024  *
 025  * The interface was inspired by PHPLib templates,  and the template file (formats are
 026  * quite similar)
 027  *
 028  * The keyword/conditional implementation is currently based on sections of code from
 029  * the Smarty templating engine (c) 2001 ispi of Lincoln, Inc. which is released
 030  * (on its own and in whole) under the LGPL. Section 3 of the LGPL states that any code
 031  * derived from an LGPL application may be relicenced under the GPL, this applies
 032  * to this source
 033  *
 034  * DEFINE directive inspired by a request by Cyberalien
 035  *
 036  * @package phpBB3
 037  */
 038  class template_compile
 039  {
 040      var $template;
 041   
 042      // Various storage arrays
 043      var $block_names = array();
 044      var $block_else_level = array();
 045   
 046      /**
 047      * constuctor
 048      */
 049      function template_compile(&$template)
 050      {
 051          $this->template = &$template;
 052      }
 053      
 054      /**
 055      * Load template source from file
 056      * @access private
 057      */
 058      function _tpl_load_file($handle, $store_in_db = false)
 059      {
 060          // Try and open template for read
 061          if (!file_exists($this->template->files[$handle]))
 062          {
 063              trigger_error("template->_tpl_load_file(): File {$this->template->files[$handle]} does not exist or is empty", E_USER_ERROR);
 064          }
 065   
 066          $this->template->compiled_code[$handle] = $this->compile(trim(@file_get_contents($this->template->files[$handle])));
 067   
 068          // Actually compile the code now.
 069          $this->compile_write($handle, $this->template->compiled_code[$handle]);
 070   
 071          // Store in database if required...
 072          if ($store_in_db)
 073          {
 074              global $db, $user;
 075   
 076              $sql_ary = array(
 077                  'template_id'            => $user->theme['template_id'],
 078                  'template_filename'        => $this->template->filename[$handle],
 079                  'template_included'        => '',
 080                  'template_mtime'        => time(),
 081                  'template_data'            => trim(@file_get_contents($this->template->files[$handle])),
 082              );
 083   
 084              $sql = 'INSERT INTO ' . STYLES_TEMPLATE_DATA_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
 085              $db->sql_query($sql);
 086          }
 087      }
 088   
 089      /**
 090      * Remove any PHP tags that do not belong, these regular expressions are derived from
 091      * the ones that exist in zend_language_scanner.l
 092      * @access private
 093      */
 094      function remove_php_tags(&$code)
 095      {
 096          // This matches the information gathered from the internal PHP lexer
 097          $match = array(
 098              '#<([\?%])=?.*?\1>#s',
 099              '#<script\s+language\s*=\s*(["\']?)php\1\s*>.*?</script\s*>#s',
 100              '#<\?php(?:\r\n?|[ \n\t]).*?\?>#s'
 101          );
 102   
 103          $code = preg_replace($match, '', $code);
 104      }
 105   
 106      /**
 107      * The all seeing all doing compile method. Parts are inspired by or directly from Smarty
 108      * @access private
 109      */
 110      function compile($code, $no_echo = false, $echo_var = '')
 111      {
 112          global $config;
 113   
 114          if ($echo_var)
 115          {
 116              global $$echo_var;
 117          }
 118   
 119          // Remove any "loose" php ... we want to give admins the ability
 120          // to switch on/off PHP for a given template. Allowing unchecked
 121          // php is a no-no. There is a potential issue here in that non-php
 122          // content may be removed ... however designers should use entities
 123          // if they wish to display < and >
 124          $this->remove_php_tags($code);
 125   
 126          // Pull out all block/statement level elements and separate plain text
 127          preg_match_all('#<!-- PHP -->(.*?)<!-- ENDPHP -->#s', $code, $matches);
 128          $php_blocks = $matches[1];
 129          $code = preg_replace('#<!-- PHP -->.*?<!-- ENDPHP -->#s', '<!-- PHP -->', $code);
 130   
 131          preg_match_all('#<!-- INCLUDE ([a-zA-Z0-9\_\-\+\./]+) -->#', $code, $matches);
 132          $include_blocks = $matches[1];
 133          $code = preg_replace('#<!-- INCLUDE [a-zA-Z0-9\_\-\+\./]+ -->#', '<!-- INCLUDE -->', $code);
 134   
 135          preg_match_all('#<!-- INCLUDEPHP ([a-zA-Z0-9\_\-\+\./]+) -->#', $code, $matches);
 136          $includephp_blocks = $matches[1];
 137          $code = preg_replace('#<!-- INCLUDEPHP [a-zA-Z0-9\_\-\+\./]+ -->#', '<!-- INCLUDEPHP -->', $code);
 138   
 139          preg_match_all('#<!-- ([^<].*?) (.*?)? ?-->#', $code, $blocks, PREG_SET_ORDER);
 140   
 141          $text_blocks = preg_split('#<!-- [^<].*? (?:.*?)? ?-->#', $code);
 142   
 143          for ($i = 0, $j = sizeof($text_blocks); $i < $j; $i++)
 144          {
 145              $this->compile_var_tags($text_blocks[$i]);
 146          }
 147          $compile_blocks = array();
 148   
 149          for ($curr_tb = 0, $tb_size = sizeof($blocks); $curr_tb < $tb_size; $curr_tb++)
 150          {
 151              $block_val = &$blocks[$curr_tb];
 152   
 153              switch ($block_val[1])
 154              {
 155                  case 'BEGIN':
 156                      $this->block_else_level[] = false;
 157                      $compile_blocks[] = '<?php ' . $this->compile_tag_block($block_val[2]) . ' ?>';
 158                  break;
 159   
 160                  case 'BEGINELSE':
 161                      $this->block_else_level[sizeof($this->block_else_level) - 1] = true;
 162                      $compile_blocks[] = '<?php }} else { ?>';
 163                  break;
 164   
 165                  case 'END':
 166                      array_pop($this->block_names);
 167                      $compile_blocks[] = '<?php ' . ((array_pop($this->block_else_level)) ? '}' : '}}') . ' ?>';
 168                  break;
 169   
 170                  case 'IF':
 171                      $compile_blocks[] = '<?php ' . $this->compile_tag_if($block_val[2], false) . ' ?>';
 172                  break;
 173   
 174                  case 'ELSE':
 175                      $compile_blocks[] = '<?php } else { ?>';
 176                  break;
 177   
 178                  case 'ELSEIF':
 179                      $compile_blocks[] = '<?php ' . $this->compile_tag_if($block_val[2], true) . ' ?>';
 180                  break;
 181   
 182                  case 'ENDIF':
 183                      $compile_blocks[] = '<?php } ?>';
 184                  break;
 185   
 186                  case 'DEFINE':
 187                      $compile_blocks[] = '<?php ' . $this->compile_tag_define($block_val[2], true) . ' ?>';
 188                  break;
 189   
 190                  case 'UNDEFINE':
 191                      $compile_blocks[] = '<?php ' . $this->compile_tag_define($block_val[2], false) . ' ?>';
 192                  break;
 193   
 194                  case 'INCLUDE':
 195                      $temp = array_shift($include_blocks);
 196                      $compile_blocks[] = '<?php ' . $this->compile_tag_include($temp) . ' ?>';
 197                      $this->template->_tpl_include($temp, false);
 198                  break;
 199   
 200                  case 'INCLUDEPHP':
 201                      $compile_blocks[] = ($config['tpl_allow_php']) ? '<?php ' . $this->compile_tag_include_php(array_shift($includephp_blocks)) . ' ?>' : '';
 202                  break;
 203   
 204                  case 'PHP':
 205                      $compile_blocks[] = ($config['tpl_allow_php']) ? '<?php ' . array_shift($php_blocks) . ' ?>' : '';
 206                  break;
 207   
 208                  default:
 209                      $this->compile_var_tags($block_val[0]);
 210                      $trim_check = trim($block_val[0]);
 211                      $compile_blocks[] = (!$no_echo) ? ((!empty($trim_check)) ? $block_val[0] : '') : ((!empty($trim_check)) ? $block_val[0] : '');
 212                  break;
 213              }
 214          }
 215   
 216          $template_php = '';
 217          for ($i = 0, $size = sizeof($text_blocks); $i < $size; $i++)
 218          {
 219              $trim_check_text = trim($text_blocks[$i]);
 220              $template_php .= (!$no_echo) ? (($trim_check_text != '') ? $text_blocks[$i] : '') . ((isset($compile_blocks[$i])) ? $compile_blocks[$i] : '') : (($trim_check_text != '') ? $text_blocks[$i] : '') . ((isset($compile_blocks[$i])) ? $compile_blocks[$i] : '');
 221          }
 222   
 223          // There will be a number of occasions where we switch into and out of
 224          // PHP mode instantaneously. Rather than "burden" the parser with this
 225          // we'll strip out such occurences, minimising such switching
 226          $template_php = str_replace(' ?><?php ', ' ', $template_php);
 227   
 228          return (!$no_echo) ? $template_php : "\$$echo_var .= '" . $template_php . "'";
 229      }
 230   
 231      /**
 232      * Compile variables
 233      * @access private
 234      */
 235      function compile_var_tags(&$text_blocks)
 236      {
 237          // change template varrefs into PHP varrefs
 238          $varrefs = array();
 239   
 240          // This one will handle varrefs WITH namespaces
 241          preg_match_all('#\{((?:[a-z0-9\-_]+\.)+)(\$)?([A-Z0-9\-_]+)\}#', $text_blocks, $varrefs, PREG_SET_ORDER);
 242   
 243          foreach ($varrefs as $var_val)
 244          {
 245              $namespace = $var_val[1];
 246              $varname = $var_val[3];
 247              $new = $this->generate_block_varref($namespace, $varname, true, $var_val[2]);
 248   
 249              $text_blocks = str_replace($var_val[0], $new, $text_blocks);
 250          }
 251   
 252          // This will handle the remaining root-level varrefs
 253          // transform vars prefixed by L_ into their language variable pendant if nothing is set within the tpldata array
 254          if (strpos($text_blocks, '{L_') !== false)
 255          {
 256              $text_blocks = preg_replace('#\{L_([a-z0-9\-_]*)\}#is', "<?php echo ((isset(\$this->_rootref['L_\\1'])) ? \$this->_rootref['L_\\1'] : ((isset(\$user->lang['\\1'])) ? \$user->lang['\\1'] : '{ \\1 }')); ?>", $text_blocks);
 257          }
 258   
 259          // Handle addslashed language variables prefixed with LA_
 260          // If a template variable already exist, it will be used in favor of it...
 261          if (strpos($text_blocks, '{LA_') !== false)
 262          {
 263              $text_blocks = preg_replace('#\{LA_([a-z0-9\-_]*)\}#is', "<?php echo ((isset(\$this->_rootref['LA_\\1'])) ? \$this->_rootref['LA_\\1'] : ((isset(\$this->_rootref['L_\\1'])) ? addslashes(\$this->_rootref['L_\\1']) : ((isset(\$user->lang['\\1'])) ? addslashes(\$user->lang['\\1']) : '{ \\1 }'))); ?>", $text_blocks);
 264          }
 265   
 266          // Handle remaining varrefs
 267          $text_blocks = preg_replace('#\{([a-z0-9\-_]*)\}#is', "<?php echo (isset(\$this->_rootref['\\1'])) ? \$this->_rootref['\\1'] : ''; ?>", $text_blocks);
 268          $text_blocks = preg_replace('#\{\$([a-z0-9\-_]*)\}#is', "<?php echo (isset(\$this->_tpldata['DEFINE']['.']['\\1'])) ? \$this->_tpldata['DEFINE']['.']['\\1'] : ''; ?>", $text_blocks);
 269   
 270          return;
 271      }
 272   
 273      /**
 274      * Compile blocks
 275      * @access private
 276      */
 277      function compile_tag_block($tag_args)
 278      {
 279          $no_nesting = false;
 280   
 281          // Is the designer wanting to call another loop in a loop?
 282          if (strpos($tag_args, '!') === 0)
 283          {
 284              // Count the number if ! occurrences (not allowed in vars)
 285              $no_nesting = substr_count($tag_args, '!');
 286              $tag_args = substr($tag_args, $no_nesting);
 287          }
 288   
 289          // Allow for control of looping (indexes start from zero):
 290          // foo(2)    : Will start the loop on the 3rd entry
 291          // foo(-2)   : Will start the loop two entries from the end
 292          // foo(3,4)  : Will start the loop on the fourth entry and end it on the fifth
 293          // foo(3,-4) : Will start the loop on the fourth entry and end it four from last
 294          if (preg_match('#^([^()]*)\(([\-\d]+)(?:,([\-\d]+))?\)$#', $tag_args, $match))
 295          {
 296              $tag_args = $match[1];
 297   
 298              if ($match[2] < 0)
 299              {
 300                  $loop_start = '($_' . $tag_args . '_count ' . $match[2] . ' < 0 ? 0 : $_' . $tag_args . '_count ' . $match[2] . ')';
 301              }
 302              else
 303              {
 304                  $loop_start = '($_' . $tag_args . '_count < ' . $match[2] . ' ? $_' . $tag_args . '_count : ' . $match[2] . ')';
 305              }
 306   
 307              if (strlen($match[3]) < 1 || $match[3] == -1)
 308              {
 309                  $loop_end = '$_' . $tag_args . '_count';
 310              }
 311              else if ($match[3] >= 0)
 312              {
 313                  $loop_end = '(' . ($match[3] + 1) . ' > $_' . $tag_args . '_count ? $_' . $tag_args . '_count : ' . ($match[3] + 1) . ')';
 314              }
 315              else //if ($match[3] < -1)
 316              {
 317                  $loop_end = '$_' . $tag_args . '_count' . ($match[3] + 1);
 318              }
 319          }
 320          else
 321          {
 322              $loop_start = 0;
 323              $loop_end = '$_' . $tag_args . '_count';
 324          }
 325   
 326          $tag_template_php = '';
 327          array_push($this->block_names, $tag_args);
 328   
 329          if ($no_nesting !== false)
 330          {
 331              // We need to implode $no_nesting times from the end...
 332              $block = array_slice($this->block_names, -$no_nesting);
 333          }
 334          else
 335          {
 336              $block = $this->block_names;
 337          }
 338   
 339          if (sizeof($block) < 2)
 340          {
 341              // Block is not nested.
 342              $tag_template_php = '$_' . $tag_args . "_count = (isset(\$this->_tpldata['$tag_args'])) ? sizeof(\$this->_tpldata['$tag_args']) : 0;";
 343              $varref = "\$this->_tpldata['$tag_args']";
 344          }
 345          else
 346          {
 347              // This block is nested.
 348              // Generate a namespace string for this block.
 349              $namespace = implode('.', $block);
 350   
 351              // Get a reference to the data array for this block that depends on the
 352              // current indices of all parent blocks.
 353              $varref = $this->generate_block_data_ref($namespace, false);
 354   
 355              // Create the for loop code to iterate over this block.
 356              $tag_template_php = '$_' . $tag_args . '_count = (isset(' . $varref . ')) ? sizeof(' . $varref . ') : 0;';
 357          }
 358   
 359          $tag_template_php .= 'if ($_' . $tag_args . '_count) {';
 360   
 361          /**
 362          * The following uses foreach for iteration instead of a for loop, foreach is faster but requires PHP to make a copy of the contents of the array which uses more memory
 363          * <code>
 364          *    if (!$offset)
 365          *    {
 366          *        $tag_template_php .= 'foreach (' . $varref . ' as $_' . $tag_args . '_i => $_' . $tag_args . '_val){';
 367          *    }
 368          * </code>
 369          */
 370   
 371          $tag_template_php .= 'for ($_' . $tag_args . '_i = ' . $loop_start . '; $_' . $tag_args . '_i < ' . $loop_end . '; ++$_' . $tag_args . '_i){';
 372          $tag_template_php .= '$_'. $tag_args . '_val = &' . $varref . '[$_'. $tag_args. '_i];';
 373   
 374          return $tag_template_php;
 375      }
 376   
 377      /**
 378      * Compile IF tags - much of this is from Smarty with
 379      * some adaptions for our block level methods
 380      * @access private
 381      */
 382      function compile_tag_if($tag_args, $elseif)
 383      {
 384          // Tokenize args for 'if' tag.
 385          preg_match_all('/(?:
 386              "[^"\\\\]*(?:\\\\.[^"\\\\]*)*"         |
 387              \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'     |
 388              [(),]                                  |
 389              [^\s(),]+)/x', $tag_args, $match);
 390   
 391          $tokens = $match[0];
 392          $is_arg_stack = array();
 393   
 394          for ($i = 0, $size = sizeof($tokens); $i < $size; $i++)
 395          {
 396              $token = &$tokens[$i];
 397   
 398              switch ($token)
 399              {
 400                  case '!==':
 401                  case '===':
 402                  case '<<':
 403                  case '>>':
 404                  case '|':
 405                  case '^':
 406                  case '&':
 407                  case '~':
 408                  case ')':
 409                  case ',':
 410                  case '+':
 411                  case '-':
 412                  case '*':
 413                  case '/':
 414                  case '@':
 415                  break;
 416   
 417                  case '==':
 418                  case 'eq':
 419                      $token = '==';
 420                  break;
 421   
 422                  case '!=':
 423                  case '<>':
 424                  case 'ne':
 425                  case 'neq':
 426                      $token = '!=';
 427                  break;
 428   
 429                  case '<':
 430                  case 'lt':
 431                      $token = '<';
 432                  break;
 433   
 434                  case '<=':
 435                  case 'le':
 436                  case 'lte':
 437                      $token = '<=';
 438                  break;
 439   
 440                  case '>':
 441                  case 'gt':
 442                      $token = '>';
 443                  break;
 444   
 445                  case '>=':
 446                  case 'ge':
 447                  case 'gte':
 448                      $token = '>=';
 449                  break;
 450   
 451                  case '&&':
 452                  case 'and':
 453                      $token = '&&';
 454                  break;
 455   
 456                  case '||':
 457                  case 'or':
 458                      $token = '||';
 459                  break;
 460   
 461                  case '!':
 462                  case 'not':
 463                      $token = '!';
 464                  break;
 465   
 466                  case '%':
 467                  case 'mod':
 468                      $token = '%';
 469                  break;
 470   
 471                  case '(':
 472                      array_push($is_arg_stack, $i);
 473                  break;
 474   
 475                  case 'is':
 476                      $is_arg_start = ($tokens[$i-1] == ')') ? array_pop($is_arg_stack) : $i-1;
 477                      $is_arg    = implode('    ', array_slice($tokens,    $is_arg_start, $i -    $is_arg_start));
 478   
 479                      $new_tokens    = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
 480   
 481                      array_splice($tokens, $is_arg_start, sizeof($tokens), $new_tokens);
 482   
 483                      $i = $is_arg_start;
 484   
 485                  // no break
 486   
 487                  default:
 488                      if (preg_match('#^((?:[a-z0-9\-_]+\.)+)?(\$)?(?=[A-Z])([A-Z0-9\-_]+)#s', $token, $varrefs))
 489                      {
 490                          $token = (!empty($varrefs[1])) ? $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[2]) . '[\'' . $varrefs[3] . '\']' : (($varrefs[2]) ? '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[3] . '\']' : '$this->_rootref[\'' . $varrefs[3] . '\']');
 491                      }
 492                      else if (preg_match('#^\.((?:[a-z0-9\-_]+\.?)+)$#s', $token, $varrefs))
 493                      {
 494                          // Allow checking if loops are set with .loopname
 495                          // It is also possible to check the loop count by doing <!-- IF .loopname > 1 --> for example
 496                          $blocks = explode('.', $varrefs[1]);
 497   
 498                          // If the block is nested, we have a reference that we can grab.
 499                          // If the block is not nested, we just go and grab the block from _tpldata
 500                          if (sizeof($blocks) > 1)
 501                          {
 502                              $block = array_pop($blocks);
 503                              $namespace = implode('.', $blocks);
 504                              $varref = $this->generate_block_data_ref($namespace, true);
 505   
 506                              // Add the block reference for the last child.
 507                              $varref .= "['" . $block . "']";
 508                          }
 509                          else
 510                          {
 511                              $varref = '$this->_tpldata';
 512   
 513                              // Add the block reference for the last child.
 514                              $varref .= "['" . $blocks[0] . "']";
 515                          }
 516                          $token = "sizeof($varref)";
 517                      }
 518   
 519                  break;
 520              }
 521          }
 522   
 523          return (($elseif) ? '} else if (' : 'if (') . (implode(' ', $tokens) . ') { ');
 524      }
 525   
 526      /**
 527      * Compile DEFINE tags
 528      * @access private
 529      */
 530      function compile_tag_define($tag_args, $op)
 531      {
 532          preg_match('#^((?:[a-z0-9\-_]+\.)+)?\$(?=[A-Z])([A-Z0-9_\-]*)(?: = (\'?)([^\']*)(\'?))?$#', $tag_args, $match);
 533   
 534          if (empty($match[2]) || (!isset($match[4]) && $op))
 535          {
 536              return '';
 537          }
 538   
 539          if (!$op)
 540          {
 541              return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ');';
 542          }
 543   
 544          // Are we a string?
 545          if ($match[3] && $match[5])
 546          {
 547              $match[4] = str_replace(array('\\\'', '\\\\', '\''), array('\'', '\\', '\\\''), $match[4]);
 548   
 549              // Compile reference, we allow template variables in defines...
 550              $match[4] = $this->compile($match[4]);
 551   
 552              // Now replace the php code
 553              $match[4] = "'" . str_replace(array('<?php echo ', '; ?>'), array("' . ", " . '"), $match[4]) . "'";
 554          }
 555          else
 556          {
 557              preg_match('#true|false|\.#i', $match[4], $type);
 558   
 559              switch (strtolower($type[0]))
 560              {
 561                  case 'true':
 562                  case 'false':
 563                      $match[4] = strtoupper($match[4]);
 564                  break;
 565   
 566                  case '.':
 567                      $match[4] = doubleval($match[4]);
 568                  break;
 569   
 570                  default:
 571                      $match[4] = intval($match[4]);
 572                  break;
 573              }
 574          }
 575   
 576          return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[2] . '\']' : '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $match[2] . '\']') . ' = ' . $match[4] . ';';
 577      }
 578   
 579      /**
 580      * Compile INCLUDE tag
 581      * @access private
 582      */
 583      function compile_tag_include($tag_args)
 584      {
 585          return "\$this->_tpl_include('$tag_args');";
 586      }
 587   
 588      /**
 589      * Compile INCLUDE_PHP tag
 590      * @access private
 591      */
 592      function compile_tag_include_php($tag_args)
 593      {
 594          return "include('" . $tag_args . "');";
 595      }
 596   
 597      /**
 598      * parse expression
 599      * This is from Smarty
 600      * @access private
 601      */
 602      function _parse_is_expr($is_arg, $tokens)
 603      {
 604          $expr_end = 0;
 605          $negate_expr = false;
 606   
 607          if (($first_token = array_shift($tokens)) == 'not')
 608          {
 609              $negate_expr = true;
 610              $expr_type = array_shift($tokens);
 611          }
 612          else
 613          {
 614              $expr_type = $first_token;
 615          }
 616   
 617          switch ($expr_type)
 618          {
 619              case 'even':
 620                  if (@$tokens[$expr_end] == 'by')
 621                  {
 622                      $expr_end++;
 623                      $expr_arg = $tokens[$expr_end++];
 624                      $expr = "!(($is_arg / $expr_arg) % $expr_arg)";
 625                  }
 626                  else
 627                  {
 628                      $expr = "!($is_arg & 1)";
 629                  }
 630              break;
 631   
 632              case 'odd':
 633                  if (@$tokens[$expr_end] == 'by')
 634                  {
 635                      $expr_end++;
 636                      $expr_arg = $tokens[$expr_end++];
 637                      $expr = "(($is_arg / $expr_arg) % $expr_arg)";
 638                  }
 639                  else
 640                  {
 641                      $expr = "($is_arg & 1)";
 642                  }
 643              break;
 644   
 645              case 'div':
 646                  if (@$tokens[$expr_end] == 'by')
 647                  {
 648                      $expr_end++;
 649                      $expr_arg = $tokens[$expr_end++];
 650                      $expr = "!($is_arg % $expr_arg)";
 651                  }
 652              break;
 653          }
 654   
 655          if ($negate_expr)
 656          {
 657              $expr = "!($expr)";
 658          }
 659   
 660          array_splice($tokens, 0, $expr_end, $expr);
 661   
 662          return $tokens;
 663      }
 664   
 665      /**
 666      * Generates a reference to the given variable inside the given (possibly nested)
 667      * block namespace. This is a string of the form:
 668      * ' . $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['varname'] . '
 669      * It's ready to be inserted into an "echo" line in one of the templates.
 670      * NOTE: expects a trailing "." on the namespace.
 671      * @access private
 672      */
 673      function generate_block_varref($namespace, $varname, $echo = true, $defop = false)
 674      {
 675          // Strip the trailing period.
 676          $namespace = substr($namespace, 0, -1);
 677   
 678          // Get a reference to the data block for this namespace.
 679          $varref = $this->generate_block_data_ref($namespace, true, $defop);
 680          // Prepend the necessary code to stick this in an echo line.
 681   
 682          // Append the variable reference.
 683          $varref .= "['$varname']";
 684          $varref = ($echo) ? "<?php echo $varref; ?>" : ((isset($varref)) ? $varref : '');
 685   
 686          return $varref;
 687      }
 688   
 689      /**
 690      * Generates a reference to the array of data values for the given
 691      * (possibly nested) block namespace. This is a string of the form:
 692      * $this->_tpldata['parent'][$_parent_i]['$child1'][$_child1_i]['$child2'][$_child2_i]...['$childN']
 693      *
 694      * If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above.
 695      * NOTE: does not expect a trailing "." on the blockname.
 696      * @access private
 697      */
 698      function generate_block_data_ref($blockname, $include_last_iterator, $defop = false)
 699      {
 700          // Get an array of the blocks involved.
 701          $blocks = explode('.', $blockname);
 702          $blockcount = sizeof($blocks) - 1;
 703   
 704          // DEFINE is not an element of any referenced variable, we must use _tpldata to access it
 705          if ($defop)
 706          {
 707              $varref = '$this->_tpldata[\'DEFINE\']';
 708              // Build up the string with everything but the last child.
 709              for ($i = 0; $i < $blockcount; $i++)
 710              {
 711                  $varref .= "['" . $blocks[$i] . "'][\$_" . $blocks[$i] . '_i]';
 712              }
 713              // Add the block reference for the last child.
 714              $varref .= "['" . $blocks[$blockcount] . "']";
 715              // Add the iterator for the last child if requried.
 716              if ($include_last_iterator)
 717              {
 718                  $varref .= '[$_' . $blocks[$blockcount] . '_i]';
 719              }
 720              return $varref;
 721          }
 722          else if ($include_last_iterator)
 723          {
 724              return '$_'. $blocks[$blockcount] . '_val';
 725          }
 726          else
 727          {
 728              return '$_'. $blocks[$blockcount - 1] . '_val[\''. $blocks[$blockcount]. '\']';
 729          }
 730      }
 731   
 732      /**
 733      * Write compiled file to cache directory
 734      * @access private
 735      */
 736      function compile_write($handle, $data)
 737      {
 738          global $phpEx;
 739   
 740          $filename = $this->template->cachepath . str_replace('/', '.', $this->template->filename[$handle]) . '.' . $phpEx;
 741   
 742          if ($fp = @fopen($filename, 'wb'))
 743          {
 744              @flock($fp, LOCK_EX);
 745              @fwrite ($fp, $data);
 746              @flock($fp, LOCK_UN);
 747              @fclose($fp);
 748   
 749              @chmod($filename, 0666);
 750          }
 751   
 752          return;
 753      }
 754  }
 755   
 756  ?>