[ Index ]
 

Code source de vtiger CRM 5.0.2

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

title

Body

[fermer]

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


Généré le : Sun Feb 25 10:22:19 2007 par Balluche grâce à PHPXref 0.7