[ Index ]

XOOPS v2.2 Reference

title

Body

[close]

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

   1  <?php
   2  
   3  if (!defined('SMARTY_DIR')) {
   4      exit();
   5  }
   6  /**
   7   * Project:     Smarty: the PHP compiling template engine
   8   * File:        Smarty_Compiler.class.php
   9   *
  10   * This library is free software; you can redistribute it and/or
  11   * modify it under the terms of the GNU Lesser General Public
  12   * License as published by the Free Software Foundation; either
  13   * version 2.1 of the License, or (at your option) any later version.
  14   *
  15   * This library is distributed in the hope that it will be useful,
  16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18   * Lesser General Public License for more details.
  19   *
  20   * You should have received a copy of the GNU Lesser General Public
  21   * License along with this library; if not, write to the Free Software
  22   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  23   *
  24   * @link http://smarty.php.net/
  25   * @author Monte Ohrt <monte@ispi.net>
  26   * @author Andrei Zmievski <andrei@php.net>
  27   * @version 2.6.5-dev
  28   * @copyright 2001-2004 ispi of Lincoln, Inc.
  29   * @package Smarty
  30   */
  31  
  32  /* $Id: Smarty_Compiler.class.php,v 1.3.6.1 2005/08/14 19:17:50 skalpa Exp $ */
  33  
  34  /**
  35   * Template compiling class
  36   * @package Smarty
  37   */
  38  class Smarty_Compiler extends Smarty {
  39  
  40      // internal vars
  41      /**#@+
  42       * @access private
  43       */
  44      var $_folded_blocks         =   array();    // keeps folded template blocks
  45      var $_current_file          =   null;       // the current template being compiled
  46      var $_current_line_no       =   1;          // line number for error messages
  47      var $_capture_stack         =   array();    // keeps track of nested capture buffers
  48      var $_plugin_info           =   array();    // keeps track of plugins to load
  49      var $_init_smarty_vars      =   false;
  50      var $_permitted_tokens      =   array('true','false','yes','no','on','off','null');
  51      var $_db_qstr_regexp        =   null;        // regexps are setup in the constructor
  52      var $_si_qstr_regexp        =   null;
  53      var $_qstr_regexp           =   null;
  54      var $_func_regexp           =   null;
  55      var $_var_bracket_regexp    =   null;
  56      var $_dvar_guts_regexp      =   null;
  57      var $_dvar_regexp           =   null;
  58      var $_cvar_regexp           =   null;
  59      var $_svar_regexp           =   null;
  60      var $_avar_regexp           =   null;
  61      var $_mod_regexp            =   null;
  62      var $_var_regexp            =   null;
  63      var $_parenth_param_regexp  =   null;
  64      var $_func_call_regexp      =   null;
  65      var $_obj_ext_regexp        =   null;
  66      var $_obj_start_regexp      =   null;
  67      var $_obj_params_regexp     =   null;
  68      var $_obj_call_regexp       =   null;
  69      var $_cacheable_state       =   0;
  70      var $_cache_attrs_count     =   0;
  71      var $_nocache_count         =   0;
  72      var $_cache_serial          =   null;
  73      var $_cache_include         =   null;
  74  
  75      var $_strip_depth           =   0;
  76      var $_additional_newline    =   "\n";
  77  
  78      /**#@-*/
  79      /**
  80       * The class constructor.
  81       */
  82      function Smarty_Compiler()
  83      {
  84          // matches double quoted strings:
  85          // "foobar"
  86          // "foo\"bar"
  87          $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
  88  
  89          // matches single quoted strings:
  90          // 'foobar'
  91          // 'foo\'bar'
  92          $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
  93  
  94          // matches single or double quoted strings
  95          $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
  96  
  97          // matches bracket portion of vars
  98          // [0]
  99          // [foo]
 100          // [$bar]
 101          $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
 102  
 103          // matches numerical constants
 104          // 30
 105          // -12
 106          // 13.22
 107          $this->_num_const_regexp = '\-?\d+(?:\.\d+)?';
 108  
 109          // matches $ vars (not objects):
 110          // $foo
 111          // $foo.bar
 112          // $foo.bar.foobar
 113          // $foo[0]
 114          // $foo[$bar]
 115          // $foo[5][blah]
 116          // $foo[5].bar[$foobar][4]
 117          $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))';
 118          $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
 119          $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
 120                  . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
 121          $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
 122  
 123          // matches config vars:
 124          // #foo#
 125          // #foobar123_foo#
 126          $this->_cvar_regexp = '\#\w+\#';
 127  
 128          // matches section vars:
 129          // %foo.bar%
 130          $this->_svar_regexp = '\%\w+\.\w+\%';
 131  
 132          // matches all valid variables (no quotes, no modifiers)
 133          $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
 134             . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
 135  
 136          // matches valid variable syntax:
 137          // $foo
 138          // $foo
 139          // #foo#
 140          // #foo#
 141          // "text"
 142          // "text"
 143          $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_num_const_regexp . '|' . $this->_qstr_regexp . ')';
 144  
 145          // matches valid object call (one level of object nesting allowed in parameters):
 146          // $foo->bar
 147          // $foo->bar()
 148          // $foo->bar("text")
 149          // $foo->bar($foo, $bar, "text")
 150          // $foo->bar($foo, "foo")
 151          // $foo->bar->foo()
 152          // $foo->bar->foo->bar()
 153          // $foo->bar($foo->bar)
 154          // $foo->bar($foo->bar())
 155          // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
 156          $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
 157          $this->_obj_restricted_param_regexp = '(?:'
 158                  . $this->_var_regexp . '(?:' . $this->_obj_ext_regexp . '(?:\((?:' . $this->_var_regexp
 159                  . '(?:\s*,\s*' . $this->_var_regexp . ')*)?\))?)*)';
 160          $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
 161                  . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
 162          $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
 163                  . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
 164          $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
 165          $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 . ')*)?)';
 166          
 167          // matches valid modifier syntax:
 168          // |foo
 169          // |@foo
 170          // |foo:"bar"
 171          // |foo:$bar
 172          // |foo:"bar":$foobar
 173          // |foo|bar
 174          // |foo:$foo->bar
 175          $this->_mod_regexp = '(?:\|@?\w+(?::(?>-?\w+|'
 176             . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
 177  
 178          // matches valid function name:
 179          // foo123
 180          // _foo_bar
 181          $this->_func_regexp = '[a-zA-Z_]\w*';
 182  
 183          // matches valid registered object:
 184          // foo->bar
 185          $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
 186  
 187          // matches valid parameter values:
 188          // true
 189          // $foo
 190          // $foo|bar
 191          // #foo#
 192          // #foo#|bar
 193          // "text"
 194          // "text"|bar
 195          // $foo->bar
 196          $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
 197             . $this->_var_regexp  . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
 198  
 199          // matches valid parenthesised function parameters:
 200          //
 201          // "text"
 202          //    $foo, $bar, "text"
 203          // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
 204          $this->_parenth_param_regexp = '(?:\((?:\w+|'
 205                  . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
 206                  . $this->_param_regexp . ')))*)?\))';
 207  
 208          // matches valid function call:
 209          // foo()
 210          // foo_bar($foo)
 211          // _foo_bar($foo,"bar")
 212          // foo123($foo,$foo->bar(),"foo")
 213          $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
 214             . $this->_parenth_param_regexp . '))';
 215      }
 216  
 217      /**
 218       * compile a resource
 219       *
 220       * sets $compiled_content to the compiled source
 221       * @param string $resource_name
 222       * @param string $source_content
 223       * @param string $compiled_content
 224       * @return true
 225       */
 226      function _compile_file($resource_name, $source_content, &$compiled_content)
 227      {
 228  
 229          if ($this->security) {
 230              // do not allow php syntax to be executed unless specified
 231              if ($this->php_handling == SMARTY_PHP_ALLOW &&
 232                  !$this->security_settings['PHP_HANDLING']) {
 233                  $this->php_handling = SMARTY_PHP_PASSTHRU;
 234              }
 235          }
 236  
 237          $this->_load_filters();
 238  
 239          $this->_current_file = $resource_name;
 240          $this->_current_line_no = 1;
 241          $ldq = preg_quote($this->left_delimiter, '~');
 242          $rdq = preg_quote($this->right_delimiter, '~');
 243  
 244          // run template source through prefilter functions
 245          if (count($this->_plugins['prefilter']) > 0) {
 246              foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
 247                  if ($prefilter === false) continue;
 248                  if ($prefilter[3] || is_callable($prefilter[0])) {
 249                      $source_content = call_user_func_array($prefilter[0],
 250                                                              array($source_content, &$this));
 251                      $this->_plugins['prefilter'][$filter_name][3] = true;
 252                  } else {
 253                      $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
 254                  }
 255              }
 256          }
 257  
 258          /* fetch all special blocks */
 259          $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
 260  
 261          preg_match_all($search, $source_content, $match,  PREG_SET_ORDER);
 262          $this->_folded_blocks = $match;
 263          reset($this->_folded_blocks);
 264  
 265          /* replace special blocks by "{php}" */
 266          $source_content = preg_replace($search.'e', "'"
 267                                         . $this->_quote_replace($this->left_delimiter) . 'php'
 268                                         . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
 269                                         . $this->_quote_replace($this->right_delimiter)
 270                                         . "'"
 271                                         , $source_content);
 272  
 273          /* Gather all template tags. */
 274          preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match);
 275          $template_tags = $_match[1];
 276          /* Split content by template tags to obtain non-template content. */
 277          $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);
 278  
 279          /* loop through text blocks */
 280          for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
 281              /* match anything resembling php tags */
 282              if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) {
 283                  /* replace tags with placeholders to prevent recursive replacements */
 284                  $sp_match[1] = array_unique($sp_match[1]);
 285                  usort($sp_match[1], '_smarty_sort_length');
 286                  for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
 287                      $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
 288                  }
 289                  /* process each one */
 290                  for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
 291                      if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
 292                          /* echo php contents */
 293                          $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
 294                      } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
 295                          /* quote php tags */
 296                          $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
 297                      } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
 298                          /* remove php tags */
 299                          $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
 300                      } else {
 301                          /* SMARTY_PHP_ALLOW, but echo non php starting tags */
 302                          $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
 303                          $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
 304                      }
 305                  }
 306              }
 307          }
 308  
 309          /* Compile the template tags into PHP code. */
 310          $compiled_tags = array();
 311          for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
 312              $this->_current_line_no += substr_count($text_blocks[$i], "\n");
 313              $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
 314              $this->_current_line_no += substr_count($template_tags[$i], "\n");
 315          }
 316          if (count($this->_tag_stack)>0) {
 317              list($_open_tag, $_line_no) = end($this->_tag_stack);
 318              $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
 319              return;
 320          }
 321  
 322          $compiled_content = '';
 323  
 324          /* Interleave the compiled contents and text blocks to get the final result. */
 325          for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
 326              if ($compiled_tags[$i] == '') {
 327                  // tag result empty, remove first newline from following text block
 328                  $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]);
 329              }
 330              $compiled_content .= $text_blocks[$i].$compiled_tags[$i];
 331          }
 332          $compiled_content .= $text_blocks[$i];
 333  
 334          /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
 335          if (preg_match_all("~{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}~s", $compiled_content, $_match)) {
 336              $strip_tags = $_match[0];
 337              $strip_tags_modified = preg_replace("~{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+~m", '', $strip_tags);
 338              $strip_tags_modified = preg_replace('~[\r\n]+~m', '', $strip_tags_modified);
 339              for ($i = 0, $for_max = count($strip_tags); $i < $for_max; $i++)
 340                  $compiled_content = preg_replace("~{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}~s",
 341                                                    $this->_quote_replace($strip_tags_modified[$i]),
 342                                                    $compiled_content, 1);
 343          }
 344  
 345          // remove \n from the end of the file, if any
 346          if (($_len=strlen($compiled_content)) && ($compiled_content{$_len - 1} == "\n" )) {
 347              $compiled_content = substr($compiled_content, 0, -1);
 348          }
 349  
 350          if (!empty($this->_cache_serial)) {
 351              $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
 352          }
 353  
 354          // remove unnecessary close/open tags
 355          $compiled_content = preg_replace('~\?>\n?<\?php~', '', $compiled_content);
 356  
 357          // run compiled template through postfilter functions
 358          if (count($this->_plugins['postfilter']) > 0) {
 359              foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
 360                  if ($postfilter === false) continue;
 361                  if ($postfilter[3] || is_callable($postfilter[0])) {
 362                      $compiled_content = call_user_func_array($postfilter[0],
 363                                                                array($compiled_content, &$this));
 364                      $this->_plugins['postfilter'][$filter_name][3] = true;
 365                  } else {
 366                      $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
 367                  }
 368              }
 369          }
 370  
 371          // put header at the top of the compiled template
 372          $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
 373          $template_header .= "         compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
 374  
 375          /* Emit code to load needed plugins. */
 376          $this->_plugins_code = '';
 377          if (count($this->_plugin_info)) {
 378              $_plugins_params = "array('plugins' => array(";
 379              foreach ($this->_plugin_info as $plugin_type => $plugins) {
 380                  foreach ($plugins as $plugin_name => $plugin_info) {
 381                      $_plugins_params .= "array('$plugin_type', '$plugin_name', '$plugin_info[0]', $plugin_info[1], ";
 382                      $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
 383                  }
 384              }
 385              $_plugins_params .= '))';
 386              $plugins_code = "<?php require_once (SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
 387              $template_header .= $plugins_code;
 388              $this->_plugin_info = array();
 389              $this->_plugins_code = $plugins_code;
 390          }
 391  
 392          if ($this->_init_smarty_vars) {
 393              $template_header .= "<?php require_once (SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
 394              $this->_init_smarty_vars = false;
 395          }
 396  
 397          $compiled_content = $template_header . $compiled_content;
 398          return true;