PageRenderTime 10ms CodeModel.GetById 46ms app.highlight 76ms RepoModel.GetById 1ms app.codeStats 1ms

/bblog/libs/Smarty_Compiler.class.php

https://github.com/escherlat/loquacity
PHP | 2137 lines | 1388 code | 277 blank | 472 comment | 303 complexity | 4befe532d7e3a51cd080e78b42c2b015 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2/**
   3 * ./bblog/libs/Smarty_Compiler.class.php
   4 *
   5 * @package default
   6 */
   7
   8
   9/**
  10 * Project:     Smarty: the PHP compiling template engine
  11 * File:        Smarty_Compiler.class.php
  12 *
  13 * This library is free software; you can redistribute it and/or
  14 * modify it under the terms of the GNU Lesser General Public
  15 * License as published by the Free Software Foundation; either
  16 * version 2.1 of the License, or (at your option) any later version.
  17 *
  18 * This library is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  21 * Lesser General Public License for more details.
  22 *
  23 * You should have received a copy of the GNU Lesser General Public
  24 * License along with this library; if not, write to the Free Software
  25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  26 *
  27 * You may contact the authors of Smarty by e-mail at:
  28 * monte@ispi.net
  29 * andrei@php.net
  30 *
  31 * Or, write to:
  32 * Monte Ohrt
  33 * Director of Technology, ispi
  34 * 237 S. 70th suite 220
  35 * Lincoln, NE 68510
  36 *
  37 * The latest version of Smarty can be obtained from:
  38 * http://smarty.php.net/
  39 *
  40 * @link http://smarty.php.net/
  41 * @author Monte Ohrt <monte@ispi.net>
  42 * @author Andrei Zmievski <andrei@php.net>
  43 * @version 2.6.0
  44 * @copyright 2001-2003 ispi of Lincoln, Inc.
  45 * @package Smarty
  46 */
  47/* $Id: Smarty_Compiler.class.php,v 1.4 2004/01/08 13:04:44 eadz Exp $ */
  48
  49/**
  50 * Template compiling class
  51 *
  52 * @package Smarty
  53 */
  54class Smarty_Compiler extends Smarty {
  55
  56	// internal vars
  57	/**#@+
  58     * @access private
  59     */
  60	var $_sectionelse_stack     =   array();    // keeps track of whether section had 'else' part
  61	var $_foreachelse_stack     =   array();    // keeps track of whether foreach had 'else' part
  62	var $_literal_blocks        =   array();    // keeps literal template blocks
  63	var $_php_blocks            =   array();    // keeps php code blocks
  64	var $_current_file          =   null;       // the current template being compiled
  65	var $_current_line_no       =   1;          // line number for error messages
  66	var $_capture_stack         =   array();    // keeps track of nested capture buffers
  67	var $_plugin_info           =   array();    // keeps track of plugins to load
  68	var $_init_smarty_vars      =   false;
  69	var $_permitted_tokens      =   array('true', 'false', 'yes', 'no', 'on', 'off', 'null');
  70	var $_db_qstr_regexp        =   null;        // regexps are setup in the constructor
  71	var $_si_qstr_regexp        =   null;
  72	var $_qstr_regexp           =   null;
  73	var $_func_regexp           =   null;
  74	var $_var_bracket_regexp    =   null;
  75	var $_dvar_guts_regexp      =   null;
  76	var $_dvar_regexp           =   null;
  77	var $_cvar_regexp           =   null;
  78	var $_svar_regexp           =   null;
  79	var $_avar_regexp           =   null;
  80	var $_mod_regexp            =   null;
  81	var $_var_regexp            =   null;
  82	var $_parenth_param_regexp  =   null;
  83	var $_func_call_regexp      =   null;
  84	var $_obj_ext_regexp        =   null;
  85	var $_obj_start_regexp      =   null;
  86	var $_obj_params_regexp     =   null;
  87	var $_obj_call_regexp       =   null;
  88	var $_cacheable_state       =   0;
  89	var $_cache_attrs_count     =   0;
  90	var $_nocache_count         =   0;
  91	var $_cache_serial          =   null;
  92	var $_cache_include         =   null;
  93
  94	var $_strip_depth           =   0;
  95	var $_additional_newline    =   "\n";
  96
  97	/**#@-*/
  98
  99
 100	/**
 101	 * The class constructor.
 102	 */
 103	function Smarty_Compiler() {
 104		// matches double quoted strings:
 105		// "foobar"
 106		// "foo\"bar"
 107		$this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
 108
 109		// matches single quoted strings:
 110		// 'foobar'
 111		// 'foo\'bar'
 112		$this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
 113
 114		// matches single or double quoted strings
 115		$this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
 116
 117		// matches bracket portion of vars
 118		// [0]
 119		// [foo]
 120		// [$bar]
 121		$this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
 122
 123		// matches $ vars (not objects):
 124		// $foo
 125		// $foo.bar
 126		// $foo.bar.foobar
 127		// $foo[0]
 128		// $foo[$bar]
 129		// $foo[5][blah]
 130		// $foo[5].bar[$foobar][4]
 131		$this->_dvar_math_regexp = '[\+\-\*\/\%]';
 132		$this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
 133		$this->_dvar_num_var_regexp = '\-?\d+(?:\.\d+)?' . $this->_dvar_math_var_regexp;
 134		$this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
 135			. ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:\-?\d+(?:\.\d+)?|' . $this->_dvar_math_var_regexp . ')*)?';
 136		$this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
 137
 138		// matches config vars:
 139		// #foo#
 140		// #foobar123_foo#
 141		$this->_cvar_regexp = '\#\w+\#';
 142
 143		// matches section vars:
 144		// %foo.bar%
 145		$this->_svar_regexp = '\%\w+\.\w+\%';
 146
 147		// matches all valid variables (no quotes, no modifiers)
 148		$this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
 149			. $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
 150
 151		// matches valid variable syntax:
 152		// $foo
 153		// $foo
 154		// #foo#
 155		// #foo#
 156		// "text"
 157		// "text"
 158		$this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
 159
 160		// matches valid object call (no objects allowed in parameters):
 161		// $foo->bar
 162		// $foo->bar()
 163		// $foo->bar("text")
 164		// $foo->bar($foo, $bar, "text")
 165		// $foo->bar($foo, "foo")
 166		// $foo->bar->foo()
 167		// $foo->bar->foo->bar()
 168		$this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
 169		$this->_obj_params_regexp = '\((?:\w+|'
 170			. $this->_var_regexp . '(?:\s*,\s*(?:(?:\w+|'
 171			. $this->_var_regexp . ')))*)?\)';
 172		$this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
 173		$this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?)';
 174
 175		// matches valid modifier syntax:
 176		// |foo
 177		// |@foo
 178		// |foo:"bar"
 179		// |foo:$bar
 180		// |foo:"bar":$foobar
 181		// |foo|bar
 182		// |foo:$foo->bar
 183		$this->_mod_regexp = '(?:\|@?\w+(?::(?>-?\w+|'
 184			. $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
 185
 186		// matches valid function name:
 187		// foo123
 188		// _foo_bar
 189		$this->_func_regexp = '[a-zA-Z_]\w*';
 190
 191		// matches valid registered object:
 192		// foo->bar
 193		$this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
 194
 195		// matches valid parameter values:
 196		// true
 197		// $foo
 198		// $foo|bar
 199		// #foo#
 200		// #foo#|bar
 201		// "text"
 202		// "text"|bar
 203		// $foo->bar
 204		$this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
 205			. $this->_var_regexp  . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
 206
 207		// matches valid parenthesised function parameters:
 208		//
 209		// "text"
 210		//    $foo, $bar, "text"
 211		// $foo|bar, "foo"|bar, $foo->bar($foo)|bar
 212		$this->_parenth_param_regexp = '(?:\((?:\w+|'
 213			. $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
 214			. $this->_param_regexp . ')))*)?\))';
 215
 216		// matches valid function call:
 217		// foo()
 218		// foo_bar($foo)
 219		// _foo_bar($foo,"bar")
 220		// foo123($foo,$foo->bar(),"foo")
 221		$this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
 222			. $this->_parenth_param_regexp . '))';
 223	}
 224
 225
 226	/**
 227	 * compile a resource
 228	 *
 229	 * sets $compiled_content to the compiled source
 230	 *
 231	 * @param string  $resource_name
 232	 * @param string  $source_content
 233	 * @param string  $compiled_content
 234	 * @return true
 235	 */
 236	function _compile_file($resource_name, $source_content, &$compiled_content) {
 237
 238		if ($this->security) {
 239			// do not allow php syntax to be executed unless specified
 240			if ($this->php_handling == SMARTY_PHP_ALLOW &&
 241				!$this->security_settings['PHP_HANDLING']) {
 242				$this->php_handling = SMARTY_PHP_PASSTHRU;
 243			}
 244		}
 245
 246		$this->_load_filters();
 247
 248		$this->_current_file = $resource_name;
 249		$this->_current_line_no = 1;
 250		$ldq = preg_quote($this->left_delimiter, '!');
 251		$rdq = preg_quote($this->right_delimiter, '!');
 252
 253		// run template source through prefilter functions
 254		if (count($this->_plugins['prefilter']) > 0) {
 255			foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
 256				if ($prefilter === false) continue;
 257				if ($prefilter[3] || is_callable($prefilter[0])) {
 258					$source_content = call_user_func_array($prefilter[0],
 259						array($source_content, &$this));
 260					$this->_plugins['prefilter'][$filter_name][3] = true;
 261				} else {
 262					$this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
 263				}
 264			}
 265		}
 266
 267		/* Annihilate the comments. */
 268		$source_content = preg_replace("!({$ldq})\*(.*?)\*({$rdq})!se",
 269			"'\\1*'.str_repeat(\"\n\", substr_count('\\2', \"\n\")) .'*\\3'",
 270			$source_content);
 271
 272		/* Pull out the literal blocks. */
 273		preg_match_all("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", $source_content, $_match);
 274		$this->_literal_blocks = $_match[1];
 275		$source_content = preg_replace("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s",
 276			$this->_quote_replace($this->left_delimiter.'literal'.$this->right_delimiter), $source_content);
 277
 278		/* Pull out the php code blocks. */
 279		preg_match_all("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s", $source_content, $_match);
 280		$this->_php_blocks = $_match[1];
 281		$source_content = preg_replace("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s",
 282			$this->_quote_replace($this->left_delimiter.'php'.$this->right_delimiter), $source_content);
 283
 284		/* Gather all template tags. */
 285		preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $source_content, $_match);
 286		$template_tags = $_match[1];
 287		/* Split content by template tags to obtain non-template content. */
 288		$text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $source_content);
 289
 290		/* loop through text blocks */
 291		for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
 292			/* match anything resembling php tags */
 293			if (preg_match_all('!(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)!is', $text_blocks[$curr_tb], $sp_match)) {
 294				/* replace tags with placeholders to prevent recursive replacements */
 295				$sp_match[1] = array_unique($sp_match[1]);
 296				usort($sp_match[1], '_smarty_sort_length');
 297				for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
 298					$text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp], '%%%SMARTYSP'.$curr_sp.'%%%', $text_blocks[$curr_tb]);
 299				}
 300				/* process each one */
 301				for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
 302					if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
 303						/* echo php contents */
 304						$text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
 305					} else if ($this->php_handling == SMARTY_PHP_QUOTE) {
 306							/* quote php tags */
 307							$text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
 308						} else if ($this->php_handling == SMARTY_PHP_REMOVE) {
 309							/* remove php tags */
 310							$text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
 311						} else {
 312						/* SMARTY_PHP_ALLOW, but echo non php starting tags */
 313						$sp_match[1][$curr_sp] = preg_replace('%(<\?(?!php|=|$))%i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
 314						$text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
 315					}
 316				}
 317			}
 318		}
 319
 320		/* Compile the template tags into PHP code. */
 321		$compiled_tags = array();
 322		for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
 323			$this->_current_line_no += substr_count($text_blocks[$i], "\n");
 324			$compiled_tags[] = $this->_compile_tag($template_tags[$i]);
 325			$this->_current_line_no += substr_count($template_tags[$i], "\n");
 326		}
 327
 328		$compiled_content = '';
 329
 330		/* Interleave the compiled contents and text blocks to get the final result. */
 331		for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
 332			if ($compiled_tags[$i] == '') {
 333				// tag result empty, remove first newline from following text block
 334				$text_blocks[$i+1] = preg_replace('!^(\r\n|\r|\n)!', '', $text_blocks[$i+1]);
 335			}
 336			$compiled_content .= $text_blocks[$i].$compiled_tags[$i];
 337		}
 338		$compiled_content .= $text_blocks[$i];
 339
 340		/* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
 341		if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_content, $_match)) {
 342			$strip_tags = $_match[0];
 343			$strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags);
 344			$strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified);
 345			for ($i = 0, $for_max = count($strip_tags); $i < $for_max; $i++)
 346				$compiled_content = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s",
 347				$this->_quote_replace($strip_tags_modified[$i]),
 348				$compiled_content, 1);
 349		}
 350
 351		// remove \n from the end of the file, if any
 352		if (($_len=strlen($compiled_content)) && ($compiled_content{$_len - 1} == "\n" )) {
 353			$compiled_content = substr($compiled_content, 0, -1);
 354		}
 355
 356		if (!empty($this->_cache_serial)) {
 357			$compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
 358		}
 359
 360		// remove unnecessary close/open tags
 361		$compiled_content = preg_replace('!\?>\n?<\?php!', '', $compiled_content);
 362
 363		// run compiled template through postfilter functions
 364		if (count($this->_plugins['postfilter']) > 0) {
 365			foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
 366				if ($postfilter === false) continue;
 367				if ($postfilter[3] || is_callable($postfilter[0])) {
 368					$compiled_content = call_user_func_array($postfilter[0],
 369						array($compiled_content, &$this));
 370					$this->_plugins['postfilter'][$filter_name][3] = true;
 371				} else {
 372					$this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
 373				}
 374			}
 375		}
 376
 377		// put header at the top of the compiled template
 378		$template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
 379		$template_header .= "         compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
 380
 381		/* Emit code to load needed plugins. */
 382		$this->_plugins_code = '';
 383		if (count($this->_plugin_info)) {
 384			$_plugins_params = "array('plugins' => array(";
 385			foreach ($this->_plugin_info as $plugin_type => $plugins) {
 386				foreach ($plugins as $plugin_name => $plugin_info) {
 387					$_plugins_params .= "array('$plugin_type', '$plugin_name', '$plugin_info[0]', $plugin_info[1], ";
 388					$_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
 389				}
 390			}
 391			$_plugins_params .= '))';
 392			$plugins_code = "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
 393			$template_header .= $plugins_code;
 394			$this->_plugin_info = array();
 395			$this->_plugins_code = $plugins_code;
 396		}
 397
 398		if ($this->_init_smarty_vars) {
 399			$template_header .= "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
 400			$this->_init_smarty_vars = false;
 401		}
 402
 403		$compiled_content = $template_header . $compiled_content;
 404
 405		return true;
 406	}
 407
 408
 409	/**
 410	 * Compile a template tag
 411	 *
 412	 * @param string  $template_tag
 413	 * @return string
 414	 */
 415	function _compile_tag($template_tag) {
 416		/* Matched comment. */
 417		if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
 418			return '';
 419
 420		/* Split tag into two three parts: command, command modifiers and the arguments. */
 421		if (! preg_match('/^(?:(' . $this->_obj_call_regexp . '|' . $this->_var_regexp
 422				. '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
 423                      (?:\s+(.*))?$
 424                    /xs', $template_tag, $match)) {
 425			$this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
 426		}
 427
 428		$tag_command = $match[1];
 429		$tag_modifier = isset($match[2]) ? $match[2] : null;
 430		$tag_args = isset($match[3]) ? $match[3] : null;
 431
 432		if (preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$!', $tag_command)) {
 433			/* tag name is a variable or object */
 434			$_return = $this->_parse_var_props($tag_command . $tag_modifier, $this->_parse_attrs($tag_args));
 435			if (isset($_tag_attrs['assign'])) {
 436				return "<?php \$this->assign('" . $this->_dequote($_tag_attrs['assign']) . "', $_return ); ?>\n";
 437			} else {
 438				return "<?php echo $_return; ?>" . $this->_additional_newline;
 439			}
 440		}
 441
 442		/* If the tag name is a registered object, we process it. */
 443		if (preg_match('!^\/?' . $this->_reg_obj_regexp . '$!', $tag_command)) {
 444			return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
 445		}
 446
 447		switch ($tag_command) {
 448		case 'include':
 449			return $this->_compile_include_tag($tag_args);
 450
 451		case 'include_php':
 452			return $this->_compile_include_php_tag($tag_args);
 453
 454		case 'if':
 455			return $this->_compile_if_tag($tag_args);
 456
 457		case 'else':
 458			return '<?php else: ?>';
 459
 460		case 'elseif':
 461			return $this->_compile_if_tag($tag_args, true);
 462
 463		case '/if':
 464			return '<?php endif; ?>';
 465
 466		case 'capture':
 467			return $this->_compile_capture_tag(true, $tag_args);
 468
 469		case '/capture':
 470			return $this->_compile_capture_tag(false);
 471
 472		case 'ldelim':
 473			return $this->left_delimiter;
 474
 475		case 'rdelim':
 476			return $this->right_delimiter;
 477
 478		case 'section':
 479			array_push($this->_sectionelse_stack, false);
 480			return $this->_compile_section_start($tag_args);
 481
 482		case 'sectionelse':
 483			$this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;
 484			return "<?php endfor; else: ?>";
 485
 486		case '/section':
 487			if (array_pop($this->_sectionelse_stack))
 488				return "<?php endif; ?>";
 489			else
 490				return "<?php endfor; endif; ?>";
 491
 492		case 'foreach':
 493			array_push($this->_foreachelse_stack, false);
 494			return $this->_compile_foreach_start($tag_args);
 495			break;
 496
 497		case 'foreachelse':
 498			$this->_foreachelse_stack[count($this->_foreachelse_stack)-1] = true;
 499			return "<?php endforeach; unset(\$_from); else: ?>";
 500
 501		case '/foreach':
 502			if (array_pop($this->_foreachelse_stack))
 503				return "<?php endif; ?>";
 504			else
 505				return "<?php endforeach; unset(\$_from); endif; ?>";
 506
 507		case 'strip':
 508		case '/strip':
 509			if ($tag_command{0}=='/') {
 510				if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
 511					$this->_additional_newline = "\n";
 512					return $this->left_delimiter.$tag_command.$this->right_delimiter;
 513				}
 514			} else {
 515				if ($this->_strip_depth++==0) { /* outermost opening {strip} */
 516					$this->_additional_newline = "";
 517					return $this->left_delimiter.$tag_command.$this->right_delimiter;
 518				}
 519			}
 520			return '';
 521
 522		case 'literal':
 523			list (, $literal_block) = each($this->_literal_blocks);
 524			$this->_current_line_no += substr_count($literal_block, "\n");
 525			return "<?php echo '".str_replace("'", "\'", str_replace("\\", "\\\\", $literal_block))."'; ?>" . $this->_additional_newline;
 526
 527		case 'php':
 528			if ($this->security && !$this->security_settings['PHP_TAGS']) {
 529				$this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
 530				return;
 531			}
 532			list (, $php_block) = each($this->_php_blocks);
 533			$this->_current_line_no += substr_count($php_block, "\n");
 534			return '<?php '.$php_block.' ?>';
 535
 536		case 'insert':
 537			return $this->_compile_insert_tag($tag_args);
 538
 539		default:
 540			if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
 541				return $output;
 542			} else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
 543					return $output;
 544				} else {
 545				return $this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier);
 546			}
 547		}
 548	}
 549
 550
 551	/**
 552	 * compile the custom compiler tag
 553	 *
 554	 * sets $output to the compiled custom compiler tag
 555	 *
 556	 * @param string  $tag_command
 557	 * @param string  $tag_args
 558	 * @param string  $output
 559	 * @return boolean
 560	 */
 561	function _compile_compiler_tag($tag_command, $tag_args, &$output) {
 562		$found = false;
 563		$have_function = true;
 564
 565		/*
 566         * First we check if the compiler function has already been registered
 567         * or loaded from a plugin file.
 568         */
 569		if (isset($this->_plugins['compiler'][$tag_command])) {
 570			$found = true;
 571			$plugin_func = $this->_plugins['compiler'][$tag_command][0];
 572			if (!is_callable($plugin_func)) {
 573				$message = "compiler function '$tag_command' is not implemented";
 574				$have_function = false;
 575			}
 576		}
 577		/*
 578         * Otherwise we need to load plugin file and look for the function
 579         * inside it.
 580         */
 581		else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
 582				$found = true;
 583
 584				include_once $plugin_file;
 585
 586				$plugin_func = 'smarty_compiler_' . $tag_command;
 587				if (!is_callable($plugin_func)) {
 588					$message = "plugin function $plugin_func() not found in $plugin_file\n";
 589					$have_function = false;
 590				} else {
 591					$this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
 592				}
 593			}
 594
 595		/*
 596         * True return value means that we either found a plugin or a
 597         * dynamically registered function. False means that we didn't and the
 598         * compiler should now emit code to load custom function plugin for this
 599         * tag.
 600         */
 601		if ($found) {
 602			if ($have_function) {
 603				$output = call_user_func_array($plugin_func, array($tag_args, &$this));
 604				if ($output != '') {
 605					$output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
 606						. $output
 607						. $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
 608				}
 609			} else {
 610				$this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
 611			}
 612			return true;
 613		} else {
 614			return false;
 615		}
 616	}
 617
 618
 619	/**
 620	 * compile block function tag
 621	 *
 622	 * sets $output to compiled block function tag
 623	 *
 624	 * @param string  $tag_command
 625	 * @param string  $tag_args
 626	 * @param string  $tag_modifier
 627	 * @param string  $output
 628	 * @return boolean
 629	 */
 630	function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output) {
 631		if ($tag_command{0} == '/') {
 632			$start_tag = false;
 633			$tag_command = substr($tag_command, 1);
 634		} else
 635			$start_tag = true;
 636
 637		$found = false;
 638		$have_function = true;
 639
 640		/*
 641         * First we check if the block function has already been registered
 642         * or loaded from a plugin file.
 643         */
 644		if (isset($this->_plugins['block'][$tag_command])) {
 645			$found = true;
 646			$plugin_func = $this->_plugins['block'][$tag_command][0];
 647			if (!is_callable($plugin_func)) {
 648				$message = "block function '$tag_command' is not implemented";
 649				$have_function = false;
 650			}
 651		}
 652		/*
 653         * Otherwise we need to load plugin file and look for the function
 654         * inside it.
 655         */
 656		else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
 657				$found = true;
 658
 659				include_once $plugin_file;
 660
 661				$plugin_func = 'smarty_block_' . $tag_command;
 662				if (!function_exists($plugin_func)) {
 663					$message = "plugin function $plugin_func() not found in $plugin_file\n";
 664					$have_function = false;
 665				} else {
 666					$this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
 667
 668				}
 669			}
 670
 671		if (!$found) {
 672			return false;
 673		} else if (!$have_function) {
 674				$this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
 675				return true;
 676			}
 677
 678		/*
 679         * Even though we've located the plugin function, compilation
 680         * happens only once, so the plugin will still need to be loaded
 681         * at runtime for future requests.
 682         */
 683		$this->_add_plugin('block', $tag_command);
 684
 685		if ($start_tag) {
 686			$output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
 687			$attrs = $this->_parse_attrs($tag_args);
 688			$arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs='');
 689			$output .= "$_cache_attrs\$_params = \$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
 690			$output .= $this->_compile_plugin_call('block', $tag_command).'($_params[1], null, $this, $_block_repeat=true); unset($_params);';
 691			$output .= 'while ($_block_repeat) { ob_start(); ?>';
 692		} else {
 693			$output = '<?php $this->_block_content = ob_get_contents(); ob_end_clean(); ';
 694			$_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $this->_block_content, $this, $_block_repeat=false)';
 695			if ($tag_modifier != '') {
 696				$this->_parse_modifiers($_out_tag_text, $tag_modifier);
 697			}
 698			$output .= 'echo '.$_out_tag_text.'; } ';
 699			$output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
 700		}
 701
 702		return true;
 703	}
 704
 705
 706	/**
 707	 * compile custom function tag
 708	 *
 709	 * @param string  $tag_command
 710	 * @param string  $tag_args
 711	 * @param string  $tag_modifier
 712	 * @return string
 713	 */
 714	function _compile_custom_tag($tag_command, $tag_args, $tag_modifier) {
 715		$this->_add_plugin('function', $tag_command);
 716
 717		$_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
 718		$attrs = $this->_parse_attrs($tag_args);
 719		$arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs='');
 720
 721		$_return = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
 722		if ($tag_modifier != '') {
 723			$this->_parse_modifiers($_return, $tag_modifier);
 724		}
 725
 726		if ($_return != '') {
 727			$_return =  '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $_return . ';'
 728				. $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
 729		}
 730
 731		return $_return;
 732	}
 733
 734
 735	/**
 736	 * compile a registered object tag
 737	 *
 738	 * @param string  $tag_command
 739	 * @param array   $attrs
 740	 * @param string  $tag_modifier
 741	 * @return string
 742	 */
 743	function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier) {
 744		if ($tag_command{0} == '/') {
 745			$start_tag = false;
 746			$tag_command = substr($tag_command, 1);
 747		} else {
 748			$start_tag = true;
 749		}
 750
 751		list($object, $obj_comp) = explode('->', $tag_command);
 752
 753		$arg_list = array();
 754		if (count($attrs)) {
 755			$_assign_var = false;
 756			foreach ($attrs as $arg_name => $arg_value) {
 757				if ($arg_name == 'assign') {
 758					$_assign_var = $arg_value;
 759					unset($attrs['assign']);
 760					continue;
 761				}
 762				if (is_bool($arg_value))
 763					$arg_value = $arg_value ? 'true' : 'false';
 764				$arg_list[] = "'$arg_name' => $arg_value";
 765			}
 766		}
 767
 768		if ($this->_reg_objects[$object][2]) {
 769			// smarty object argument format
 770			$args = "array(".implode(',', (array)$arg_list)."), \$this";
 771		} else {
 772			// traditional argument format
 773			$args = implode(',', array_values($attrs));
 774			if (empty($args)) {
 775				$args = 'null';
 776			}
 777		}
 778
 779		$prefix = '';
 780		$postfix = '';
 781		$newline = '';
 782		if (!is_object($this->_reg_objects[$object][0])) {
 783			$this->_trigger_fatal_error("registered '$object' is not an object");
 784		} elseif (!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
 785			$this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'");
 786		} elseif (method_exists($this->_reg_objects[$object][0], $obj_comp)) {
 787			// method
 788			if (in_array($obj_comp, $this->_reg_objects[$object][3])) {
 789				// block method
 790				if ($start_tag) {
 791					$prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
 792					$prefix .= "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat=true); ";
 793					$prefix .= "while (\$_block_repeat) { ob_start();";
 794					$return = null;
 795					$postfix = '';
 796				} else {
 797					$prefix = "\$this->_obj_block_content = ob_get_contents(); ob_end_clean(); ";
 798					$return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$this->_obj_block_content, \$this, \$_block_repeat=false)";
 799					$postfix = "} array_pop(\$this->_tag_stack);";
 800				}
 801			} else {
 802				// non-block method
 803				$return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
 804			}
 805		} else {
 806			// property
 807			$return = "\$this->_reg_objects['$object'][0]->$obj_comp";
 808		}
 809
 810		if ($return != null) {
 811			if ($tag_modifier != '') {
 812				$this->_parse_modifiers($return, $tag_modifier);
 813			}
 814
 815			if (!empty($_assign_var)) {
 816				$output = "\$this->assign('" . $this->_dequote($_assign_var) ."',  $return);";
 817			} else {
 818				$output = 'echo ' . $return . ';';
 819				$newline = $this->_additional_newline;
 820			}
 821		} else {
 822			$output = '';
 823		}
 824
 825		return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
 826	}
 827
 828
 829	/**
 830	 * Compile {insert ...} tag
 831	 *
 832	 * @param string  $tag_args
 833	 * @return string
 834	 */
 835	function _compile_insert_tag($tag_args) {
 836		$attrs = $this->_parse_attrs($tag_args);
 837		$name = $this->_dequote($attrs['name']);
 838
 839		if (empty($name)) {
 840			$this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
 841		}
 842
 843		if (!empty($attrs['script'])) {
 844			$delayed_loading = true;
 845		} else {
 846			$delayed_loading = false;
 847		}
 848
 849		foreach ($attrs as $arg_name => $arg_value) {
 850			if (is_bool($arg_value))
 851				$arg_value = $arg_value ? 'true' : 'false';
 852			$arg_list[] = "'$arg_name' => $arg_value";
 853		}
 854
 855		$this->_add_plugin('insert', $name, $delayed_loading);
 856
 857		$_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
 858
 859		return "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
 860	}
 861
 862
 863	/**
 864	 * Compile {include ...} tag
 865	 *
 866	 * @param string  $tag_args
 867	 * @return string
 868	 */
 869	function _compile_include_tag($tag_args) {
 870		$attrs = $this->_parse_attrs($tag_args);
 871		$arg_list = array();
 872
 873		if (empty($attrs['file'])) {
 874			$this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
 875		}
 876
 877		foreach ($attrs as $arg_name => $arg_value) {
 878			if ($arg_name == 'file') {
 879				$include_file = $arg_value;
 880				continue;
 881			} else if ($arg_name == 'assign') {
 882					$assign_var = $arg_value;
 883					continue;
 884				}
 885			if (is_bool($arg_value))
 886				$arg_value = $arg_value ? 'true' : 'false';
 887			$arg_list[] = "'$arg_name' => $arg_value";
 888		}
 889
 890		$output = '<?php ';
 891
 892		if (isset($assign_var)) {
 893			$output .= "ob_start();\n";
 894		}
 895
 896		$output .=
 897			"\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
 898
 899
 900		$_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
 901		$output .= "\$this->_smarty_include($_params);\n" .
 902			"\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
 903			"unset(\$_smarty_tpl_vars);\n";
 904
 905		if (isset($assign_var)) {
 906			$output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
 907		}
 908
 909		$output .= ' ?>';
 910
 911		return $output;
 912
 913	}
 914
 915
 916	/**
 917	 * Compile {include ...} tag
 918	 *
 919	 * @param string  $tag_args
 920	 * @return string
 921	 */
 922	function _compile_include_php_tag($tag_args) {
 923		$attrs = $this->_parse_attrs($tag_args);
 924
 925		if (empty($attrs['file'])) {
 926			$this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
 927		}
 928
 929		$assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
 930		$once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
 931
 932		foreach ($attrs as $arg_name => $arg_value) {
 933			if ($arg_name != 'file' and $arg_name != 'once' and $arg_name != 'assign') {
 934				if (is_bool($arg_value))
 935					$arg_value = $arg_value ? 'true' : 'false';
 936				$arg_list[] = "'$arg_name' => $arg_value";
 937			}
 938		}
 939
 940		$_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
 941
 942		return "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
 943	}
 944
 945
 946	/**
 947	 * Compile {section ...} tag
 948	 *
 949	 * @param string  $tag_args
 950	 * @return string
 951	 */
 952	function _compile_section_start($tag_args) {
 953		$attrs = $this->_parse_attrs($tag_args);
 954		$arg_list = array();
 955
 956		$output = '<?php ';
 957		$section_name = $attrs['name'];
 958		if (empty($section_name)) {
 959			$this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
 960		}
 961
 962		$output .= "if (isset(\$this->_sections[$section_name])) unset(\$this->_sections[$section_name]);\n";
 963		$section_props = "\$this->_sections[$section_name]";
 964
 965		foreach ($attrs as $attr_name => $attr_value) {
 966			switch ($attr_name) {
 967			case 'loop':
 968				$output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
 969				break;
 970
 971			case 'show':
 972				if (is_bool($attr_value))
 973					$show_attr_value = $attr_value ? 'true' : 'false';
 974				else
 975					$show_attr_value = "(bool)$attr_value";
 976				$output .= "{$section_props}['show'] = $show_attr_value;\n";
 977				break;
 978
 979			case 'name':
 980				$output .= "{$section_props}['$attr_name'] = $attr_value;\n";
 981				break;
 982
 983			case 'max':
 984			case 'start':
 985				$output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
 986				break;
 987
 988			case 'step':
 989				$output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
 990				break;
 991
 992			default:
 993				$this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
 994				break;
 995			}
 996		}
 997
 998		if (!isset($attrs['show']))
 999			$output .= "{$section_props}['show'] = true;\n";
1000
1001		if (!isset($attrs['loop']))
1002			$output .= "{$section_props}['loop'] = 1;\n";
1003
1004		if (!isset($attrs['max']))
1005			$output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
1006		else
1007			$output .= "if ({$section_props}['max'] < 0)\n" .
1008			"    {$section_props}['max'] = {$section_props}['loop'];\n";
1009
1010		if (!isset($attrs['step']))
1011			$output .= "{$section_props}['step'] = 1;\n";
1012
1013		if (!isset($attrs['start']))
1014			$output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
1015		else {
1016			$output .= "if ({$section_props}['start'] < 0)\n" .
1017				"    {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
1018				"else\n" .
1019				"    {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
1020		}
1021
1022		$output .= "if ({$section_props}['show']) {\n";
1023		if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
1024			$output .= "    {$section_props}['total'] = {$section_props}['loop'];\n";
1025		} else {
1026			$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";
1027		}
1028		$output .= "    if ({$section_props}['total'] == 0)\n" .
1029			"        {$section_props}['show'] = false;\n" .
1030			"} else\n" .
1031			"    {$section_props}['total'] = 0;\n";
1032
1033		$output .= "if ({$section_props}['show']):\n";
1034		$output .= "
1035            for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
1036                 {$section_props}['iteration'] <= {$section_props}['total'];
1037                 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
1038		$output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
1039		$output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
1040		$output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
1041		$output .= "{$section_props}['first']      = ({$section_props}['iteration'] == 1);\n";
1042		$output .= "{$section_props}['last']       = ({$section_props}['iteration'] == {$section_props}['total']);\n";
1043
1044		$output .= "?>";
1045
1046		return $output;
1047	}
1048
1049
1050	/**
1051	 * Compile {foreach ...} tag.
1052	 *
1053	 * @param string  $tag_args
1054	 * @return string
1055	 */
1056	function _compile_foreach_start($tag_args) {
1057		$attrs = $this->_parse_attrs($tag_args);
1058		$arg_list = array();
1059
1060		if (empty($attrs['from'])) {
1061			$this->_syntax_error("missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
1062		}
1063
1064		if (empty($attrs['item'])) {
1065			$this->_syntax_error("missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
1066		}
1067
1068		$from = $attrs['from'];
1069		$item = $this->_dequote($attrs['item']);
1070		if (isset($attrs['name']))
1071			$name = $attrs['name'];
1072
1073		$output = '<?php ';
1074		if (isset($name)) {
1075			$output .= "if (isset(\$this->_foreach[$name])) unset(\$this->_foreach[$name]);\n";
1076			$foreach_props = "\$this->_foreach[$name]";
1077		}
1078
1079		$key_part = '';
1080
1081		foreach ($attrs as $attr_name => $attr_value) {
1082			switch ($attr_name) {
1083			case 'key':
1084				$key  = $this->_dequote($attrs['key']);
1085				$key_part = "\$this->_tpl_vars['$key'] => ";
1086				break;
1087
1088			case 'name':
1089				$output .= "{$foreach_props}['$attr_name'] = $attr_value;\n";
1090				break;
1091			}
1092		}
1093
1094		if (isset($name)) {
1095			$output .= "{$foreach_props}['total'] = count(\$_from = (array)$from);\n";
1096			$output .= "{$foreach_props}['show'] = {$foreach_props}['total'] > 0;\n";
1097			$output .= "if ({$foreach_props}['show']):\n";
1098			$output .= "{$foreach_props}['iteration'] = 0;\n";
1099			$output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1100			$output .= "        {$foreach_props}['iteration']++;\n";
1101			$output .= "        {$foreach_props}['first'] = ({$foreach_props}['iteration'] == 1);\n";
1102			$output .= "        {$foreach_props}['last']  = ({$foreach_props}['iteration'] == {$foreach_props}['total']);\n";
1103		} else {
1104			$output .= "if (count(\$_from = (array)$from)):\n";
1105			$output .= "    foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1106		}
1107		$output .= '?>';
1108
1109		return $output;
1110	}
1111
1112
1113	/**
1114	 * Compile {capture} .. {/capture} tags
1115	 *
1116	 * @param boolean $start    true if this is the {capture} tag
1117	 * @param string  $tag_args
1118	 * @return string
1119	 */
1120	function _compile_capture_tag($start, $tag_args = '') {
1121		$attrs = $this->_parse_attrs($tag_args);
1122
1123		if ($start) {
1124			if (isset($attrs['name']))
1125				$buffer = $attrs['name'];
1126			else
1127				$buffer = "'default'";
1128
1129			if (isset($attrs['assign']))
1130				$assign = $attrs['assign'];
1131			else
1132				$assign = null;
1133			$output = "<?php ob_start(); ?>";
1134			$this->_capture_stack[] = array($buffer, $assign);
1135		} else {
1136			list($buffer, $assign) = array_pop($this->_capture_stack);
1137			$output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
1138			if (isset($assign)) {
1139				$output .= " \$this->assign($assign, ob_get_contents());";
1140			}
1141			$output .= "ob_end_clean(); ?>";
1142		}
1143
1144		return $output;
1145	}
1146
1147
1148	/**
1149	 * Compile {if ...} tag
1150	 *
1151	 * @param string  $tag_args
1152	 * @param boolean $elseif   (optional) if true, uses elseif instead of if
1153	 * @return string
1154	 */
1155	function _compile_if_tag($tag_args, $elseif = false) {
1156
1157		/* Tokenize args for 'if' tag. */
1158		preg_match_all('/(?>
1159                ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
1160                ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)?    | # var or quoted string
1161                \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@    | # valid non-word token
1162                \b\w+\b                                                        | # valid word token
1163                \S+                                                           # anything else
1164                )/x', $tag_args, $match);
1165
1166		$tokens = $match[0];
1167
1168		// make sure we have balanced parenthesis
1169		$token_count = array_count_values($tokens);
1170		if (isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1171			$this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
1172		}
1173
1174		$is_arg_stack = array();
1175
1176		for ($i = 0; $i < count($tokens); $i++) {
1177
1178			$token = &$tokens[$i];
1179
1180			switch (strtolower($token)) {
1181			case '!':
1182			case '%':
1183			case '!==':
1184			case '==':
1185			case '===':
1186			case '>':
1187			case '<':
1188			case '!=':
1189			case '<>':
1190			case '<<':
1191			case '>>':
1192			case '<=':
1193			case '>=':
1194			case '&&':
1195			case '||':
1196			case '|':
1197			case '^':
1198			case '&':
1199			case '~':
1200			case ')':
1201			case ',':
1202			case '+':
1203			case '-':
1204			case '*':
1205			case '/':
1206			case '@':
1207				break;
1208
1209			case 'eq':
1210				$token = '==';
1211				break;
1212
1213			case 'ne':
1214			case 'neq':
1215				$token = '!=';
1216				break;
1217
1218			case 'lt':
1219				$token = '<';
1220				break;
1221
1222			case 'le':
1223			case 'lte':
1224				$token = '<=';
1225				break;
1226
1227			case 'gt':
1228				$token = '>';
1229				break;
1230
1231			case 'ge':
1232			case 'gte':
1233				$token = '>=';
1234				break;
1235
1236			case 'and':
1237				$token = '&&';
1238				break;
1239
1240			case 'or':
1241				$token = '||';
1242				break;
1243
1244			case 'not':
1245				$token = '!';
1246				break;
1247
1248			case 'mod':
1249				$token = '%';
1250				break;
1251
1252			case '(':
1253				array_push($is_arg_stack, $i);
1254				break;
1255
1256			case 'is':
1257				/* If last token was a ')', we operate on the parenthesized
1258                       expression. The start of the expression is on the stack.
1259                       Otherwise, we operate on the last encountered token. */
1260				if ($tokens[$i-1] == ')')
1261					$is_arg_start = array_pop($is_arg_stack);
1262				else
1263					$is_arg_start = $i-1;
1264				/* Construct the argument for 'is' expression, so it knows
1265                       what to operate on. */
1266				$is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1267
1268				/* Pass all tokens from next one until the end to the
1269                       'is' expression parsing function. The function will
1270                       return modified tokens, where the first one is the result
1271                       of the 'is' expression and the rest are the tokens it
1272                       didn't touch. */
1273				$new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
1274
1275				/* Replace the old tokens with the new ones. */
1276				array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1277
1278				/* Adjust argument start so that it won't change from the
1279                       current position for the next iteration. */
1280				$i = $is_arg_start;
1281				break;
1282
1283			default:
1284				if (preg_match('!^' . $this->_func_regexp . '$!', $token) ) {
1285					// function call
1286					if ($this->security &&
1287						!in_array($token, $this->security_settings['IF_FUNCS'])) {
1288						$this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1289					}
1290				} elseif (preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$!', $token)) {
1291					// object or variable
1292					$token = $this->_parse_var_props($token);
1293				} elseif (is_numeric($token)) {
1294					// number, skip it
1295				} else {
1296					$this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
1297				}
1298				break;
1299			}
1300		}
1301
1302		if ($elseif)
1303			return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1304		else
1305			return '<?php if ('.implode(' ', $tokens).'): ?>';
1306	}
1307
1308
1309	/**
1310	 *
1311	 *
1312	 * @param unknown $type
1313	 * @param unknown $name
1314	 * @param unknown $attrs
1315	 * @param unknown $cache_code (reference)
1316	 * @return unknown
1317	 */
1318	function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1319		$arg_list = array();
1320
1321		if (isset($type) && isset($name)
1322			&& isset($this->_plugins[$type])
1323			&& isset($this->_plugins[$type][$name])
1324			&& empty($this->_plugins[$type][$name][4])
1325			&& is_array($this->_plugins[$type][$name][5])
1326		) {
1327			/* we have a list of parameters that should be cached */
1328			$_cache_attrs = $this->_plugins[$type][$name][5];
1329			$_count = $this->_cache_attrs_count++;
1330			$cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1331
1332		} else {
1333			/* no parameters are cached */
1334			$_cache_attrs = null;
1335		}
1336
1337		foreach ($attrs as $arg_name => $arg_value) {
1338			if (is_bool($arg_value))
1339				$arg_value = $arg_value ? 'true' : 'false';
1340			if (is_null($arg_value))
1341				$arg_value = 'null';
1342			if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1343				$arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1344			} else {
1345				$arg_list[] = "'$arg_name' => $arg_value";
1346			}
1347		}
1348		return $arg_list;
1349	}
1350
1351
1352	/**
1353	 * Parse is expression
1354	 *
1355	 * @param string  $is_arg
1356	 * @param array   $tokens
1357	 * @return array
1358	 */
1359	function _parse_is_expr($is_arg, $tokens) {
1360		$expr_end = 0;
1361		$negate_expr = false;
1362
1363		if (($first_token = array_shift($tokens)) == 'not') {
1364			$negate_expr = true;
1365			$expr_type = array_shift($tokens);
1366		} else
1367			$expr_type = $first_token;
1368
1369		switch ($expr_type) {
1370		case 'even':
1371			if (@$tokens[$expr_end] == 'by') {
1372				$expr_end++;
1373				$expr_arg = $tokens[$expr_end++];
1374				$expr = "!(($is_arg / $expr_arg) % " . $this->_parse_var_props($expr_arg) . ")";
1375			} else
1376				$expr = "!($is_arg % 2)";
1377			break;
1378
1379		case 'odd':
1380			if (@$tokens[$expr_end] == 'by') {
1381				$expr_end++;
1382				$expr_arg = $tokens[$expr_end++];
1383				$expr = "(($is_arg / $expr_arg) % ". $this->_parse_var_props($expr_arg) . ")";
1384			} else
1385				$expr = "($is_arg % 2)";
1386			break;
1387
1388		case 'div':
1389			if (@$tokens[$expr_end] == 'by') {
1390				$expr_end++;
1391				$expr_arg = $tokens[$expr_end++];
1392				$expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1393			} else {
1394				$this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1395			}
1396			break;
1397
1398		default:
1399			$this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1400			break;
1401		}
1402
1403		if ($negate_expr) {
1404			$expr = "!($expr)";
1405		}
1406
1407		array_splice($tokens, 0, $expr_end, $expr);
1408
1409		return $tokens;
1410	}
1411
1412
1413	/**
1414	 * Parse attribute string
1415	 *
1416	 * @param string  $tag_args
1417	 * @return array
1418	 */
1419	function _parse_attrs($tag_args) {
1420
1421		/* Tokenize tag attributes. */
1422		preg_match_all('/(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
1423                         )+ |
1424                         [=]
1425                        /x', $tag_args, $match);
1426		$tokens       = $match[0];
1427
1428		$attrs = array();
1429		/* Parse state:
1430            0 - expecting attribute name
1431            1 - expecting '='
1432            2 - expecting attribute value (not '=') */
1433		$state = 0;
1434
1435		foreach ($tokens as $token) {
1436			switch ($state) {
1437			case 0:
1438				/* If the token is a valid identifier, we set attribute name
1439                       and go to state 1. */
1440				if (preg_match('!^\w+$!', $token)) {
1441					$attr_name = $token;
1442					$state = 1;
1443				} else
1444					$this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1445				break;
1446
1447			case 1:
1448				/* If the token is '=', then we go to state 2. */
1449				if ($token == '=') {
1450					$state = 2;
1451				} else
1452					$this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1453				break;
1454
1455			case 2:
1456				/* If token is not '=', we set the attribute value and go to
1457                       state 0. */
1458				if ($token != '=') {
1459					/* We booleanize the token if it's a non-quoted possible
1460                           boolean value. */
1461					if (preg_match('!^(on|yes|true)$!', $token)) {
1462						$token = 'true';
1463					} else if (preg_match('!^(off|no|false)$!', $token)) {
1464							$token = 'false';
1465						} else if ($token == 'null') {
1466							$token = 'null';
1467						} else if (preg_match('!^-?([0-9]+|0[xX][0-9a-fA-F]+)$!', $token)) {
1468							/* treat integer literally */
1469						} else if (!preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$!', $token)) {
1470							/* treat as a string, double-quote it escaping quotes */
1471							$token = '"'.addslashes($token).'"';
1472						}
1473
1474					$attrs[$attr_name] = $token;
1475					$state = 0;
1476				} else
1477					$this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1478				break;
1479			}
1480			$last_token = $token;
1481		}
1482
1483		if ($state != 0) {
1484			if ($state == 1) {
1485				$this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1486			} else {
1487				$this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
1488			}
1489		}
1490
1491		$this->_parse_vars_props($attrs);
1492
1493		return $attrs;
1494	}
1495
1496
1497	/**
1498	 * compile multiple variables and section properties tokens into
1499	 * PHP code
1500	 *
1501	 * @param array   $tokens
1502	 */
1503	function _parse_vars_props(&$tokens) {
1504		foreach ($tokens as $key => $val) {
1505			$tokens[$key] = $this->_parse_var_props($val);
1506		}
1507	}
1508
1509
1510	/**
1511	 * compile single variable and section properties token into
1512	 * PHP code
1513	 *
1514	 * @param string  $val
1515	 * @return string
1516	 */
1517	function _parse_var_props($val) {
1518		$val = trim($val);
1519
1520		if (preg_match('!^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$!', $val, $match)) {
1521			// $ variable or object
1522			$return = $this->_parse_var($match[1]);
1523			if ($match[2] != '') {
1524				$this->_parse_modifiers($return, $match[2]);
1525			}
1526			return $return;
1527		}
1528		elseif (preg_match('!^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
1529			// double quoted text
1530			preg_match('!^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$!', $val, $match);
1531			$return = $this->_expand_quoted_text($match[1]);
1532			if ($match[2] != '') {
1533				$this->_parse_modifiers($return, $match[2]);
1534			}
1535			return $return;
1536		}
1537		elseif (preg_match('!^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
1538			// single quoted text
1539			preg_match('!^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$!', $val, $match);
1540			if ($match[2] != '') {
1541				$this->_parse_modifiers($match[1], $match[2]);
1542				return $match[1];
1543			}
1544		}
1545		elseif

Large files files are truncated, but you can click here to view the full file