Syntax Desktop PHP Cross Reference Web Portal Systems

Source: /public/lib/smarty/libs/Smarty_Compiler.class.php - 2365 lines - 94363 bytes - Summary - Text - Print

Description: Project:     Smarty: the PHP compiling template engine File:        Smarty_Compiler.class.php This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

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

title

Description

title

Description

title

Description

title

title

Body