[ Index ]
 

Code source de phpMyVisites 2.3

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/libs/smarty/ -> Smarty_Compiler.class.php (source)

   1  <?php
   2  
   3  /**
   4   * Project:     Smarty: the PHP compiling template engine
   5   * File:        Smarty_Compiler.class.php
   6   *
   7   * This library is free software; you can redistribute it and/or
   8   * modify it under the terms of the GNU Lesser General Public
   9   * License as published by the Free Software Foundation; either
  10   * version 2.1 of the License, or (at your option) any later version.
  11   *
  12   * This library is distributed in the hope that it will be useful,
  13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15   * Lesser General Public License for more details.
  16   *
  17   * You should have received a copy of the GNU Lesser General Public
  18   * License along with this library; if not, write to the Free Software
  19   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20   *
  21   * @link http://smarty.php.net/
  22   * @author Monte Ohrt <monte at ohrt dot com>
  23   * @author Andrei Zmievski <andrei@php.net>
  24   * @version 2.6.9
  25   * @copyright 2001-2005 New Digital Group, Inc.
  26   * @package Smarty
  27   */
  28  
  29  /* $Id: Smarty_Compiler.class.php,v 1.1 2005/07/26 08:49:04 atomik-fr Exp $ */
  30  
  31  /**
  32   * Template compiling class
  33   * @package Smarty
  34   */
  35  class Smarty_Compiler extends Smarty {
  36  
  37      // internal vars
  38      /**#@+
  39       * @access private
  40       */
  41      var $_folded_blocks         =   array();    // keeps folded template blocks
  42      var $_current_file          =   null;       // the current template being compiled
  43      var $_current_line_no       =   1;          // line number for error messages
  44      var $_capture_stack         =   array();    // keeps track of nested capture buffers
  45      var $_plugin_info           =   array();    // keeps track of plugins to load
  46      var $_init_smarty_vars      =   false;
  47      var $_permitted_tokens      =   array('true','false','yes','no','on','off','null');
  48      var $_db_qstr_regexp        =   null;        // regexps are setup in the constructor
  49      var $_si_qstr_regexp        =   null;
  50      var $_qstr_regexp           =   null;
  51      var $_func_regexp           =   null;
  52      var $_reg_obj_regexp        =   null;
  53      var $_var_bracket_regexp    =   null;
  54      var $_num_const_regexp      =   null;
  55      var $_dvar_guts_regexp      =   null;
  56      var $_dvar_regexp           =   null;
  57      var $_cvar_regexp           =   null;
  58      var $_svar_regexp           =   null;
  59      var $_avar_regexp           =   null;
  60      var $_mod_regexp            =   null;
  61      var $_var_regexp            =   null;
  62      var $_parenth_param_regexp  =   null;
  63      var $_func_call_regexp      =   null;
  64      var $_obj_ext_regexp        =   null;
  65      var $_obj_start_regexp      =   null;
  66      var $_obj_params_regexp     =   null;
  67      var $_obj_call_regexp       =   null;
  68      var $_cacheable_state       =   0;
  69      var $_cache_attrs_count     =   0;
  70      var $_nocache_count         =   0;
  71      var $_cache_serial          =   null;
  72      var $_cache_include         =   null;
  73  
  74      var $_strip_depth           =   0;
  75      var $_additional_newline    =   "\n";
  76  
  77      /**#@-*/
  78      /**
  79       * The class constructor.
  80       */
  81      function Smarty_Compiler()
  82      {
  83          // matches double quoted strings:
  84          // "foobar"
  85          // "foo\"bar"
  86          $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
  87  
  88          // matches single quoted strings:
  89          // 'foobar'
  90          // 'foo\'bar'
  91          $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
  92  
  93          // matches single or double quoted strings
  94          $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
  95  
  96          // matches bracket portion of vars
  97          // [0]
  98          // [foo]
  99          // [$bar]
 100          $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
 101  
 102          // matches numerical constants
 103          // 30
 104          // -12
 105          // 13.22
 106          $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
 107  
 108          // matches $ vars (not objects):
 109          // $foo
 110          // $foo.bar
 111          // $foo.bar.foobar
 112          // $foo[0]
 113          // $foo[$bar]
 114          // $foo[5][blah]
 115          // $foo[5].bar[$foobar][4]
 116          $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))';
 117          $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
 118          $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
 119                  . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
 120          $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
 121  
 122          // matches config vars:
 123          // #foo#
 124          // #foobar123_foo#
 125          $this->_cvar_regexp = '\#\w+\#';
 126  
 127          // matches section vars:
 128          // %foo.bar%
 129          $this->_svar_regexp = '\%\w+\.\w+\%';
 130  
 131          // matches all valid variables (no quotes, no modifiers)
 132          $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
 133             . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
 134  
 135          // matches valid variable syntax:
 136          // $foo
 137          // $foo
 138          // #foo#
 139          // #foo#
 140          // "text"
 141          // "text"
 142          $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
 143  
 144          // matches valid object call (one level of object nesting allowed in parameters):
 145          // $foo->bar
 146          // $foo->bar()
 147          // $foo->bar("text")
 148          // $foo->bar($foo, $bar, "text")
 149          // $foo->bar($foo, "foo")
 150          // $foo->bar->foo()
 151          // $foo->bar->foo->bar()
 152          // $foo->bar($foo->bar)
 153          // $foo->bar($foo->bar())
 154          // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
 155          $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
 156          $this->_obj_restricted_param_regexp = '(?:'
 157                  . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')'
 158                  . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)';
 159          $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
 160                  . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
 161          $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
 162                  . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
 163          $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
 164          $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?)';
 165          
 166          // matches valid modifier syntax:
 167          // |foo
 168          // |@foo
 169          // |foo:"bar"
 170          // |foo:$bar
 171          // |foo:"bar":$foobar
 172          // |foo|bar
 173          // |foo:$foo->bar
 174          $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|'
 175             . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
 176  
 177          // matches valid function name:
 178          // foo123
 179          // _foo_bar
 180          $this->_func_regexp = '[a-zA-Z_]\w*';
 181  
 182          // matches valid registered object:
 183          // foo->bar
 184          $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
 185  
 186          // matches valid parameter values:
 187          // true
 188          // $foo
 189          // $foo|bar
 190          // #foo#
 191          // #foo#|bar
 192          // "text"
 193          // "text"|bar
 194          // $foo->bar
 195          $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
 196             . $this->_var_regexp . '|' . $this->_num_const_regexp  . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
 197  
 198          // matches valid parenthesised function parameters:
 199          //
 200          // "text"
 201          //    $foo, $bar, "text"
 202          // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
 203          $this->_parenth_param_regexp = '(?:\((?:\w+|'
 204                  . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
 205                  . $this->_param_regexp . ')))*)?\))';
 206  
 207          // matches valid function call:
 208          // foo()
 209          // foo_bar($foo)
 210          // _foo_bar($foo,"bar")
 211          // foo123($foo,$foo->bar(),"foo")
 212          $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
 213             . $this->_parenth_param_regexp . '))';
 214      }
 215  
 216      /**
 217       * compile a resource
 218       *
 219       * sets $compiled_content to the compiled source
 220       * @param string $resource_name
 221       * @param string $source_content
 222       * @param string $compiled_content
 223       * @return true
 224       */
 225      function _compile_file($resource_name, $source_content, &$compiled_content)
 226      {
 227  
 228          if ($this->security) {
 229              // do not allow php syntax to be executed unless specified
 230              if ($this->php_handling == SMARTY_PHP_ALLOW &&
 231                  !$this->security_settings['PHP_HANDLING']) {
 232                  $this->php_handling = SMARTY_PHP_PASSTHRU;
 233              }
 234          }
 235  
 236          $this->_load_filters();
 237  
 238          $this->_current_file = $resource_name;
 239          $this->_current_line_no = 1;
 240          $ldq = preg_quote($this->left_delimiter, '~');
 241          $rdq = preg_quote($this->right_delimiter, '~');
 242  
 243          // run template source through prefilter functions
 244          if (count($this->_plugins['prefilter']) > 0) {
 245              foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
 246                  if ($prefilter === false) continue;
 247                  if ($prefilter[3] || is_callable($prefilter[0])) {
 248                      $source_content = call_user_func_array($prefilter[0],
 249                                                              array($source_content, &$this));
 250                      $this->_plugins['prefilter'][$filter_name][3] = true;
 251                  } else {
 252                      $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
 253                  }
 254              }
 255          }
 256  
 257          /* fetch all special blocks */
 258          $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
 259  
 260          preg_match_all($search, $source_content, $match,  PREG_SET_ORDER);
 261          $this->_folded_blocks = $match;
 262          reset($this->_folded_blocks);
 263  
 264          /* replace special blocks by "{php}" */
 265          $source_content = preg_replace($search.'e', "'"
 266                                         . $this->_quote_replace($this->left_delimiter) . 'php'
 267                                         . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
 268                                         . $this->_quote_replace($this->right_delimiter)
 269                                         . "'"
 270                                         , $source_content);
 271  
 272          /* Gather all template tags. */
 273          preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match);
 274          $template_tags = $_match[1];
 275          /* Split content by template tags to obtain non-template content. */
 276          $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);
 277  
 278          /* loop through text blocks */
 279          for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
 280              /* match anything resembling php tags */
 281              if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) {
 282                  /* replace tags with placeholders to prevent recursive replacements */
 283                  $sp_match[1] = array_unique($sp_match[1]);
 284                  usort($sp_match[1], '_smarty_sort_length');
 285                  for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
 286                      $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
 287                  }
 288                  /* process each one */
 289                  for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
 290                      if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
 291                          /* echo php contents */
 292                          $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
 293                      } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
 294                          /* quote php tags */
 295                          $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
 296                      } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
 297                          /* remove php tags */
 298                          $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
 299                      } else {
 300                          /* SMARTY_PHP_ALLOW, but echo non php starting tags */
 301                          $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
 302                          $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
 303                      }
 304                  }
 305              }
 306          }
 307  
 308          /* Compile the template tags into PHP code. */
 309          $compiled_tags = array();
 310          for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
 311              $this->_current_line_no += substr_count($text_blocks[$i], "\n");
 312              $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
 313              $this->_current_line_no += substr_count($template_tags[$i], "\n");
 314          }
 315          if (count($this->_tag_stack)>0) {
 316              list($_open_tag, $_line_no) = end($this->_tag_stack);
 317              $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
 318              return;
 319          }
 320  
 321          /* Reformat $text_blocks between 'strip' and '/strip' tags,
 322             removing spaces, tabs and newlines. */
 323          $strip = false;
 324          for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
 325              if ($compiled_tags[$i] == '{strip}') {
 326                  $compiled_tags[$i] = '';
 327                  $strip = true;
 328                  /* remove leading whitespaces */
 329                  $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
 330              }
 331              if ($strip) {
 332                  /* strip all $text_blocks before the next '/strip' */
 333                  for ($j = $i + 1; $j < $for_max; $j++) {
 334                      /* remove leading and trailing whitespaces of each line */
 335                      $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]);
 336                      if ($compiled_tags[$j] == '{/strip}') {                       
 337                          /* remove trailing whitespaces from the last text_block */
 338                          $text_blocks[$j] = rtrim($text_blocks[$j]);
 339                      }
 340                      $text_blocks[$j] = "<?php echo '" . strtr($text_blocks[$j], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>";
 341                      if ($compiled_tags[$j] == '{/strip}') {
 342                          $compiled_tags[$j] = "\n"; /* slurped by php, but necessary
 343                                      if a newline is following the closing strip-tag */
 344                          $strip = false;
 345                          $i = $j;
 346                          break;
 347                      }
 348                  }
 349              }
 350          }
 351          $compiled_content = '';
 352  
 353          /* Interleave the compiled contents and text blocks to get the final result. */
 354          for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
 355              if ($compiled_tags[$i] == '') {
 356                  // tag result empty, remove first newline from following text block
 357                  $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]);
 358              }
 359              $compiled_content .= $text_blocks[$i].$compiled_tags[$i];
 360          }
 361          $compiled_content .= $text_blocks[$i];
 362  
 363          // remove \n from the end of the file, if any
 364          if (($_len=strlen($compiled_content)) && ($compiled_content{$_len - 1} == "\n" )) {
 365              $compiled_content = substr($compiled_content, 0, -1);
 366          }
 367  
 368          if (!empty($this->_cache_serial)) {
 369              $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
 370          }
 371  
 372          // remove unnecessary close/open tags
 373          $compiled_content = preg_replace('~\?>\n?<\?php~', '', $compiled_content);
 374  
 375          // run compiled template through postfilter functions
 376          if (count($this->_plugins['postfilter']) > 0) {
 377              foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
 378                  if ($postfilter === false) continue;
 379                  if ($postfilter[3] || is_callable($postfilter[0])) {
 380                      $compiled_content = call_user_func_array($postfilter[0],
 381                                                                array($compiled_content, &$this));
 382                      $this->_plugins['postfilter'][$filter_name][3] = true;
 383                  } else {
 384                      $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
 385                  }
 386              }
 387          }
 388  
 389          // put header at the top of the compiled template
 390          $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
 391          $template_header .= "         compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
 392  
 393          /* Emit code to load needed plugins. */
 394          $this->_plugins_code = '';
 395          if (count($this->_plugin_info)) {
 396              $_plugins_params = "array('plugins' => array(";
 397              foreach ($this->_plugin_info as $plugin_type => $plugins) {
 398                  foreach ($plugins as $plugin_name => $plugin_info) {
 399                      $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
 400                      $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
 401                  }
 402              }
 403              $_plugins_params .= '))';
 404              $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
 405              $template_header .= $plugins_code;
 406              $this->_plugin_info = array();
 407              $this->_plugins_code = $plugins_code;
 408          }
 409  
 410          if ($this->_init_smarty_vars) {
 411              $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
 412              $this->_init_smarty_vars = false;
 413          }
 414  
 415          $compiled_content = $template_header . $compiled_content;
 416          return true;
 417      }
 418  
 419      /**
 420       * Compile a template tag
 421       *
 422       * @param string $template_tag
 423       * @return string
 424       */
 425      function _compile_tag($template_tag)
 426      {
 427          /* Matched comment. */
 428          if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
 429              return '';
 430          
 431          /* Split tag into two three parts: command, command modifiers and the arguments. */
 432          if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp
 433                  . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
 434                        (?:\s+(.*))?$
 435                      ~xs', $template_tag, $match)) {
 436              $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
 437          }
 438          
 439          $tag_command = $match[1];
 440          $tag_modifier = isset($match[2]) ? $match[2] : null;
 441          $tag_args = isset($match[3]) ? $match[3] : null;
 442  
 443          if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {
 444              /* tag name is a variable or object */
 445              $_return = $this->_parse_var_props($tag_command . $tag_modifier);
 446              return "<?php echo $_return; ?>" . $this->_additional_newline;
 447          }
 448  
 449          /* If the tag name is a registered object, we process it. */
 450          if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) {
 451              return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
 452          }
 453  
 454          switch ($tag_command) {
 455              case 'include':
 456                  return $this->_compile_include_tag($tag_args);
 457  
 458              case 'include_php':
 459                  return $this->_compile_include_php_tag($tag_args);
 460  
 461              case 'if':
 462                  $this->_push_tag('if');
 463                  return $this->_compile_if_tag($tag_args);
 464  
 465              case 'else':
 466                  list($_open_tag) = end($this->_tag_stack);
 467                  if ($_open_tag != 'if' && $_open_tag != 'elseif')
 468                      $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
 469                  else
 470                      $this->_push_tag('else');
 471                  return '<?php else: ?>';
 472  
 473              case 'elseif':
 474                  list($_open_tag) = end($this->_tag_stack);
 475                  if ($_open_tag != 'if' && $_open_tag != 'elseif')
 476                      $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
 477                  if ($_open_tag == 'if')
 478                      $this->_push_tag('elseif');
 479                  return $this->_compile_if_tag($tag_args, true);
 480  
 481              case '/if':
 482                  $this->_pop_tag('if');
 483                  return '<?php endif; ?>';
 484  
 485              case 'capture':
 486                  return $this->_compile_capture_tag(true, $tag_args);
 487  
 488              case '/capture':
 489                  return $this->_compile_capture_tag(false);
 490  
 491              case 'ldelim':
 492                  return $this->left_delimiter;
 493  
 494              case 'rdelim':
 495                  return $this->right_delimiter;
 496  
 497              case 'section':
 498                  $this->_push_tag('section');
 499                  return $this->_compile_section_start($tag_args);
 500  
 501              case 'sectionelse':
 502                  $this->_push_tag('sectionelse');
 503                  return "<?php endfor; else: ?>";
 504                  break;
 505  
 506              case '/section':
 507                  $_open_tag = $this->_pop_tag('section');
 508                  if ($_open_tag == 'sectionelse')
 509                      return "<?php endif; ?>";
 510                  else
 511                      return "<?php endfor; endif; ?>";
 512  
 513              case 'foreach':
 514                  $this->_push_tag('foreach');
 515                  return $this->_compile_foreach_start($tag_args);
 516                  break;
 517  
 518              case 'foreachelse':
 519                  $this->_push_tag('foreachelse');
 520                  return "<?php endforeach; else: ?>";
 521  
 522              case '/foreach':
 523                  $_open_tag = $this->_pop_tag('foreach');
 524                  if ($_open_tag == 'foreachelse')
 525                      return "<?php endif; unset(\$_from); ?>";
 526                  else
 527                      return "<?php endforeach; endif; unset(\$_from); ?>";
 528                  break;
 529  
 530              case 'strip':
 531              case '/strip':
 532                  if ($tag_command{0}=='/') {
 533                      $this->_pop_tag('strip');
 534                      if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
 535                          $this->_additional_newline = "\n";
 536                          return '{' . $tag_command . '}';
 537                      }
 538                  } else {
 539                      $this->_push_tag('strip');
 540                      if ($this->_strip_depth++==0) { /* outermost opening {strip} */
 541                          $this->_additional_newline = "";
 542                          return '{' . $tag_command . '}';
 543                      }
 544                  }
 545                  return '';
 546  
 547              case 'php':
 548                  /* handle folded tags replaced by {php} */
 549                  list(, $block) = each($this->_folded_blocks);
 550                  $this->_current_line_no += substr_count($block[0], "\n");
 551                  /* the number of matched elements in the regexp in _compile_file()
 552                     determins the type of folded tag that was found */
 553                  switch (count($block)) {
 554                      case 2: /* comment */
 555                          return '';
 556  
 557                      case 3: /* literal */
 558                          return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
 559  
 560                      case 4: /* php */
 561                          if ($this->security && !$this->security_settings['PHP_TAGS']) {
 562                              $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
 563                              return;
 564                          }
 565                          return '<?php ' . $block[3] .' ?>';
 566                  }
 567                  break;
 568  
 569              case 'insert':
 570                  return $this->_compile_insert_tag($tag_args);
 571  
 572              default:
 573                  if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
 574                      return $output;
 575                  } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
 576                      return $output;
 577                  } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
 578                      return $output;                    
 579                  } else {
 580                      $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
 581                  }
 582  
 583          }
 584      }
 585  
 586  
 587      /**
 588       * compile the custom compiler tag
 589       *
 590       * sets $output to the compiled custom compiler tag
 591       * @param string $tag_command
 592       * @param string $tag_args
 593       * @param string $output
 594       * @return boolean
 595       */
 596      function _compile_compiler_tag($tag_command, $tag_args, &$output)
 597      {
 598          $found = false;
 599          $have_function = true;
 600  
 601          /*
 602           * First we check if the compiler function has already been registered
 603           * or loaded from a plugin file.
 604           */
 605          if (isset($this->_plugins['compiler'][$tag_command])) {
 606              $found = true;
 607              $plugin_func = $this->_plugins['compiler'][$tag_command][0];
 608              if (!is_callable($plugin_func)) {
 609                  $message = "compiler function '$tag_command' is not implemented";
 610                  $have_function = false;
 611              }
 612          }
 613          /*
 614           * Otherwise we need to load plugin file and look for the function
 615           * inside it.
 616           */
 617          else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
 618              $found = true;
 619  
 620              include_once $plugin_file;
 621  
 622              $plugin_func = 'smarty_compiler_' . $tag_command;
 623              if (!is_callable($plugin_func)) {
 624                  $message = "plugin function $plugin_func() not found in $plugin_file\n";
 625                  $have_function = false;
 626              } else {
 627                  $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
 628              }
 629          }
 630  
 631          /*
 632           * True return value means that we either found a plugin or a
 633           * dynamically registered function. False means that we didn't and the
 634           * compiler should now emit code to load custom function plugin for this
 635           * tag.
 636           */
 637          if ($found) {
 638              if ($have_function) {
 639                  $output = call_user_func_array($plugin_func, array($tag_args, &$this));
 640                  if($output != '') {
 641                  $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
 642                                     . $output
 643                                     . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
 644                  }
 645              } else {
 646                  $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
 647              }
 648              return true;
 649          } else {
 650              return false;
 651          }
 652      }
 653  
 654  
 655      /**
 656       * compile block function tag
 657       *
 658       * sets $output to compiled block function tag
 659       * @param string $tag_command
 660       * @param string $tag_args
 661       * @param string $tag_modifier
 662       * @param string $output
 663       * @return boolean
 664       */
 665      function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
 666      {
 667          if ($tag_command{0} == '/') {
 668              $start_tag = false;
 669              $tag_command = substr($tag_command, 1);
 670          } else
 671              $start_tag = true;
 672  
 673          $found = false;
 674          $have_function = true;
 675  
 676          /*
 677           * First we check if the block function has already been registered
 678           * or loaded from a plugin file.
 679           */
 680          if (isset($this->_plugins['block'][$tag_command])) {
 681              $found = true;
 682              $plugin_func = $this->_plugins['block'][$tag_command][0];
 683              if (!is_callable($plugin_func)) {
 684                  $message = "block function '$tag_command' is not implemented";
 685                  $have_function = false;
 686              }
 687          }
 688          /*
 689           * Otherwise we need to load plugin file and look for the function
 690           * inside it.
 691           */
 692          else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
 693              $found = true;
 694  
 695              include_once $plugin_file;
 696  
 697              $plugin_func = 'smarty_block_' . $tag_command;
 698              if (!function_exists($plugin_func)) {
 699                  $message = "plugin function $plugin_func() not found in $plugin_file\n";
 700                  $have_function = false;
 701              } else {
 702                  $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
 703  
 704              }
 705          }
 706  
 707          if (!$found) {
 708              return false;
 709          } else if (!$have_function) {
 710              $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
 711              return true;
 712          }
 713  
 714          /*
 715           * Even though we've located the plugin function, compilation
 716           * happens only once, so the plugin will still need to be loaded
 717           * at runtime for future requests.
 718           */
 719          $this->_add_plugin('block', $tag_command);
 720  
 721          if ($start_tag)
 722              $this->_push_tag($tag_command);
 723          else
 724              $this->_pop_tag($tag_command);
 725  
 726          if ($start_tag) {
 727              $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
 728              $attrs = $this->_parse_attrs($tag_args);
 729              $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs='');
 730              $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
 731              $output .= $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat=true);';
 732              $output .= 'while ($_block_repeat) { ob_start(); ?>';
 733          } else {
 734              $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); ';
 735              $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat=false)';
 736              if ($tag_modifier != '') {
 737                  $this->_parse_modifiers($_out_tag_text, $tag_modifier);
 738              }
 739              $output .= 'echo '.$_out_tag_text.'; } ';
 740              $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
 741          }
 742  
 743          return true;
 744      }
 745  
 746  
 747      /**
 748       * compile custom function tag
 749       *
 750       * @param string $tag_command
 751       * @param string $tag_args
 752       * @param string $tag_modifier
 753       * @return string
 754       */
 755      function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
 756      {
 757          $found = false;
 758          $have_function = true;
 759  
 760          /*
 761           * First we check if the custom function has already been registered
 762           * or loaded from a plugin file.
 763           */
 764          if (isset($this->_plugins['function'][$tag_command])) {
 765              $found = true;
 766              $plugin_func = $this->_plugins['function'][$tag_command][0];
 767              if (!is_callable($plugin_func)) {
 768                  $message = "custom function '$tag_command' is not implemented";
 769                  $have_function = false;
 770              }
 771          }
 772          /*
 773           * Otherwise we need to load plugin file and look for the function
 774           * inside it.
 775           */
 776          else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
 777              $found = true;
 778  
 779              include_once $plugin_file;
 780  
 781              $plugin_func = 'smarty_function_' . $tag_command;
 782              if (!function_exists($plugin_func)) {
 783                  $message = "plugin function $plugin_func() not found in $plugin_file\n";
 784                  $have_function = false;
 785              } else {
 786                  $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true);
 787  
 788              }
 789          }
 790  
 791          if (!$found) {
 792              return false;
 793          } else if (!$have_function) {
 794              $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
 795              return true;
 796          }
 797  
 798          /* declare plugin to be loaded on display of the template that
 799             we compile right now */
 800          $this->_add_plugin('function', $tag_command);
 801  
 802          $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
 803          $attrs = $this->_parse_attrs($tag_args);
 804          $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs='');
 805  
 806          $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
 807          if($tag_modifier != '') {
 808              $this->_parse_modifiers($output, $tag_modifier);
 809          }
 810  
 811          if($output != '') {
 812              $output =  '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
 813                  . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
 814          }
 815  
 816          return true;
 817      }
 818  
 819      /**
 820       * compile a registered object tag
 821       *
 822       * @param string $tag_command
 823       * @param array $attrs
 824       * @param string $tag_modifier
 825       * @return string
 826       */
 827      function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
 828      {
 829          if ($tag_command{0} == '/') {
 830              $start_tag = false;
 831              $tag_command = substr($tag_command, 1);
 832          } else {
 833              $start_tag = true;
 834          }
 835  
 836          list($object, $obj_comp) = explode('->', $tag_command);
 837  
 838          $arg_list = array();
 839          if(count($attrs)) {
 840              $_assign_var = false;
 841              foreach ($attrs as $arg_name => $arg_value) {
 842                  if($arg_name == 'assign') {
 843                      $_assign_var = $arg_value;
 844                      unset($attrs['assign']);
 845                      continue;
 846                  }
 847                  if (is_bool($arg_value))
 848                      $arg_value = $arg_value ? 'true' : 'false';
 849                  $arg_list[] = "'$arg_name' => $arg_value";
 850              }
 851          }
 852  
 853          if($this->_reg_objects[$object][2]) {
 854              // smarty object argument format
 855              $args = "array(".implode(',', (array)$arg_list)."), \$this";
 856          } else {
 857              // traditional argument format
 858              $args = implode(',', array_values($attrs));
 859              if (empty($args)) {
 860                  $args = 'null';
 861              }
 862          }
 863  
 864          $prefix = '';
 865          $postfix = '';
 866          $newline = '';
 867          if(!is_object($this->_reg_objects[$object][0])) {
 868              $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
 869          } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
 870              $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
 871          } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
 872              // method
 873              if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
 874                  // block method
 875                  if ($start_tag) {
 876                      $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
 877                      $prefix .= "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat=true); ";
 878                      $prefix .= "while (\$_block_repeat) { ob_start();";
 879                      $return = null;
 880                      $postfix = '';
 881              } else {
 882                      $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); ";
 883                      $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat=false)";
 884                      $postfix = "} array_pop(\$this->_tag_stack);";
 885                  }
 886              } else {
 887                  // non-block method
 888                  $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
 889              }
 890          } else {
 891              // property
 892              $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
 893          }
 894  
 895          if($return != null) {
 896              if($tag_modifier != '') {
 897                  $this->_parse_modifiers($return, $tag_modifier);
 898              }
 899  
 900              if(!empty($_assign_var)) {
 901                  $output = "\$this->assign('" . $this->_dequote($_assign_var) ."',  $return);";
 902              } else {
 903                  $output = 'echo ' . $return . ';';
 904                  $newline = $this->_additional_newline;
 905              }
 906          } else {
 907              $output = '';
 908          }
 909  
 910          return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
 911      }
 912  
 913      /**
 914       * Compile {insert ...} tag
 915       *
 916       * @param string $tag_args
 917       * @return string
 918       */
 919      function _compile_insert_tag($tag_args)
 920      {
 921          $attrs = $this->_parse_attrs($tag_args);
 922          $name = $this->_dequote($attrs['name']);
 923  
 924          if (empty($name)) {
 925              $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
 926          }
 927  
 928          if (!empty($attrs['script'])) {
 929              $delayed_loading = true;
 930          } else {
 931              $delayed_loading = false;
 932          }
 933  
 934          foreach ($attrs as $arg_name => $arg_value) {
 935              if (is_bool($arg_value))
 936                  $arg_value = $arg_value ? 'true' : 'false';
 937              $arg_list[] = "'$arg_name' => $arg_value";
 938          }
 939  
 940          $this->_add_plugin('insert', $name, $delayed_loading);
 941  
 942          $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
 943  
 944          return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
 945      }
 946  
 947      /**
 948       * Compile {include ...} tag
 949       *
 950       * @param string $tag_args
 951       * @return string
 952       */
 953      function _compile_include_tag($tag_args)
 954      {
 955          $attrs = $this->_parse_attrs($tag_args);
 956          $arg_list = array();
 957  
 958          if (empty($attrs['file'])) {
 959              $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
 960          }
 961  
 962          foreach ($attrs as $arg_name => $arg_value) {
 963              if ($arg_name == 'file') {
 964                  $include_file = $arg_value;
 965                  continue;
 966              } else if ($arg_name == 'assign') {
 967                  $assign_var = $arg_value;
 968                  continue;
 969              }
 970              if (is_bool($arg_value))
 971                  $arg_value = $arg_value ? 'true' : 'false';
 972              $arg_list[] = "'$arg_name' => $arg_value";
 973          }
 974  
 975          $output = '<?php ';
 976  
 977          if (isset($assign_var)) {
 978              $output .= "ob_start();\n";
 979          }
 980  
 981          $output .=
 982              "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
 983  
 984  
 985          $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
 986          $output .= "\$this->_smarty_include($_params);\n" .
 987          "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
 988          "unset(\$_smarty_tpl_vars);\n";
 989  
 990          if (isset($assign_var)) {
 991              $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
 992          }
 993  
 994          $output .= ' ?>';
 995  
 996          return $output;
 997  
 998      }
 999  
