[ Index ]

krapohl.info

title

Body

[close]

/pnincludes/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   * Author:      Monte Ohrt <monte@ispi.net>
   7   *              Andrei Zmievski <andrei@php.net>
   8   *
   9   * Version:     2.3.1
  10   * Copyright:   2001,2002 ispi of Lincoln, Inc.
  11   *
  12   * This library is free software; you can redistribute it and/or
  13   * modify it under the terms of the GNU Lesser General Public
  14   * License as published by the Free Software Foundation; either
  15   * version 2.1 of the License, or (at your option) any later version.
  16   *
  17   * This library is distributed in the hope that it will be useful,
  18   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  20   * Lesser General Public License for more details.
  21   *
  22   * You should have received a copy of the GNU Lesser General Public
  23   * License along with this library; if not, write to the Free Software
  24   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  25   *
  26   * You may contact the authors of Smarty by e-mail at:
  27   * monte@ispi.net
  28   * andrei@php.net
  29   *
  30   * Or, write to:
  31   * Monte Ohrt
  32   * Director of Technology, ispi
  33   * 237 S. 70th suite 220
  34   * Lincoln, NE 68510
  35   *
  36   * The latest version of Smarty can be obtained from:
  37   * http://www.phpinsider.com/
  38   *
  39   */
  40  
  41  class Smarty_Compiler extends Smarty {
  42  
  43      // internal vars
  44      var $_sectionelse_stack     =   array();    // keeps track of whether section had 'else' part
  45      var $_foreachelse_stack     =   array();    // keeps track of whether foreach had 'else' part
  46      var $_literal_blocks        =   array();    // keeps literal template blocks
  47      var $_php_blocks            =   array();    // keeps php code blocks
  48      var $_current_file          =   null;       // the current template being compiled
  49      var $_current_line_no       =   1;          // line number for error messages
  50      var $_capture_stack         =   array();    // keeps track of nested capture buffers
  51      var $_plugin_info           =   array();    // keeps track of plugins to load
  52      var $_init_smarty_vars      =   false;
  53  
  54  
  55  /*======================================================================*\
  56      Function:   _compile_file()
  57      Input:      compile a template file
  58  \*======================================================================*/
  59      function _compile_file($tpl_file, $template_source, &$template_compiled)
  60      {
  61          if ($this->security) {
  62              // do not allow php syntax to be executed unless specified
  63              if ($this->php_handling == SMARTY_PHP_ALLOW &&
  64                  !$this->security_settings['PHP_HANDLING']) {
  65                  $this->php_handling = SMARTY_PHP_PASSTHRU;
  66              }
  67          }
  68  
  69          $this->_load_filters();
  70  
  71          $this->_current_file = $tpl_file;
  72          $this->_current_line_no = 1;
  73          $ldq = preg_quote($this->left_delimiter, '!');
  74          $rdq = preg_quote($this->right_delimiter, '!');
  75  
  76          // run template source through prefilter functions
  77          if (count($this->_plugins['prefilter']) > 0) {
  78              foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
  79                  if ($prefilter === false) continue;
  80                  if ($prefilter[3] || function_exists($prefilter[0])) {
  81                      $template_source = $prefilter[0]($template_source, $this);
  82                      $this->_plugins['prefilter'][$filter_name][3] = true;
  83                  } else {
  84                      $this->_trigger_plugin_error("Smarty plugin error: prefilter '$filter_name' is not implemented");
  85                  }
  86              }
  87          }
  88  
  89          /* Annihilate the comments. */
  90          $template_source = preg_replace("!({$ldq})\*(.*?)\*({$rdq})!se",
  91                                          "'\\1*'.str_repeat(\"\n\", substr_count('\\2', \"\n\")) .'*\\3'",
  92                                          $template_source);
  93  
  94          /* Pull out the literal blocks. */
  95          preg_match_all("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s", $template_source, $match);
  96          $this->_literal_blocks = $match[1];
  97          $template_source = preg_replace("!{$ldq}literal{$rdq}(.*?){$ldq}/literal{$rdq}!s",
  98                                          $this->quote_replace($this->left_delimiter.'literal'.$this->right_delimiter), $template_source);
  99  
 100          /* Pull out the php code blocks. */
 101          preg_match_all("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s", $template_source, $match);
 102          $this->_php_blocks = $match[1];
 103          $template_source = preg_replace("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s",
 104                                          $this->quote_replace($this->left_delimiter.'php'.$this->right_delimiter), $template_source);
 105  
 106          /* Gather all template tags. */
 107          preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $template_source, $match);
 108          $template_tags = $match[1];
 109          /* Split content by template tags to obtain non-template content. */
 110          $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $template_source);
 111  
 112          /* loop through text blocks */
 113          for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
 114              /* match anything within <? ?> */
 115              if (preg_match_all('!(<\?[^?]*?\?>|<script\s+language\s*=\s*[\"\']?php[\"\']?\s*>)!is', $text_blocks[$curr_tb], $sp_match)) {
 116                  /* found at least one match, loop through each one */
 117                  for ($curr_sp = 0, $for_max2 = count($sp_match[0]); $curr_sp < $for_max2; $curr_sp++) {
 118                      if (preg_match('!^(<\?(php\s|\s|=\s)|<script\s*language\s*=\s*[\"\']?php[\"\']?\s*>)!is', $sp_match[0][$curr_sp])) {
 119                          /* php tag */
 120                          if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
 121                              /* echo php contents */
 122                              $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '<?php echo \''.str_replace("'", "\'", $sp_match[0][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
 123                         } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
 124                              /* quote php tags */
 125                              $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], htmlspecialchars($sp_match[0][$curr_sp]), $text_blocks[$curr_tb]);
 126                          } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
 127                              /* remove php tags */
 128                              if (substr($sp_match[0][$curr_sp], 0, 2) == '<?')
 129                                  $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '', $text_blocks[$curr_tb]);
 130                              else
 131                                  /* attempt to remove everything between <script ...> and </script> */
 132                                  $text_blocks[$curr_tb] = preg_replace('!'.preg_quote($sp_match[0][$curr_sp], '!').'.*?</script\s*>!is', '', $text_blocks[$curr_tb]);
 133                          }
 134                      } else
 135                          /* echo the non-php tags */
 136                          $text_blocks[$curr_tb] = str_replace($sp_match[0][$curr_sp], '<?php echo \''.str_replace("'", "\'", $sp_match[0][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
 137                  }
 138              }
 139          }
 140  
 141          /* Compile the template tags into PHP code. */
 142          $compiled_tags = array();
 143          for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
 144              $this->_current_line_no += substr_count($text_blocks[$i], "\n");
 145              $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
 146              $this->_current_line_no += substr_count($template_tags[$i], "\n");
 147          }
 148  
 149          $template_compiled = '';
 150  
 151          /* Interleave the compiled contents and text blocks to get the final result. */
 152          for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
 153              $template_compiled .= $text_blocks[$i].$compiled_tags[$i];
 154          }
 155          $template_compiled .= $text_blocks[$i];
 156  
 157          /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
 158          if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $template_compiled, $match)) {
 159              $strip_tags = $match[0];
 160              $strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags);
 161              $strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified);
 162              for ($i = 0, $for_max = count($strip_tags); $i < $for_max; $i++)
 163                  $template_compiled = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s",
 164                                                    $this->quote_replace($strip_tags_modified[$i]),
 165                                                    $template_compiled, 1);
 166          }
 167  
 168          // remove \n from the end of the file, if any
 169          if ($template_compiled{strlen($template_compiled) - 1} == "\n" ) {
 170              $template_compiled = substr($template_compiled, 0, -1);
 171          }
 172  
 173          // run compiled template through postfilter functions
 174          if (count($this->_plugins['postfilter']) > 0) {
 175              foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
 176                  if ($postfilter === false) continue;
 177                  if ($postfilter[3] || function_exists($postfilter[0])) {
 178                      $template_compiled = $postfilter[0]($template_compiled, $this);
 179                      $this->_plugins['postfilter'][$filter_name][3] = true;
 180                  } else {
 181                      $this->_trigger_plugin_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
 182                  }
 183              }
 184          }
 185  
 186          // put header at the top of the compiled template
 187          $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
 188          $template_header .= "         compiled from ".$tpl_file." */ ?>\n";
 189  
 190          /* Emit code to load needed plugins. */
 191          if (count($this->_plugin_info)) {
 192              $plugins_code = '<?php $this->_load_plugins(array(';
 193              foreach ($this->_plugin_info as $plugin_type => $plugins) {
 194                  foreach ($plugins as $plugin_name => $plugin_info) {
 195                      $plugins_code .= "\narray('$plugin_type', '$plugin_name', '$plugin_info[0]', $plugin_info[1], ";
 196                      $plugins_code .= $plugin_info[2] ? 'true),' : 'false),';
 197                  }
 198              }
 199              $plugins_code .= ")); ?>";
 200              $template_header .= $plugins_code;
 201              $this->_plugin_info = array();
 202          }
 203  
 204          if ($this->_init_smarty_vars) {
 205              $template_header .= "<?php \$this->_assign_smarty_interface(); ?>\n";
 206              $this->_init_smarty_vars = false;
 207          }
 208  
 209          $template_compiled = $template_header . $template_compiled;
 210  
 211          return true;
 212      }
 213  
 214  
 215  /*======================================================================*\
 216      Function: _compile_tag
 217      Purpose:  Compile a template tag
 218  \*======================================================================*/
 219      function _compile_tag($template_tag)
 220      {
 221          /* Matched comment. */
 222          if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
 223              return '';
 224  
 225          $qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
 226  
 227          /* Split tag into two parts: command and the arguments. */
 228          preg_match('/^(
 229                         (?: ' . $qstr_regexp . ' | (?>[^"\'\s]+))+
 230                        )
 231                        (?:\s+(.*))?
 232                      /xs', $template_tag, $match);
 233          $tag_command = $match[1];
 234          $tag_args = isset($match[2]) ? $match[2] : '';
 235  
 236          /* If the tag name matches a variable or section property definition,
 237             we simply process it. */
 238          if (preg_match('!^\$\w+(?>(\[(\d+|\$\w+|\w+(\.\w+)?)\])|((\.|->)\$?\w+))*(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command) ||   // if a variable
 239              preg_match('!^#(\w+)#(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command)     ||  // or a configuration variable
 240              preg_match('!^%\w+\.\w+%(?>\|@?\w+(:(?>' . $qstr_regexp . '|[^|]+))*)*$!', $tag_command)) {    // or a section property
 241              settype($tag_command, 'array');
 242              $this->_parse_vars_props($tag_command);
 243              return "<?php echo $tag_command[0]; ?>\n";
 244          }
 245  
 246          switch ($tag_command) {
 247              case 'include':
 248                  return $this->_compile_include_tag($tag_args);
 249  
 250              case 'include_php':
 251                  return $this->_compile_include_php_tag($tag_args);
 252  
 253              case 'if':
 254                  return $this->_compile_if_tag($tag_args);
 255  
 256              case 'else':
 257                  return '<?php else: ?>';
 258  
 259              case 'elseif':
 260                  return $this->_compile_if_tag($tag_args, true);
 261  
 262              case '/if':
 263                  return '<?php endif; ?>';
 264  
 265              case 'capture':
 266                  return $this->_compile_capture_tag(true, $tag_args);
 267  
 268              case '/capture':
 269                  return $this->_compile_capture_tag(false);
 270  
 271              case 'ldelim':
 272                  return $this->left_delimiter;
 273  
 274              case 'rdelim':
 275                  return $this->right_delimiter;
 276  
 277              case 'section':
 278                  array_push($this->_sectionelse_stack, false);
 279                  return $this->_compile_section_start($tag_args);
 280  
 281              case 'sectionelse':
 282                  $this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;
 283                  return "<?php endfor; else: ?>";
 284  
 285              case '/section':
 286                  if (array_pop($this->_sectionelse_stack))
 287                      return "<?php endif; ?>";
 288                  else
 289                      return "<?php endfor; endif; ?>";
 290  
 291              case 'foreach':
 292                  array_push($this->_foreachelse_stack, false);
 293                  return $this->_compile_foreach_start($tag_args);
 294                  break;
 295  
 296              case 'foreachelse':
 297                  $this->_foreachelse_stack[count($this->_foreachelse_stack)-1] = true;
 298                  return "<?php endforeach; else: ?>";
 299  
 300              case '/foreach':
 301                  if (array_pop($this->_foreachelse_stack))
 302                      return "<?php endif; ?>";
 303                  else
 304                      return "<?php endforeach; endif; ?>";
 305  
 306              case 'config_load':
 307                  return $this->_compile_config_load_tag($tag_args);
 308  
 309              case 'strip':
 310              case '/strip':
 311                  return $this->left_delimiter.$tag_command.$this->right_delimiter;
 312  
 313              case 'literal':
 314                  list (,$literal_block) = each($this->_literal_blocks);
 315                  $this->_current_line_no += substr_count($literal_block, "\n");
 316                  return "<?php echo '".str_replace("'", "\'", str_replace("\\", "\\\\", $literal_block))."'; ?>\n";
 317  
 318              case 'php':
 319                  if ($this->security && !$this->security_settings['PHP_TAGS']) {
 320                      $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING);
 321                      return;
 322                  }
 323                  list (,$php_block) = each($this->_php_blocks);
 324                  $this->_current_line_no += substr_count($php_block, "\n");
 325                  return '<?php '.$php_block.' ?>';
 326  
 327              case 'insert':
 328                  return $this->_compile_insert_tag($tag_args);
 329  
 330              default:
 331                  if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
 332                      return $output;
 333                  } else if ($this->_compile_block_tag($tag_command, $tag_args, $output)) {
 334                      return $output;
 335                  } else {
 336                      return $this->_compile_custom_tag($tag_command, $tag_args);
 337                  }
 338          }
 339      }
 340  
 341  
 342  /*======================================================================*\
 343      Function: _compile_compiler_tag
 344      Purpose:  compile the custom compiler tag
 345  \*======================================================================*/
 346      function _compile_compiler_tag($tag_command, $tag_args, &$output)
 347      {
 348          $found = false;
 349          $have_function = true;
 350  
 351          /*
 352           * First we check if the compiler function has already been registered
 353           * or loaded from a plugin file.
 354           */
 355          if (isset($this->_plugins['compiler'][$tag_command])) {
 356              $found = true;
 357              $plugin_func = $this->_plugins['compiler'][$tag_command][0];
 358              if (!function_exists($plugin_func)) {
 359                  $message = "compiler function '$tag_command' is not implemented";
 360                  $have_function = false;
 361              }
 362          }
 363          /*
 364           * Otherwise we need to load plugin file and look for the function
 365           * inside it.
 366           */
 367          else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
 368              $found = true;
 369  
 370              include_once $plugin_file;
 371  
 372              $plugin_func = 'smarty_compiler_' . $tag_command;
 373              if (!function_exists($plugin_func)) {
 374                  $message = "plugin function $plugin_func() not found in $plugin_file\n";
 375                  $have_function = false;
 376              } else {
 377                  $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null);
 378              }
 379          }
 380  
 381          /*
 382           * True return value means that we either found a plugin or a
 383           * dynamically registered function. False means that we didn't and the
 384           * compiler should now emit code to load custom function plugin for this
 385           * tag.
 386           */
 387          if ($found) {
 388              if ($have_function) {
 389                  $output = '<?php ' . $plugin_func($tag_args, $this) . ' ?>';
 390              } else {
 391                  $this->_syntax_error($message, E_USER_WARNING);
 392              }
 393              return true;
 394          } else {
 395              return false;
 396          }
 397      }
 398  
 399  
 400  /*======================================================================*\
 401      Function: _compile_block_tag
 402      Purpose:  compile block function tag
 403  \*======================================================================*/
 404      function _compile_block_tag($tag_command, $tag_args, &$output)
 405      {
 406          if ($tag_command{0} == '/') {
 407              $start_tag = false;
 408              $tag_command = substr($tag_command, 1);
 409          } else
 410              $start_tag = true;
 411  
 412          $found = false;
 413          $have_function = true;
 414  
 415          /*
 416           * First we check if the block function has already been registered
 417           * or loaded from a plugin file.
 418           */
 419          if (isset($this->_plugins['block'][$tag_command])) {
 420              $found = true;
 421              $plugin_func = $this->_plugins['block'][$tag_command][0];
 422              if (!function_exists($plugin_func)) {
 423                  $message = "block function '