[ Index ]
 

Code source de CMS made simple 1.0.5

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

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


Généré le : Tue Apr 3 18:50:37 2007 par Balluche grâce à PHPXref 0.7