[ Index ]
 

Code source de XOOPS 2.0.17.1

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

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

   1  <?php
   2  
   3  /**
   4   * Project:     Smarty: the PHP compiling template engine
   5   * File:        Smarty_Compiler.class.php
   6   *
   7   * This library is free software; you can redistribute it and/or
   8   * modify it under the terms of the GNU Lesser General Public
   9   * License as published by the Free Software Foundation; either
  10   * version 2.1 of the License, or (at your option) any later version.
  11   *
  12   * This library is distributed in the hope that it will be useful,
  13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15   * Lesser General Public License for more details.
  16   *
  17   * You should have received a copy of the GNU Lesser General Public
  18   * License along with this library; if not, write to the Free Software
  19   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20   *
  21   * @link http://smarty.php.net/
  22   * @author Monte Ohrt <monte at ohrt dot com>
  23   * @author Andrei Zmievski <andrei@php.net>
  24   * @version 2.6.18
  25   * @copyright 2001-2005 New Digital Group, Inc.
  26   * @package Smarty
  27   */
  28  
  29  /* $Id: Smarty_Compiler.class.php 942 2007-08-05 15:12:39Z dugris $ */
  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 = 'null';
 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              if (isset($attrs['name']))
1226                  $buffer = $attrs['name'];
1227              else
1228                  $buffer = "'default'";
1229  
1230              if (isset($attrs['assign']))
1231                  $assign = $attrs['assign'];
1232              else
1233                  $assign = null;
1234              $output = "<?php ob_start(); ?>";
1235              $this->_capture_stack[] = array($buffer, $assign);
1236          } else {
1237              list($buffer, $assign) = array_pop($this->_capture_stack);
1238              $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
1239              if (isset($assign)) {
1240                  $output .= " \$this->assign($assign, ob_get_contents());";
1241              }
1242              $output .= "ob_end_clean(); ?>";
1243          }
1244  
1245          return $output;
1246      }
1247  
1248      /**
1249       * Compile {if ...} tag
1250       *
1251       * @param string $tag_args
1252       * @param boolean $elseif if true, uses elseif instead of if
1253       * @return string
1254       */
1255      function _compile_if_tag($tag_args, $elseif = false)
1256      {
1257  
1258          /* Tokenize args for 'if' tag. */
1259          preg_match_all('~(?>
1260                  ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
1261                  ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)?    | # var or quoted string
1262                  \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@    | # valid non-word token
1263                  \b\w+\b                                                        | # valid word token
1264                  \S+                                                           # anything else
1265                  )~x', $tag_args, $match);
1266  
1267          $tokens = $match[0];
1268  
1269          if(empty($tokens)) {
1270              $_error_msg = $elseif ? "'elseif'" : "'if'";
1271              $_error_msg .= ' statement requires arguments'; 
1272              $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__);
1273          }
1274              
1275                  
1276          // make sure we have balanced parenthesis
1277          $token_count = array_count_values($tokens);
1278          if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1279              $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
1280          }
1281  
1282          $is_arg_stack = array();
1283  
1284          for ($i = 0; $i < count($tokens); $i++) {
1285  
1286              $token = &$tokens[$i];
1287  
1288              switch (strtolower($token)) {
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                  case '/':
1314                  case '@':
1315                      break;
1316  
1317                  case 'eq':
1318                      $token = '==';
1319                      break;
1320  
1321                  case 'ne':
1322                  case 'neq':
1323                      $token = '!=';
1324                      break;
1325  
1326                  case 'lt':
1327                      $token = '<';
1328                      break;
1329  
1330                  case 'le':
1331                  case 'lte':
1332                      $token = '<=';
1333                      break;
1334  
1335                  case 'gt':
1336                      $token = '>';
1337                      break;
1338  
1339                  case 'ge':
1340                  case 'gte':
1341                      $token = '>=';
1342                      break;
1343  
1344                  case 'and':
1345                      $token = '&&';
1346                      break;
1347  
1348                  case 'or':
1349                      $token = '||';
1350                      break;
1351  
1352                  case 'not':
1353                      $token = '!';
1354                      break;
1355  
1356                  case 'mod':
1357                      $token = '%';
1358                      break;
1359  
1360                  case '(':
1361                      array_push($is_arg_stack, $i);
1362                      break;
1363  
1364                  case 'is':
1365                      /* If last token was a ')', we operate on the parenthesized
1366                         expression. The start of the expression is on the stack.
1367                         Otherwise, we operate on the last encountered token. */
1368                      if ($tokens[$i-1] == ')')
1369                          $is_arg_start = array_pop($is_arg_stack);
1370                      else
1371                          $is_arg_start = $i-1;
1372                      /* Construct the argument for 'is' expression, so it knows
1373                         what to operate on. */
1374                      $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1375  
1376                      /* Pass all tokens from next one until the end to the
1377                         'is' expression parsing function. The function will
1378                         return modified tokens, where the first one is the result
1379                         of the 'is' expression and the rest are the tokens it
1380                         didn't touch. */
1381                      $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
1382  
1383                      /* Replace the old tokens with the new ones. */
1384                      array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1385  
1386                      /* Adjust argument start so that it won't change from the
1387                         current position for the next iteration. */
1388                      $i = $is_arg_start;
1389                      break;
1390  
1391                  default:
1392                      if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) {
1393                              // function call
1394                              if($this->security &&
1395                                 !in_array($token, $this->security_settings['IF_FUNCS'])) {
1396                                  $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1397                              }
1398                      } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && (strpos('+-*/^%&|', substr($token, -1)) === false) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
1399                          // variable function call
1400                          $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);                      
1401                      } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {
1402                          // object or variable
1403                          $token = $this->_parse_var_props($token);
1404                      } elseif(is_numeric($token)) {
1405                          // number, skip it
1406                      } else {
1407                          $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
1408                      }
1409                      break;
1410              }
1411          }
1412  
1413          if ($elseif)
1414              return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1415          else
1416              return '<?php if ('.implode(' ', $tokens).'): ?>';
1417      }
1418  
1419  
1420      function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1421          $arg_list = array();
1422  
1423          if (isset($type) && isset($name)
1424              && isset($this->_plugins[$type])
1425              && isset($this->_plugins[$type][$name])
1426              && empty($this->_plugins[$type][$name][4])
1427              && is_array($this->_plugins[$type][$name][5])
1428              ) {
1429              /* we have a list of parameters that should be cached */
1430              $_cache_attrs = $this->_plugins[$type][$name][5];
1431              $_count = $this->_cache_attrs_count++;
1432              $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1433  
1434          } else {
1435              /* no parameters are cached */
1436              $_cache_attrs = null;
1437          }
1438  
1439          foreach ($attrs as $arg_name => $arg_value) {
1440              if (is_bool($arg_value))
1441                  $arg_value = $arg_value ? 'true' : 'false';
1442              if (is_null($arg_value))
1443                  $arg_value = 'null';
1444              if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1445                  $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1446              } else {
1447                  $arg_list[] = "'$arg_name' => $arg_value";
1448              }
1449          }
1450          return $arg_list;
1451      }
1452  
1453      /**
1454       * Parse is expression
1455       *
1456       * @param string $is_arg
1457       * @param array $tokens
1458       * @return array
1459       */
1460      function _parse_is_expr($is_arg, $tokens)
1461      {
1462          $expr_end = 0;
1463          $negate_expr = false;
1464  
1465          if (($first_token = array_shift($tokens)) == 'not') {
1466              $negate_expr = true;
1467              $expr_type = array_shift($tokens);
1468          } else
1469              $expr_type = $first_token;
1470  
1471          switch ($expr_type) {
1472              case 'even':
1473                  if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1474                      $expr_end++;
1475                      $expr_arg = $tokens[$expr_end++];
1476                      $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1477                  } else
1478                      $expr = "!(1 & $is_arg)";
1479                  break;
1480  
1481              case 'odd':
1482                  if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1483                      $expr_end++;
1484                      $expr_arg = $tokens[$expr_end++];
1485                      $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1486                  } else
1487                      $expr = "(1 & $is_arg)";
1488                  break;
1489  
1490              case 'div':
1491                  if (@$tokens[$expr_end] == 'by') {
1492                      $expr_end++;
1493                      $expr_arg = $tokens[$expr_end++];
1494                      $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1495                  } else {
1496                      $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1497                  }
1498                  break;
1499  
1500              default:
1501                  $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1502                  break;
1503          }
1504  
1505          if ($negate_expr) {
1506              $expr = "!($expr)";
1507          }
1508  
1509          array_splice($tokens, 0, $expr_end, $expr);
1510  
1511          return $tokens;
1512      }
1513  
1514  
1515      /**
1516       * Parse attribute string
1517       *
1518       * @param string $tag_args
1519       * @return array
1520       */
1521      function _parse_attrs($tag_args)
1522      {
1523  
1524          /* Tokenize tag attributes. */
1525          preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
1526                           )+ |
1527                           [=]
1528                          ~x', $tag_args, $match);
1529          $tokens       = $match[0];
1530  
1531          $attrs = array();
1532          /* Parse state:
1533              0 - expecting attribute name
1534              1 - expecting '='
1535              2 - expecting attribute value (not '=') */
1536          $state = 0;
1537  
1538          foreach ($tokens as $token) {
1539              switch ($state) {
1540                  case 0:
1541                      /* If the token is a valid identifier, we set attribute name
1542                         and go to state 1. */
1543                      if (preg_match('~^\w+$~', $token)) {
1544                          $attr_name = $token;
1545                          $state = 1;
1546                      } else
1547                          $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1548                      break;
1549  
1550                  case 1:
1551                      /* If the token is '=', then we go to state 2. */
1552                      if ($token == '=') {
1553                          $state = 2;
1554                      } else
1555                          $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1556                      break;
1557  
1558                  case 2:
1559                      /* If token is not '=', we set the attribute value and go to
1560                         state 0. */
1561                      if ($token != '=') {
1562                          /* We booleanize the token if it's a non-quoted possible
1563                             boolean value. */
1564                          if (preg_match('~^(on|yes|true)$~', $token)) {
1565                              $token = 'true';
1566                          } else if (preg_match('~^(off|no|false)$~', $token)) {
1567                              $token = 'false';
1568                          } else if ($token == 'null') {
1569                              $token = 'null';
1570                          } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
1571                              /* treat integer literally */
1572                          } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {
1573                              /* treat as a string, double-quote it escaping quotes */
1574                              $token = '"'.addslashes($token).'"';
1575                          }
1576  
1577                          $attrs[$attr_name] = $token;
1578                          $state = 0;
1579                      } else
1580                          $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1581                      break;
1582              }
1583              $last_token = $token;
1584          }
1585  
1586          if($state != 0) {
1587              if($state == 1) {
1588                  $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1589              } else {
1590                  $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
1591              }
1592          }
1593  
1594          $this->_parse_vars_props($attrs);
1595  
1596          return $attrs;
1597      }
1598  
1599      /**
1600       * compile multiple variables and section properties tokens into
1601       * PHP code
1602       *
1603       * @param array $tokens
1604       */
1605      function _parse_vars_props(&$tokens)
1606      {
1607          foreach($tokens as $key => $val) {
1608              $tokens[$key] = $this->_parse_var_props($val);
1609          }
1610      }
1611  
1612      /**
1613       * compile single variable and section properties token into
1614       * PHP code
1615       *
1616       * @param string $val
1617       * @param string $tag_attrs
1618       * @return string
1619       */
1620      function _parse_var_props($val)
1621      {
1622          $val = trim($val);
1623  
1624          if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {
1625              // $ variable or object
1626              $return = $this->_parse_var($match[1]);
1627              $modifiers = $match[2];
1628              if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
1629                  $_default_mod_string = implode('|',(array)$this->default_modifiers);
1630                  $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
1631              }
1632              $this->_parse_modifiers($return, $modifiers);
1633              return $return;
1634          } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1635                  // double quoted text
1636                  preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1637                  $return = $this->_expand_quoted_text($match[1]);
1638                  if($match[2] != '') {
1639                      $this->_parse_modifiers($return, $match[2]);
1640                  }
1641                  return $return;
1642              }
1643          elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1644                  // numerical constant
1645                  preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1646                  if($match[2] != '') {
1647                      $this->_parse_modifiers($match[1], $match[2]);
1648                      return $match[1];
1649                  }
1650              }
1651          elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1652                  // single quoted text
1653                  preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1654                  if($match[2] != '') {
1655                      $this->_parse_modifiers($match[1], $match[2]);
1656                      return $match[1];
1657                  }
1658              }
1659          elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1660                  // config var
1661                  return $this->_parse_conf_var($val);
1662              }
1663          elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1664                  // section var
1665                  return $this->_parse_section_prop($val);
1666              }
1667          elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
1668              // literal string
1669              return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"');
1670          }
1671          return $val;
1672      }
1673  
1674      /**
1675       * expand quoted text with embedded variables
1676       *
1677       * @param string $var_expr
1678       * @return string
1679       */
1680      function _expand_quoted_text($var_expr)
1681      {
1682          // if contains unescaped $, expand it
1683          if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) {
1684              $_match = $_match[0];
1685              $_replace = array();
1686              foreach($_match as $_var) {
1687                  $_replace[$_var] = '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."';
1688              }
1689              $var_expr = strtr($var_expr, $_replace);
1690              $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr);
1691          } else {
1692              $_return = $var_expr;
1693          }
1694          // replace double quoted literal string with single quotes
1695          $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return);
1696          return $_return;
1697      }
1698  
1699      /**
1700       * parse variable expression into PHP code
1701       *
1702       * @param string $var_expr
1703       * @param string $output
1704       * @return string
1705       */
1706      function _parse_var($var_expr)
1707      {
1708          $_has_math = false;
1709          $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
1710  
1711          if(count($_math_vars) > 1) {
1712              $_first_var = "";
1713              $_complete_var = "";
1714              $_output = "";
1715              // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
1716              foreach($_math_vars as $_k => $_math_var) {
1717                  $_math_var = $_math_vars[$_k];
1718  
1719                  if(!empty($_math_var) || is_numeric($_math_var)) {
1720                      // hit a math operator, so process the stuff which came before it
1721                      if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
1722                          $_has_math = true;
1723                          if(!empty($_complete_var) || is_numeric($_complete_var)) {
1724                              $_output .= $this->_parse_var($_complete_var);
1725                          }
1726  
1727                          // just output the math operator to php
1728                          $_output .= $_math_var;
1729  
1730                          if(empty($_first_var))
1731                              $_first_var = $_complete_var;
1732  
1733                          $_complete_var = "";
1734                      } else {
1735                          $_complete_var .= $_math_var;
1736                      }
1737                  }
1738              }
1739              if($_has_math) {
1740                  if(!empty($_complete_var) || is_numeric($_complete_var))
1741                      $_output .= $this->_parse_var($_complete_var);
1742  
1743                  // get the modifiers working (only the last var from math + modifier is left)
1744                  $var_expr = $_complete_var;
1745              }
1746          }
1747  
1748          // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
1749          if(is_numeric(substr($var_expr, 0, 1)))
1750              $_var_ref = $var_expr;
1751          else
1752              $_var_ref = substr($var_expr, 1);
1753          
1754          if(!$_has_math) {
1755              
1756              // get [foo] and .foo and ->foo and (...) pieces
1757              preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match);
1758                          
1759              $_indexes = $match[0];
1760              $_var_name = array_shift($_indexes);
1761  
1762              /* Handle $smarty.* variable references as a special case. */
1763              if ($_var_name == 'smarty') {
1764                  /*
1765                   * If the reference could be compiled, use the compiled output;
1766                   * otherwise, fall back on the $smarty variable generated at
1767                   * run-time.
1768                   */
1769                  if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
1770                      $_output = $smarty_ref;
1771                  } else {
1772                      $_var_name = substr(array_shift($_indexes), 1);
1773                      $_output = "\$this->_smarty_vars['$_var_name']";
1774                  }
1775              } elseif(is_numeric($_var_name) && is_numeric(substr($var_expr, 0, 1))) {
1776                  // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
1777                  if(count($_indexes) > 0)
1778                  {
1779                      $_var_name .= implode("", $_indexes);
1780                      $_indexes = array();
1781                  }
1782                  $_output = $_var_name;
1783              } else {
1784                  $_output = "\$this->_tpl_vars['$_var_name']";
1785              }
1786  
1787              foreach ($_indexes as $_index) {
1788                  if (substr($_index, 0, 1) == '[') {
1789                      $_index = substr($_index, 1, -1);
1790                      if (is_numeric($_index)) {
1791                          $_output .= "[$_index]";
1792                      } elseif (substr($_index, 0, 1) == '$') {
1793                          if (strpos($_index, '.') !== false) {
1794                              $_output .= '[' . $this->_parse_var($_index) . ']';
1795                          } else {
1796                              $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
1797                          }
1798                      } else {
1799                          $_var_parts = explode('.', $_index);
1800                          $_var_section = $_var_parts[0];
1801                          $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
1802                          $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
1803                      }
1804                  } else if (substr($_index, 0, 1) == '.') {
1805                      if (substr($_index, 1, 1) == '$')
1806                          $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
1807                      else
1808                          $_output .= "['" . substr($_index, 1) . "']";
1809                  } else if (substr($_index,0,2) == '->') {
1810                      if(substr($_index,2,2) == '__') {
1811                          $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1812                      } elseif($this->security && substr($_index, 2, 1) == '_') {
1813                          $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1814                      } elseif (substr($_index, 2, 1) == '$') {
1815                          if ($this->security) {
1816                              $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1817                          } else {
1818                              $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
1819                          }
1820                      } else {
1821                          $_output .= $_index;
1822                      }
1823                  } elseif (substr($_index, 0, 1) == '(') {
1824                      $_index = $this->_parse_parenth_args($_index);
1825                      $_output .= $_index;
1826                  } else {
1827                      $_output .= $_index;
1828                  }
1829              }
1830          }
1831  
1832          return $_output;
1833      }
1834  
1835      /**
1836       * parse arguments in function call parenthesis
1837       *
1838       * @param string $parenth_args
1839       * @return string
1840       */
1841      function _parse_parenth_args($parenth_args)
1842      {
1843          preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match);
1844          $orig_vals = $match = $match[0];
1845          $this->_parse_vars_props($match);
1846          $replace = array();
1847          for ($i = 0, $count = count($match); $i < $count; $i++) {
1848              $replace[$orig_vals[$i]] = $match[$i];
1849          }
1850          return strtr($parenth_args, $replace);
1851      }
1852  
1853      /**
1854       * parse configuration variable expression into PHP code
1855       *
1856       * @param string $conf_var_expr
1857       */
1858      function _parse_conf_var($conf_var_expr)
1859      {
1860          $parts = explode('|', $conf_var_expr, 2);
1861          $var_ref = $parts[0];
1862          $modifiers = isset($parts[1]) ? $parts[1] : '';
1863  
1864          $var_name = substr($var_ref, 1, -1);
1865  
1866          $output = "\$this->_config[0]['vars']['$var_name']";
1867  
1868          $this->_parse_modifiers($output, $modifiers);
1869  
1870          return $output;
1871      }
1872  
1873      /**
1874       * parse section property expression into PHP code
1875       *
1876       * @param string $section_prop_expr
1877       * @return string
1878       */
1879      function _parse_section_prop($section_prop_expr)
1880      {
1881          $parts = explode('|', $section_prop_expr, 2);
1882          $var_ref = $parts[0];
1883          $modifiers = isset($parts[1]) ? $parts[1] : '';
1884  
1885          preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1886          $section_name = $match[1];
1887          $prop_name = $match[2];
1888  
1889          $output = "\$this->_sections['$section_name']['$prop_name']";
1890  
1891          $this->_parse_modifiers($output, $modifiers);
1892  
1893          return $output;
1894      }
1895  
1896  
1897      /**
1898       * parse modifier chain into PHP code
1899       *
1900       * sets $output to parsed modified chain
1901       * @param string $output
1902       * @param string $modifier_string
1903       */
1904      function _parse_modifiers(&$output, $modifier_string)
1905      {
1906          preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
1907          list(, $_modifiers, $modifier_arg_strings) = $_match;
1908  
1909          for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
1910              $_modifier_name = $_modifiers[$_i];
1911  
1912              if($_modifier_name == 'smarty') {
1913                  // skip smarty modifier
1914                  continue;
1915              }
1916  
1917              preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
1918              $_modifier_args = $_match[1];
1919  
1920              if (substr($_modifier_name, 0, 1) == '@') {
1921                  $_map_array = false;
1922                  $_modifier_name = substr($_modifier_name, 1);
1923              } else {
1924                  $_map_array = true;
1925              }
1926  
1927              if (empty($this->_plugins['modifier'][$_modifier_name])
1928                  && !$this->_get_plugin_filepath('modifier', $_modifier_name)
1929                  && function_exists($_modifier_name)) {
1930                  if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
1931                      $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
1932                  } else {
1933                      $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name,  null, null, false);
1934                  }
1935              }
1936              $this->_add_plugin('modifier', $_modifier_name);
1937  
1938              $this->_parse_vars_props($_modifier_args);
1939  
1940              if($_modifier_name == 'default') {
1941                  // supress notifications of default modifier vars and args
1942                  if(substr($output, 0, 1) == '$') {
1943                      $output = '@' . $output;
1944                  }
1945                  if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') {
1946                      $_modifier_args[0] = '@' . $_modifier_args[0];
1947                  }
1948              }
1949              if (count($_modifier_args) > 0)
1950                  $_modifier_args = ', '.implode(', ', $_modifier_args);
1951              else
1952                  $_modifier_args = '';
1953  
1954              if ($_map_array) {
1955                  $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))";
1956  
1957              } else {
1958  
1959                  $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
1960  
1961              }
1962          }
1963      }
1964  
1965  
1966      /**
1967       * add plugin
1968       *
1969       * @param string $type
1970       * @param string $name
1971       * @param boolean? $delayed_loading
1972       */
1973      function _add_plugin($type, $name, $delayed_loading = null)
1974      {
1975          if (!isset($this->_plugin_info[$type])) {
1976              $this->_plugin_info[$type] = array();
1977          }
1978          if (!isset($this->_plugin_info[$type][$name])) {
1979              $this->_plugin_info[$type][$name] = array($this->_current_file,
1980                                                        $this->_current_line_no,
1981                                                        $delayed_loading);
1982          }
1983      }
1984  
1985  
1986      /**
1987       * Compiles references of type $smarty.foo
1988       *
1989       * @param string $indexes
1990       * @return string
1991       */
1992      function _compile_smarty_ref(&$indexes)
1993      {
1994          /* Extract the reference name. */
1995          $_ref = substr($indexes[0], 1);
1996          foreach($indexes as $_index_no=>$_index) {
1997              if (substr($_index, 0, 1) != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) {
1998                  $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
1999              }
2000          }
2001  
2002          switch ($_ref) {
2003              case 'now':
2004                  $compiled_ref = 'time()';
2005                  $_max_index = 1;
2006                  break;
2007  
2008              case 'foreach':
2009                  array_shift($indexes);
2010                  $_var = $this->_parse_var_props(substr($indexes[0], 1));
2011                  $_propname = substr($indexes[1], 1);
2012                  $_max_index = 1;
2013                  switch ($_propname) {
2014                      case 'index':
2015                          array_shift($indexes);
2016                          $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)";
2017                          break;
2018                          
2019                      case 'first':
2020                          array_shift($indexes);
2021                          $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)";
2022                          break;
2023  
2024                      case 'last':
2025                          array_shift($indexes);
2026                          $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])";
2027                          break;
2028                          
2029                      case 'show':
2030                          array_shift($indexes);
2031                          $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)";
2032                          break;
2033                          
2034                      default:
2035                          unset($_max_index);
2036                          $compiled_ref = "\$this->_foreach[$_var]";
2037                  }
2038                  break;
2039  
2040              case 'section':
2041                  array_shift($indexes);
2042                  $_var = $this->_parse_var_props(substr($indexes[0], 1));
2043                  $compiled_ref = "\$this->_sections[$_var]";
2044                  break;
2045  
2046              case 'get':
2047                  $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
2048                  break;
2049  
2050              case 'post':
2051                  $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
2052                  break;
2053  
2054              case 'cookies':
2055                  $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
2056                  break;
2057  
2058              case 'env':
2059                  $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
2060                  break;
2061  
2062              case 'server':
2063                  $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
2064                  break;
2065  
2066              case 'session':
2067                  $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
2068                  break;
2069  
2070              /*
2071               * These cases are handled either at run-time or elsewhere in the
2072               * compiler.
2073               */
2074              case 'request':
2075                  if ($this->request_use_auto_globals) {
2076                      $compiled_ref = '$_REQUEST';
2077                      break;
2078                  } else {
2079                      $this->_init_smarty_vars = true;
2080                  }
2081                  return null;
2082  
2083              case 'capture':
2084                  return null;
2085  
2086              case 'template':
2087                  $compiled_ref = "'$this->_current_file'";
2088                  $_max_index = 1;
2089                  break;
2090  
2091              case 'version':
2092                  $compiled_ref = "'$this->_version'";
2093                  $_max_index = 1;
2094                  break;
2095  
2096              case 'const':
2097                  if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
2098                      $this->_syntax_error("(secure mode) constants not permitted",
2099                                           E_USER_WARNING, __FILE__, __LINE__);
2100                      return;
2101                  }
2102                  array_shift($indexes);
2103                  if (preg_match('!^\.\w+$!', $indexes[0])) {
2104                      $compiled_ref = '@' . substr($indexes[0], 1);
2105                  } else {
2106                      $_val = $this->_parse_var_props(substr($indexes[0], 1));
2107                      $compiled_ref = '@constant(' . $_val . ')';
2108                  }
2109                  $_max_index = 1;
2110                  break;
2111  
2112              case 'config':
2113                  $compiled_ref = "\$this->_config[0]['vars']";
2114                  $_max_index = 3;
2115                  break;
2116  
2117              case 'ldelim':
2118                  $compiled_ref = "'$this->left_delimiter'";
2119                  break;
2120  
2121              case 'rdelim':
2122                  $compiled_ref = "'$this->right_delimiter'";
2123                  break;
2124                  
2125              default:
2126                  $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
2127                  break;
2128          }
2129  
2130          if (isset($_max_index) && count($indexes) > $_max_index) {
2131              $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2132          }
2133  
2134          array_shift($indexes);
2135          return $compiled_ref;
2136      }
2137  
2138      /**
2139       * compiles call to plugin of type $type with name $name
2140       * returns a string containing the function-name or method call
2141       * without the paramter-list that would have follow to make the
2142       * call valid php-syntax
2143       *
2144       * @param string $type
2145       * @param string $name
2146       * @return string
2147       */
2148      function _compile_plugin_call($type, $name) {
2149          if (isset($this->_plugins[$type][$name])) {
2150              /* plugin loaded */
2151              if (is_array($this->_plugins[$type][$name][0])) {
2152                  return ((is_object($this->_plugins[$type][$name][0][0])) ?
2153                          "\$this->_plugins['$type']['$name'][0][0]->"    /* method callback */
2154                          : (string)($this->_plugins[$type][$name][0][0]).'::'    /* class callback */
2155                         ). $this->_plugins[$type][$name][0][1];
2156  
2157              } else {
2158                  /* function callback */
2159                  return $this->_plugins[$type][$name][0];
2160  
2161              }
2162          } else {
2163              /* plugin not loaded -> auto-loadable-plugin */
2164              return 'smarty_'.$type.'_'.$name;
2165  
2166          }
2167      }
2168  
2169      /**
2170       * load pre- and post-filters
2171       */
2172      function _load_filters()
2173      {
2174          if (count($this->_plugins['prefilter']) > 0) {
2175              foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
2176                  if ($prefilter === false) {
2177                      unset($this->_plugins['prefilter'][$filter_name]);
2178                      $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
2179                      require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2180                      smarty_core_load_plugins($_params, $this);
2181                  }
2182              }
2183          }
2184          if (count($this->_plugins['postfilter']) > 0) {
2185              foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
2186                  if ($postfilter === false) {
2187                      unset($this->_plugins['postfilter'][$filter_name]);
2188                      $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
2189                      require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2190                      smarty_core_load_plugins($_params, $this);
2191                  }
2192              }
2193          }
2194      }
2195  
2196  
2197      /**
2198       * Quote subpattern references
2199       *
2200       * @param string $string
2201       * @return string
2202       */
2203      function _quote_replace($string)
2204      {
2205          return strtr($string, array('\\' => '\\\\', '$' => '\\$'));
2206      }
2207  
2208      /**
2209       * display Smarty syntax error
2210       *
2211       * @param string $error_msg
2212       * @param integer $error_type
2213       * @param string $file
2214       * @param integer $line
2215       */
2216      function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
2217      {
2218          $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
2219      }
2220  
2221  
2222      /**
2223       * check if the compilation changes from cacheable to
2224       * non-cacheable state with the beginning of the current
2225       * plugin. return php-code to reflect the transition.
2226       * @return string
2227       */
2228      function _push_cacheable_state($type, $name) {
2229          $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2230          if ($_cacheable
2231              || 0<$this->_cacheable_state++) return '';
2232          if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
2233          $_ret = 'if ($this->caching && !$this->_cache_including): echo \'{nocache:'
2234              . $this->_cache_serial . '#' . $this->_nocache_count
2235              . '}\'; endif;';
2236          return $_ret;
2237      }
2238  
2239  
2240      /**
2241       * check if the compilation changes from non-cacheable to
2242       * cacheable state with the end of the current plugin return
2243       * php-code to reflect the transition.
2244       * @return string
2245       */
2246      function _pop_cacheable_state($type, $name) {
2247          $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2248          if ($_cacheable
2249              || --$this->_cacheable_state>0) return '';
2250          return 'if ($this->caching && !$this->_cache_including): echo \'{/nocache:'
2251              . $this->_cache_serial . '#' . ($this->_nocache_count++)
2252              . '}\'; endif;';
2253      }
2254  
2255  
2256      /**
2257       * push opening tag-name, file-name and line-number on the tag-stack
2258       * @param string the opening tag's name
2259       */
2260      function _push_tag($open_tag)
2261      {
2262          array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
2263      }
2264  
2265      /**
2266       * pop closing tag-name
2267       * raise an error if this stack-top doesn't match with the closing tag
2268       * @param string the closing tag's name
2269       * @return string the opening tag's name
2270       */
2271      function _pop_tag($close_tag)
2272      {
2273          $message = '';
2274          if (count($this->_tag_stack)>0) {
2275              list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
2276              if ($close_tag == $_open_tag) {
2277                  return $_open_tag;
2278              }
2279              if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
2280                  return $this->_pop_tag($close_tag);
2281              }
2282              if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
2283                  $this->_pop_tag($close_tag);
2284                  return $_open_tag;
2285              }
2286              if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
2287                  $this->_pop_tag($close_tag);
2288                  return $_open_tag;
2289              }
2290              if ($_open_tag == 'else' || $_open_tag == 'elseif') {
2291                  $_open_tag = 'if';
2292              } elseif ($_open_tag == 'sectionelse') {
2293                  $_open_tag = 'section';
2294              } elseif ($_open_tag == 'foreachelse') {
2295                  $_open_tag = 'foreach';
2296              }
2297              $message = " expected {/$_open_tag} (opened line $_line_no).";
2298          }
2299          $this->_syntax_error("mismatched tag {/$close_tag}.$message",
2300                               E_USER_ERROR, __FILE__, __LINE__);
2301      }
2302  
2303  }
2304  
2305  /**
2306   * compare to values by their string length
2307   *
2308   * @access private
2309   * @param string $a
2310   * @param string $b
2311   * @return 0|-1|1
2312   */
2313  function _smarty_sort_length($a, $b)
2314  {
2315      if($a == $b)
2316          return 0;
2317  
2318      if(strlen($a) == strlen($b))
2319          return ($a > $b) ? -1 : 1;
2320  
2321      return (strlen($a) > strlen($b)) ? -1 : 1;
2322  }
2323  
2324  
2325  /* vim: set et: */
2326  
2327  ?>


Généré le : Sun Nov 25 11:44:32 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics