[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/lib/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.2 2005/04/19 09:52:15 thepurpleblob 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