1000      /**
1001       * Compile {include ...} tag
1002       *
1003       * @param string $tag_args
1004       * @return string
1005       */
1006      function _compile_include_php_tag($tag_args)
1007      {
1008          $attrs = $this->_parse_attrs($tag_args);
1009  
1010          if (empty($attrs['file'])) {
1011              $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
1012          }
1013  
1014          $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
1015          $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
1016  
1017          $arg_list = array();
1018          foreach($attrs as $arg_name => $arg_value) {
1019              if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
1020                  if(is_bool($arg_value))
1021                      $arg_value = $arg_value ? 'true' : 'false';
1022                  $arg_list[] = "'$arg_name' => $arg_value";
1023              }
1024          }
1025  
1026          $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
1027  
1028          return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
1029      }
1030  
1031  
1032      /**
1033       * Compile {section ...} tag
1034       *
1035       * @param string $tag_args
1036       * @return string
1037       */
1038      function _compile_section_start($tag_args)
1039      {
1040          $attrs = $this->_parse_attrs($tag_args);
1041          $arg_list = array();
1042  
1043          $output = '<?php ';
1044          $section_name = $attrs['name'];
1045          if (empty($section_name)) {
1046              $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
1047          }
1048  
1049          $output .= "unset(\$this->_sections[$section_name]);\n";
1050          $section_props = "\$this->_sections[$section_name]";
1051  
1052          foreach ($attrs as $attr_name => $attr_value) {
1053              switch ($attr_name) {
1054                  case 'loop':
1055                      $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
1056                      break;
1057  
1058                  case 'show':
1059                      if (is_bool($attr_value))
1060                          $show_attr_value = $attr_value ? 'true' : 'false';
1061                      else
1062                          $show_attr_value = "(bool)$attr_value";
1063                      $output .= "{$section_props}['show'] = $show_attr_value;\n";
1064                      break;
1065  
1066                  case 'name':
1067                      $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
1068                      break;
1069  
1070                  case 'max':
1071                  case 'start':
1072                      $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
1073                      break;
1074  
1075                  case 'step':
1076                      $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
1077                      break;
1078  
1079                  default:
1080                      $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
1081                      break;
1082              }
1083          }
1084  
1085          if (!isset($attrs['show']))
1086              $output .= "{$section_props}['show'] = true;\n";
1087  
1088          if (!isset($attrs['loop']))
1089              $output .= "{$section_props}['loop'] = 1;\n";
1090  
1091          if (!isset($attrs['max']))
1092              $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
1093          else
1094              $output .= "if ({$section_props}['max'] < 0)\n" .
1095                         "    {$section_props}['max'] = {$section_props}['loop'];\n";
1096  
1097          if (!isset($attrs['step']))
1098              $output .= "{$section_props}['step'] = 1;\n";
1099  
1100          if (!isset($attrs['start']))
1101              $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
1102          else {
1103              $output .= "if ({$section_props}['start'] < 0)\n" .
1104                         "    {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
1105                         "else\n" .
1106                         "    {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
1107          }
1108  
1109          $output .= "if ({$section_props}['show']) {\n";
1110          if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
1111              $output .= "    {$section_props}['total'] = {$section_props}['loop'];\n";
1112          } else {
1113              $output .= "    {$section_props}['total'] = min(ceil(({$section_props}['step'] > 0 ? {$section_props}['loop'] - {$section_props}['start'] : {$section_props}['start']+1)/abs({$section_props}['step'])), {$section_props}['max']);\n";
1114          }
1115          $output .= "    if ({$section_props}['total'] == 0)\n" .
1116                     "        {$section_props}['show'] = false;\n" .
1117                     "} else\n" .
1118                     "    {$section_props}['total'] = 0;\n";
1119  
1120          $output .= "if ({$section_props}['show']):\n";
1121          $output .= "
1122              for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
1123                   {$section_props}['iteration'] <= {$section_props}['total'];
1124                   {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
1125          $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
1126          $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
1127          $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
1128          $output .= "{$section_props}['first']      = ({$section_props}['iteration'] == 1);\n";
1129          $output .= "{$section_props}['last']       = ({$section_props}['iteration'] == {$section_props}['total']);\n";
1130  
1131          $output .= "?>";
1132  
1133          return $output;
1134      }
1135  
1136  
1137      /**
1138       * Compile {foreach ...} tag.
1139       *
1140       * @param string $tag_args
1141       * @return string
1142       */
1143      function _compile_foreach_start($tag_args)
1144      {
1145          $attrs = $this->_parse_attrs($tag_args);
1146          $arg_list = array();
1147  
1148          if (empty($attrs['from'])) {
1149              return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
1150          }
1151          $from = $attrs['from'];
1152  
1153          if (empty($attrs['item'])) {
1154              return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
1155          }
1156          $item = $this->_dequote($attrs['item']);
1157          if (!preg_match('~^\w+$~', $item)) {
1158              return $this->_syntax_error("'foreach: item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1159          }
1160  
1161          if (isset($attrs['key'])) {
1162              $key  = $this->_dequote($attrs['key']);
1163              if (!preg_match('~^\w+$~', $key)) {
1164                  return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1165              }
1166              $key_part = "\$this->_tpl_vars['$key'] => ";
1167          } else {
1168              $key = null;
1169              $key_part = '';
1170          }
1171  
1172          if (isset($attrs['name'])) {
1173              $name = $attrs['name'];
1174          } else {
1175              $name = null;
1176          }
1177  
1178          $output = '<?php ';
1179          $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
1180          if (isset($name)) {
1181              $foreach_props = "\$this->_foreach[$name]";
1182              $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
1183              $output .= "if ({$foreach_props}['total'] > 0):\n";
1184              $output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1185              $output .= "        {$foreach_props}['iteration']++;\n";
1186          } else {
1187              $output .= "if (count(\$_from)):\n";
1188              $output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1189          }
1190          $output .= '?>';
1191  
1192          return $output;
1193      }
1194  
1195  
1196      /**
1197       * Compile {capture} .. {/capture} tags
1198       *
1199       * @param boolean $start true if this is the {capture} tag
1200       * @param string $tag_args
1201       * @return string
1202       */
1203  
1204      function _compile_capture_tag($start, $tag_args = '')
1205      {
1206          $attrs = $this->_parse_attrs($tag_args);
1207  
1208          if ($start) {
1209              if (isset($attrs['name']))
1210                  $buffer = $attrs['name'];
1211              else
1212                  $buffer = "'default'";
1213  
1214              if (isset($attrs['assign']))
1215                  $assign = $attrs['assign'];
1216              else
1217                  $assign = null;
1218              $output = "<?php ob_start(); ?>";
1219              $this->_capture_stack[] = array($buffer, $assign);
1220          } else {
1221              list($buffer, $assign) = array_pop($this->_capture_stack);
1222              $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
1223              if (isset($assign)) {
1224                  $output .= " \$this->assign($assign, ob_get_contents());";
1225              }
1226              $output .= "ob_end_clean(); ?>";
1227          }
1228  
1229          return $output;
1230      }
1231  
1232      /**
1233       * Compile {if ...} tag
1234       *
1235       * @param string $tag_args
1236       * @param boolean $elseif if true, uses elseif instead of if
1237       * @return string
1238       */
1239      function _compile_if_tag($tag_args, $elseif = false)
1240      {
1241  
1242          /* Tokenize args for 'if' tag. */
1243          preg_match_all('~(?>
1244                  ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
1245                  ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)?    | # var or quoted string
1246                  \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@    | # valid non-word token
1247                  \b\w+\b                                                        | # valid word token
1248                  \S+                                                           # anything else
1249                  )~x', $tag_args, $match);
1250  
1251          $tokens = $match[0];
1252  
1253          // make sure we have balanced parenthesis
1254          $token_count = array_count_values($tokens);
1255          if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1256              $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
1257          }
1258  
1259          $is_arg_stack = array();
1260  
1261          for ($i = 0; $i < count($tokens); $i++) {
1262  
1263              $token = &$tokens[$i];
1264  
1265              switch (strtolower($token)) {
1266                  case '!':
1267                  case '%':
1268                  case '!==':
1269                  case '==':
1270                  case '===':
1271                  case '>':
1272                  case '<':
1273                  case '!=':
1274                  case '<>':
1275                  case '<<':
1276                  case '>>':
1277                  case '<=':
1278                  case '>=':
1279                  case '&&':
1280                  case '||':
1281                  case '|':
1282                  case '^':
1283                  case '&':
1284                  case '~':
1285                  case ')':
1286                  case ',':
1287                  case '+':
1288                  case '-':
1289                  case '*':
1290                  case '/':
1291                  case '@':
1292                      break;
1293  
1294                  case 'eq':
1295                      $token = '==';
1296                      break;
1297  
1298                  case 'ne':
1299                  case 'neq':
1300                      $token = '!=';
1301                      break;
1302  
1303                  case 'lt':
1304                      $token = '<';
1305                      break;
1306  
1307                  case 'le':
1308                  case 'lte':
1309                      $token = '<=';
1310                      break;
1311  
1312                  case 'gt':
1313                      $token = '>';
1314                      break;
1315  
1316                  case 'ge':
1317                  case 'gte':
1318                      $token = '>=';
1319                      break;
1320  
1321                  case 'and':
1322                      $token = '&&';
1323                      break;
1324  
1325                  case 'or':
1326                      $token = '||';
1327                      break;
1328  
1329                  case 'not':
1330                      $token = '!';
1331                      break;
1332  
1333                  case 'mod':
1334                      $token = '%';
1335                      break;
1336  
1337                  case '(':
1338                      array_push($is_arg_stack, $i);
1339                      break;
1340  
1341                  case 'is':
1342                      /* If last token was a ')', we operate on the parenthesized
1343                         expression. The start of the expression is on the stack.
1344                         Otherwise, we operate on the last encountered token. */
1345                      if ($tokens[$i-1] == ')')
1346                          $is_arg_start = array_pop($is_arg_stack);
1347                      else
1348                          $is_arg_start = $i-1;
1349                      /* Construct the argument for 'is' expression, so it knows
1350                         what to operate on. */
1351                      $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1352  
1353                      /* Pass all tokens from next one until the end to the
1354                         'is' expression parsing function. The function will
1355                         return modified tokens, where the first one is the result
1356                         of the 'is' expression and the rest are the tokens it
1357                         didn't touch. */
1358                      $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
1359  
1360                      /* Replace the old tokens with the new ones. */
1361                      array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1362  
1363                      /* Adjust argument start so that it won't change from the
1364                         current position for the next iteration. */
1365                      $i = $is_arg_start;
1366                      break;
1367  
1368                  default:
1369                      if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) {
1370                              // function call
1371                              if($this->security &&
1372                                 !in_array($token, $this->security_settings['IF_FUNCS'])) {
1373                                  $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1374                              }
1375                      } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
1376                          // variable function call
1377                          $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);                      
1378                      } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {
1379                          // object or variable
1380                          $token = $this->_parse_var_props($token);
1381                      } elseif(is_numeric($token)) {
1382                          // number, skip it
1383                      } else {
1384                          $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
1385                      }
1386                      break;
1387              }
1388          }
1389  
1390          if ($elseif)
1391              return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1392          else
1393              return '<?php if ('.implode(' ', $tokens).'): ?>';
1394      }
1395  
1396  
1397      function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1398          $arg_list = array();
1399  
1400          if (isset($type) && isset($name)
1401              && isset($this->_plugins[$type])
1402              && isset($this->_plugins[$type][$name])
1403              && empty($this->_plugins[$type][$name][4])
1404              && is_array($this->_plugins[$type][$name][5])
1405              ) {
1406              /* we have a list of parameters that should be cached */
1407              $_cache_attrs = $this->_plugins[$type][$name][5];
1408              $_count = $this->_cache_attrs_count++;
1409              $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1410  
1411          } else {
1412              /* no parameters are cached */
1413              $_cache_attrs = null;
1414          }
1415  
1416          foreach ($attrs as $arg_name => $arg_value) {
1417              if (is_bool($arg_value))
1418                  $arg_value = $arg_value ? 'true' : 'false';
1419              if (is_null($arg_value))
1420                  $arg_value = 'null';
1421              if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1422                  $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1423              } else {
1424                  $arg_list[] = "'$arg_name' => $arg_value";
1425              }
1426          }
1427          return $arg_list;
1428      }
1429  
1430      /**
1431       * Parse is expression
1432       *
1433       * @param string $is_arg
1434       * @param array $tokens
1435       * @return array
1436       */
1437      function _parse_is_expr($is_arg, $tokens)
1438      {
1439          $expr_end = 0;
1440          $negate_expr = false;
1441  
1442          if (($first_token = array_shift($tokens)) == 'not') {
1443              $negate_expr = true;
1444              $expr_type = array_shift($tokens);
1445          } else
1446              $expr_type = $first_token;
1447  
1448          switch ($expr_type) {
1449              case 'even':
1450                  if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1451                      $expr_end++;
1452                      $expr_arg = $tokens[$expr_end++];
1453                      $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1454                  } else
1455                      $expr = "!(1 & $is_arg)";
1456                  break;
1457  
1458              case 'odd':
1459                  if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1460                      $expr_end++;
1461                      $expr_arg = $tokens[$expr_end++];
1462                      $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1463                  } else
1464                      $expr = "(1 & $is_arg)";
1465                  break;
1466  
1467              case 'div':
1468                  if (@$tokens[$expr_end] == 'by') {
1469                      $expr_end++;
1470                      $expr_arg = $tokens[$expr_end++];
1471                      $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1472                  } else {
1473                      $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1474                  }
1475                  break;
1476  
1477              default:
1478                  $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1479                  break;
1480          }
1481  
1482          if ($negate_expr) {
1483              $expr = "!($expr)";
1484          }
1485  
1486          array_splice($tokens, 0, $expr_end, $expr);
1487  
1488          return $tokens;
1489      }
1490  
1491  
1492      /**
1493       * Parse attribute string
1494       *
1495       * @param string $tag_args
1496       * @return array
1497       */
1498      function _parse_attrs($tag_args)
1499      {
1500  
1501          /* Tokenize tag attributes. */
1502          preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
1503                           )+ |
1504                           [=]
1505                          ~x', $tag_args, $match);
1506          $tokens       = $match[0];
1507  
1508          $attrs = array();
1509          /* Parse state:
1510              0 - expecting attribute name
1511              1 - expecting '='
1512              2 - expecting attribute value (not '=') */
1513          $state = 0;
1514  
1515          foreach ($tokens as $token) {
1516              switch ($state) {
1517                  case 0:
1518                      /* If the token is a valid identifier, we set attribute name
1519                         and go to state 1. */
1520                      if (preg_match('~^\w+$~', $token)) {
1521                          $attr_name = $token;
1522                          $state = 1;
1523                      } else
1524                          $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1525                      break;
1526  
1527                  case 1:
1528                      /* If the token is '=', then we go to state 2. */
1529                      if ($token == '=') {
1530                          $state = 2;
1531                      } else
1532                          $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1533                      break;
1534  
1535                  case 2:
1536                      /* If token is not '=', we set the attribute value and go to
1537                         state 0. */
1538                      if ($token != '=') {
1539                          /* We booleanize the token if it's a non-quoted possible
1540                             boolean value. */
1541                          if (preg_match('~^(on|yes|true)$~', $token)) {
1542                              $token = 'true';
1543                          } else if (preg_match('~^(off|no|false)$~', $token)) {
1544                              $token = 'false';
1545                          } else if ($token == 'null') {
1546                              $token = 'null';
1547                          } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
1548                              /* treat integer literally */
1549                          } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {
1550                              /* treat as a string, double-quote it escaping quotes */
1551                              $token = '"'.addslashes($token).'"';
1552                          }
1553  
1554                          $attrs[$attr_name] = $token;
1555                          $state = 0;
1556                      } else
1557                          $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1558                      break;
1559              }
1560              $last_token = $token;
1561          }
1562  
1563          if($state != 0) {
1564              if($state == 1) {
1565                  $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1566              } else {
1567                  $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
1568              }
1569          }
1570  
1571          $this->_parse_vars_props($attrs);
1572  
1573          return $attrs;
1574      }
1575  
1576      /**
1577       * compile multiple variables and section properties tokens into
1578       * PHP code
1579       *
1580       * @param array $tokens
1581       */
1582      function _parse_vars_props(&$tokens)
1583      {
1584          foreach($tokens as $key => $val) {
1585              $tokens[$key] = $this->_parse_var_props($val);
1586          }
1587      }
1588  
1589      /**
1590       * compile single variable and section properties token into
1591       * PHP code
1592       *
1593       * @param string $val
1594       * @param string $tag_attrs
1595       * @return string
1596       */
1597      function _parse_var_props($val)
1598      {
1599          $val = trim($val);
1600  
1601          if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {
1602              // $ variable or object
1603              $return = $this->_parse_var($match[1]);
1604              $modifiers = $match[2];
1605              if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
1606                  $_default_mod_string = implode('|',(array)$this->default_modifiers);
1607                  $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
1608              }
1609              $this->_parse_modifiers($return, $modifiers);
1610              return $return;
1611          } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1612                  // double quoted text
1613                  preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1614                  $return = $this->_expand_quoted_text($match[1]);
1615                  if($match[2] != '') {
1616                      $this->_parse_modifiers($return, $match[2]);
1617                  }
1618                  return $return;
1619              }
1620          elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1621                  // numerical constant
1622                  preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1623                  if($match[2] != '') {
1624                      $this->_parse_modifiers($match[1], $match[2]);
1625                      return $match[1];
1626                  }
1627              }
1628          elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1629                  // single quoted text
1630                  preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1631                  if($match[2] != '') {
1632                      $this->_parse_modifiers($match[1], $match[2]);
1633                      return $match[1];
1634                  }
1635              }
1636          elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1637                  // config var
1638                  return $this->_parse_conf_var($val);
1639              }
1640          elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1641                  // section var
1642                  return $this->_parse_section_prop($val);
1643              }
1644          elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
1645              // literal string
1646              return $this->_expand_quoted_text('"' . $val .'"');
1647          }
1648          return $val;
1649      }
1650  
1651      /**
1652       * expand quoted text with embedded variables
1653       *
1654       * @param string $var_expr
1655       * @return string
1656       */
1657      function _expand_quoted_text($var_expr)
1658      {
1659          // if contains unescaped $, expand it
1660          if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) {
1661              $_match = $_match[0];
1662              rsort($_match);
1663              reset($_match);
1664              foreach($_match as $_var) {
1665                  $var_expr = str_replace ($_var, '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."', $var_expr);
1666              }
1667              $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr);
1668          } else {
1669              $_return = $var_expr;
1670          }
1671          // replace double quoted literal string with single quotes
1672          $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return);
1673          return $_return;
1674      }
1675  
1676      /**
1677       * parse variable expression into PHP code
1678       *
1679       * @param string $var_expr
1680       * @param string $output
1681       * @return string
1682       */
1683      function _parse_var($var_expr)
1684      {
1685          $_has_math = false;
1686          $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
1687  
1688          if(count($_math_vars) > 1) {
1689              $_first_var = "";
1690              $_complete_var = "";
1691              $_output = "";
1692              // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
1693              foreach($_math_vars as $_k => $_math_var) {
1694                  $_math_var = $_math_vars[$_k];
1695  
1696                  if(!empty($_math_var) || is_numeric($_math_var)) {
1697                      // hit a math operator, so process the stuff which came before it
1698                      if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
1699                          $_has_math = true;
1700                          if(!empty($_complete_var) || is_numeric($_complete_var)) {
1701                              $_output .= $this->_parse_var($_complete_var);
1702                          }
1703  
1704                          // just output the math operator to php
1705                          $_output .= $_math_var;
1706  
1707                          if(empty($_first_var))
1708                              $_first_var = $_complete_var;
1709  
1710                          $_complete_var = "";
1711                      } else {
1712                          $_complete_var .= $_math_var;
1713                      }
1714                  }
1715              }
1716              if($_has_math) {
1717                  if(!empty($_complete_var) || is_numeric($_complete_var))
1718                      $_output .= $this->_parse_var($_complete_var);
1719  
1720                  // get the modifiers working (only the last var from math + modifier is left)
1721                  $var_expr = $_complete_var;
1722              }
1723          }
1724  
1725          // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
1726          if(is_numeric($var_expr{0}))
1727              $_var_ref = $var_expr;
1728          else
1729              $_var_ref = substr($var_expr, 1);
1730          
1731          if(!$_has_math) {
1732              
1733              // get [foo] and .foo and ->foo and (...) pieces
1734              preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match);
1735                          
1736              $_indexes = $match[0];
1737              $_var_name = array_shift($_indexes);
1738  
1739              /* Handle $smarty.* variable references as a special case. */
1740              if ($_var_name == 'smarty') {
1741                  /*
1742                   * If the reference could be compiled, use the compiled output;
1743                   * otherwise, fall back on the $smarty variable generated at
1744                   * run-time.
1745                   */
1746                  if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
1747                      $_output = $smarty_ref;
1748                  } else {
1749                      $_var_name = substr(array_shift($_indexes), 1);
1750                      $_output = "\$this->_smarty_vars['$_var_name']";
1751                  }
1752              } elseif(is_numeric($_var_name) && is_numeric($var_expr{0})) {
1753                  // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
1754                  if(count($_indexes) > 0)
1755                  {
1756                      $_var_name .= implode("", $_indexes);
1757                      $_indexes = array();
1758                  }
1759                  $_output = $_var_name;
1760              } else {
1761                  $_output = "\$this->_tpl_vars['$_var_name']";
1762              }
1763  
1764              foreach ($_indexes as $_index) {
1765                  if ($_index{0} == '[') {
1766                      $_index = substr($_index, 1, -1);
1767                      if (is_numeric($_index)) {
1768                          $_output .= "[$_index]";
1769                      } elseif ($_index{0} == '$') {
1770                          if (strpos($_index, '.') !== false) {
1771                              $_output .= '[' . $this->_parse_var($_index) . ']';
1772                          } else {
1773                              $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
1774                          }
1775                      } else {
1776                          $_var_parts = explode('.', $_index);
1777                          $_var_section = $_var_parts[0];
1778                          $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
1779                          $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
1780                      }
1781                  } else if ($_index{0} == '.') {
1782                      if ($_index{1} == '$')
1783                          $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
1784                      else
1785                          $_output .= "['" . substr($_index, 1) . "']";
1786                  } else if (substr($_index,0,2) == '->') {
1787                      if(substr($_index,2,2) == '__') {
1788                          $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1789                      } elseif($this->security && substr($_index, 2, 1) == '_') {
1790                          $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1791                      } elseif ($_index{2} == '$') {
1792                          if ($this->security) {
1793                              $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1794                          } else {
1795                              $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
1796                          }
1797                      } else {
1798                          $_output .= $_index;
1799                      }
1800                  } elseif ($_index{0} == '(') {
1801                      $_index = $this->_parse_parenth_args($_index);
1802                      $_output .= $_index;
1803                  } else {
1804                      $_output .= $_index;
1805                  }
1806              }
1807          }
1808  
1809          return $_output;
1810      }
1811  
1812      /**
1813       * parse arguments in function call parenthesis
1814       *
1815       * @param string $parenth_args
1816       * @return string
1817       */
1818      function _parse_parenth_args($parenth_args)
1819      {
1820          preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match);
1821          $orig_vals = $match = $match[0];
1822          $this->_parse_vars_props($match);
1823          $replace = array();
1824          for ($i = 0, $count = count($match); $i < $count; $i++) {
1825              $replace[$orig_vals[$i]] = $match[$i];
1826          }
1827          return strtr($parenth_args, $replace);
1828      }
1829  
1830      /**
1831       * parse configuration variable expression into PHP code
1832       *
1833       * @param string $conf_var_expr
1834       */
1835      function _parse_conf_var($conf_var_expr)
1836      {
1837          $parts = explode('|', $conf_var_expr, 2);
1838          $var_ref = $parts[0];
1839          $modifiers = isset($parts[1]) ? $parts[1] : '';
1840  
1841          $var_name = substr($var_ref, 1, -1);
1842  
1843          $output = "\$this->_config[0]['vars']['$var_name']";
1844  
1845          $this->_parse_modifiers($output, $modifiers);
1846  
1847          return $output;
1848      }
1849  
1850      /**
1851       * parse section property expression into PHP code
1852       *
1853       * @param string $section_prop_expr
1854       * @return string
1855       */
1856      function _parse_section_prop($section_prop_expr)
1857      {
1858          $parts = explode('|', $section_prop_expr, 2);
1859          $var_ref = $parts[0];
1860          $modifiers = isset($parts[1]) ? $parts[1] : '';
1861  
1862          preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1863          $section_name = $match[1];
1864          $prop_name = $match[2];
1865  
1866          $output = "\$this->_sections['$section_name']['$prop_name']";
1867  
1868          $this->_parse_modifiers($output, $modifiers);
1869  
1870          return $output;
1871      }
1872  
1873  
1874      /**
1875       * parse modifier chain into PHP code
1876       *
1877       * sets $output to parsed modified chain
1878       * @param string $output
1879       * @param string $modifier_string
1880       */
1881      function _parse_modifiers(&$output, $modifier_string)
1882      {
1883          preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
1884          list(, $_modifiers, $modifier_arg_strings) = $_match;
1885  
1886          for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
1887              $_modifier_name = $_modifiers[$_i];
1888  
1889              if($_modifier_name == 'smarty') {
1890                  // skip smarty modifier
1891                  continue;
1892              }
1893  
1894              preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
1895              $_modifier_args = $_match[1];
1896  
1897              if ($_modifier_name{0} == '@') {
1898                  $_map_array = false;
1899                  $_modifier_name = substr($_modifier_name, 1);
1900              } else {
1901                  $_map_array = true;
1902              }
1903  
1904              if (empty($this->_plugins['modifier'][$_modifier_name])
1905                  && !$this->_get_plugin_filepath('modifier', $_modifier_name)
1906                  && function_exists($_modifier_name)) {
1907                  if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
1908                      $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
1909                  } else {
1910                      $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name,  null, null, false);
1911                  }
1912              }
1913              $this->_add_plugin('modifier', $_modifier_name);
1914  
1915              $this->_parse_vars_props($_modifier_args);
1916  
1917              if($_modifier_name == 'default') {
1918                  // supress notifications of default modifier vars and args
1919                  if($output{0} == '$') {
1920                      $output = '@' . $output;
1921                  }
1922                  if(isset($_modifier_args[0]) && $_modifier_args[0]{0} == '$') {
1923                      $_modifier_args[0] = '@' . $_modifier_args[0];
1924                  }
1925              }
1926              if (count($_modifier_args) > 0)
1927                  $_modifier_args = ', '.implode(', ', $_modifier_args);
1928              else
1929                  $_modifier_args = '';
1930  
1931              if ($_map_array) {
1932                  $output = "((is_array(\$_tmp=$output)) ? \$this->_run_mod_handler('$_modifier_name', true, \$_tmp$_modifier_args) : " . $this->_compile_plugin_call('modifier', $_modifier_name) . "(\$_tmp$_modifier_args))";
1933  
1934              } else {
1935  
1936                  $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
1937  
1938              }
1939          }
1940      }
1941  
1942  
1943      /**
1944       * add plugin
1945       *
1946       * @param string $type
1947       * @param string $name
1948       * @param boolean? $delayed_loading
1949       */
1950      function _add_plugin($type, $name, $delayed_loading = null)
1951      {
1952          if (!isset($this->_plugin_info[$type])) {
1953              $this->_plugin_info[$type] = array();
1954          }
1955          if (!isset($this->_plugin_info[$type][$name])) {
1956              $this->_plugin_info[$type][$name] = array($this->_current_file,
1957                                                        $this->_current_line_no,
1958                                                        $delayed_loading);
1959          }
1960      }
1961  
1962  
1963      /**
1964       * Compiles references of type $smarty.foo
1965       *
1966       * @param string $indexes
1967       * @return string
1968       */
1969      function _compile_smarty_ref(&$indexes)
1970      {
1971          /* Extract the reference name. */
1972          $_ref = substr($indexes[0], 1);
1973          foreach($indexes as $_index_no=>$_index) {
1974              if ($_index{0} != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) {
1975                  $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
1976              }
1977          }
1978  
1979          switch ($_ref) {
1980              case 'now':
1981                  $compiled_ref = 'time()';
1982                  $_max_index = 1;
1983                  break;
1984  
1985              case 'foreach':
1986                  array_shift($indexes);
1987                  $_var = $this->_parse_var_props(substr($indexes[0], 1));
1988                  $_propname = substr($indexes[1], 1);
1989                  $_max_index = 1;
1990                  switch ($_propname) {
1991                      case 'index':
1992                          array_shift($indexes);
1993                          $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)";
1994                          break;
1995                          
1996                      case 'first':
1997                          array_shift($indexes);
1998                          $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)";
1999                          break;
2000  
2001                      case 'last':
2002                          array_shift($indexes);
2003                          $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])";
2004                          break;
2005                          
2006                      case 'show':
2007                          array_shift($indexes);
2008                          $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)";
2009                          break;
2010                          
2011                      default:
2012                          unset($_max_index);
2013                          $compiled_ref = "\$this->_foreach[$_var]";
2014                  }
2015                  break;
2016  
2017              case 'section':
2018                  array_shift($indexes);
2019                  $_var = $this->_parse_var_props(substr($indexes[0], 1));
2020                  $compiled_ref = "\$this->_sections[$_var]";
2021                  break;
2022  
2023              case 'get':
2024                  $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
2025                  break;
2026  
2027              case 'post':
2028                  $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
2029                  break;
2030  
2031              case 'cookies':
2032                  $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
2033                  break;
2034  
2035              case 'env':
2036                  $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
2037                  break;
2038  
2039              case 'server':
2040                  $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
2041                  break;
2042  
2043              case 'session':
2044                  $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
2045                  break;
2046  
2047              /*
2048               * These cases are handled either at run-time or elsewhere in the
2049               * compiler.
2050               */
2051              case 'request':
2052                  if ($this->request_use_auto_globals) {
2053                      $compiled_ref = '$_REQUEST';
2054                      break;
2055                  } else {
2056                      $this->_init_smarty_vars = true;
2057                  }
2058                  return null;
2059  
2060              case 'capture':
2061                  return null;
2062  
2063              case 'template':
2064                  $compiled_ref = "'$this->_current_file'";
2065                  $_max_index = 1;
2066                  break;
2067  
2068              case 'version':
2069                  $compiled_ref = "'$this->_version'";
2070                  $_max_index = 1;
2071                  break;
2072  
2073              case 'const':
2074                  if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
2075                      $this->_syntax_error("(secure mode) constants not permitted",
2076                                           E_USER_WARNING, __FILE__, __LINE__);
2077                      return;
2078                  }
2079                  array_shift($indexes);
2080                  if (preg_match('!^\.\w+$!', $indexes[0])) {
2081                      $compiled_ref = '@' . substr($indexes[0], 1);
2082                  } else {
2083                      $_val = $this->_parse_var_props(substr($indexes[0], 1));
2084                      $compiled_ref = '@constant(' . $_val . ')';
2085                  }
2086                  $_max_index = 1;
2087                  break;
2088  
2089              case 'config':
2090                  $compiled_ref = "\$this->_config[0]['vars']";
2091                  $_max_index = 3;
2092                  break;
2093  
2094              case 'ldelim':
2095                  $compiled_ref = "'$this->left_delimiter'";
2096                  break;
2097  
2098              case 'rdelim':
2099                  $compiled_ref = "'$this->right_delimiter'";
2100                  break;
2101                  
2102              default:
2103                  $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
2104                  break;
2105          }
2106  
2107          if (isset($_max_index) && count($indexes) > $_max_index) {
2108              $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2109          }
2110  
2111          array_shift($indexes);
2112          return $compiled_ref;
2113      }
2114  
2115      /**
2116       * compiles call to plugin of type $type with name $name
2117       * returns a string containing the function-name or method call
2118       * without the paramter-list that would have follow to make the
2119       * call valid php-syntax
2120       *
2121       * @param string $type
2122       * @param string $name
2123       * @return string
2124       */
2125      function _compile_plugin_call($type, $name) {
2126          if (isset($this->_plugins[$type][$name])) {
2127              /* plugin loaded */
2128              if (is_array($this->_plugins[$type][$name][0])) {
2129                  return ((is_object($this->_plugins[$type][$name][0][0])) ?
2130                          "\$this->_plugins['$type']['$name'][0][0]->"    /* method callback */
2131                          : (string)($this->_plugins[$type][$name][0][0]).'::'    /* class callback */
2132                         ). $this->_plugins[$type][$name][0][1];
2133  
2134              } else {
2135                  /* function callback */
2136                  return $this->_plugins[$type][$name][0];
2137  
2138              }
2139          } else {
2140              /* plugin not loaded -> auto-loadable-plugin */
2141              return 'smarty_'.$type.'_'.$name;
2142  
2143          }
2144      }
2145  
2146      /**
2147       * load pre- and post-filters
2148       */
2149      function _load_filters()
2150      {
2151          if (count($this->_plugins['prefilter']) > 0) {
2152              foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
2153                  if ($prefilter === false) {
2154                      unset($this->_plugins['prefilter'][$filter_name]);
2155                      $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
2156                      require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2157                      smarty_core_load_plugins($_params, $this);
2158                  }
2159              }
2160          }
2161          if (count($this->_plugins['postfilter']) > 0) {
2162              foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
2163                  if ($postfilter === false) {
2164                      unset($this->_plugins['postfilter'][$filter_name]);
2165                      $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
2166                      require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2167                      smarty_core_load_plugins($_params, $this);
2168                  }
2169              }
2170          }
2171      }
2172  
2173  
2174      /**
2175       * Quote subpattern references
2176       *
2177       * @param string $string
2178       * @return string
2179       */
2180      function _quote_replace($string)
2181      {
2182          return strtr($string, array('\\' => '\\\\', '$' => '\\$'));
2183      }
2184  
2185      /**
2186       * display Smarty syntax error
2187       *
2188       * @param string $error_msg
2189       * @param integer $error_type
2190       * @param string $file
2191       * @param integer $line
2192       */
2193      function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
2194      {
2195          $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
2196      }
2197  
2198  
2199      /**
2200       * check if the compilation changes from cacheable to
2201       * non-cacheable state with the beginning of the current
2202       * plugin. return php-code to reflect the transition.
2203       * @return string
2204       */
2205      function _push_cacheable_state($type, $name) {
2206          $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2207          if ($_cacheable
2208              || 0<$this->_cacheable_state++) return '';
2209          if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
2210          $_ret = 'if ($this->caching && !$this->_cache_including) { echo \'{nocache:'
2211              . $this->_cache_serial . '#' . $this->_nocache_count
2212              . '}\';}';
2213          return $_ret;
2214      }
2215  
2216  
2217      /**
2218       * check if the compilation changes from non-cacheable to
2219       * cacheable state with the end of the current plugin return
2220       * php-code to reflect the transition.
2221       * @return string
2222       */
2223      function _pop_cacheable_state($type, $name) {
2224          $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2225          if ($_cacheable
2226              || --$this->_cacheable_state>0) return '';
2227          return 'if ($this->caching && !$this->_cache_including) { echo \'{/nocache:'
2228              . $this->_cache_serial . '#' . ($this->_nocache_count++)
2229              . '}\';}';
2230      }
2231  
2232  
2233      /**
2234       * push opening tag-name, file-name and line-number on the tag-stack
2235       * @param string the opening tag's name
2236       */
2237      function _push_tag($open_tag)
2238      {
2239          array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
2240      }
2241  
2242      /**
2243       * pop closing tag-name
2244       * raise an error if this stack-top doesn't match with the closing tag
2245       * @param string the closing tag's name
2246       * @return string the opening tag's name
2247       */
2248      function _pop_tag($close_tag)
2249      {
2250          $message = '';
2251          if (count($this->_tag_stack)>0) {
2252              list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
2253              if ($close_tag == $_open_tag) {
2254                  return $_open_tag;
2255              }
2256              if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
2257                  return $this->_pop_tag($close_tag);
2258              }
2259              if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
2260                  $this->_pop_tag($close_tag);
2261                  return $_open_tag;
2262              }
2263              if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
2264                  $this->_pop_tag($close_tag);
2265                  return $_open_tag;
2266              }
2267              if ($_open_tag == 'else' || $_open_tag == 'elseif') {
2268                  $_open_tag = 'if';
2269              } elseif ($_open_tag == 'sectionelse') {
2270                  $_open_tag = 'section';
2271              } elseif ($_open_tag == 'foreachelse') {
2272                  $_open_tag = 'foreach';
2273              }
2274              $message = " expected {/$_open_tag} (opened line $_line_no).";
2275          }
2276          $this->_syntax_error("mismatched tag {/$close_tag}.$message",
2277                               E_USER_ERROR, __FILE__, __LINE__);
2278      }
2279  
2280  }
2281  
2282  /**
2283   * compare to values by their string length
2284   *
2285   * @access private
2286   * @param string $a
2287   * @param string $b
2288   * @return 0|-1|1
2289   */
2290  function _smarty_sort_length($a, $b)
2291  {
2292      if($a == $b)
2293          return 0;
2294  
2295      if(strlen($a) == strlen($b))
2296          return ($a > $b) ? -1 : 1;
2297  
2298      return (strlen($a) > strlen($b)) ? -1 : 1;
2299  }
2300  
2301  
2302  /* vim: set et: */
2303  
2304  ?>


Généré le : Mon Nov 26 14:10:01 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics