undef HALF_FREQUENCY_SENDING_TO_CLIENT
[ryzomcore.git] / web / public_php / admin / smarty / Smarty_Compiler.class.php
blob27bf469f1f55f2718fb35b159d271603d2748e3c
1 <?php
3 /**
4 * Project: Smarty: the PHP compiling template engine
5 * File: Smarty_Compiler.class.php
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * @link http://smarty.php.net/
22 * @author Monte Ohrt <monte at ohrt dot com>
23 * @author Andrei Zmievski <andrei@php.net>
24 * @version 2.6.25-dev
25 * @copyright 2001-2005 New Digital Group, Inc.
26 * @package Smarty
29 /* $Id: Smarty_Compiler.class.php 4779 2013-09-30 19:14:32Z Uwe.Tews@googlemail.com $ */
31 /**
32 * Template compiling class
33 * @package Smarty
35 class Smarty_Compiler extends Smarty {
37 // internal vars
38 /**#@+
39 * @access private
41 var $_folded_blocks = array(); // keeps folded template blocks
42 var $_current_file = null; // the current template being compiled
43 var $_current_line_no = 1; // line number for error messages
44 var $_capture_stack = array(); // keeps track of nested capture buffers
45 var $_plugin_info = array(); // keeps track of plugins to load
46 var $_init_smarty_vars = false;
47 var $_permitted_tokens = array('true','false','yes','no','on','off','null');
48 var $_db_qstr_regexp = null; // regexps are setup in the constructor
49 var $_si_qstr_regexp = null;
50 var $_qstr_regexp = null;
51 var $_func_regexp = null;
52 var $_reg_obj_regexp = null;
53 var $_var_bracket_regexp = null;
54 var $_num_const_regexp = null;
55 var $_dvar_guts_regexp = null;
56 var $_dvar_regexp = null;
57 var $_cvar_regexp = null;
58 var $_svar_regexp = null;
59 var $_avar_regexp = null;
60 var $_mod_regexp = null;
61 var $_var_regexp = null;
62 var $_parenth_param_regexp = null;
63 var $_func_call_regexp = null;
64 var $_obj_ext_regexp = null;
65 var $_obj_start_regexp = null;
66 var $_obj_params_regexp = null;
67 var $_obj_call_regexp = null;
68 var $_cacheable_state = 0;
69 var $_cache_attrs_count = 0;
70 var $_nocache_count = 0;
71 var $_cache_serial = null;
72 var $_cache_include = null;
74 var $_strip_depth = 0;
75 var $_additional_newline = "\n";
77 /**#@-*/
78 /**
79 * The class constructor.
81 function __construct()
83 parent::__construct();
85 // matches double quoted strings:
86 // "foobar"
87 // "foo\"bar"
88 $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
90 // matches single quoted strings:
91 // 'foobar'
92 // 'foo\'bar'
93 $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
95 // matches single or double quoted strings
96 $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
98 // matches bracket portion of vars
99 // [0]
100 // [foo]
101 // [$bar]
102 $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
104 // matches numerical constants
105 // 30
106 // -12
107 // 13.22
108 $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
110 // matches $ vars (not objects):
111 // $foo
112 // $foo.bar
113 // $foo.bar.foobar
114 // $foo[0]
115 // $foo[$bar]
116 // $foo[5][blah]
117 // $foo[5].bar[$foobar][4]
118 $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))';
119 $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
120 $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
121 . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
122 $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
124 // matches config vars:
125 // #foo#
126 // #foobar123_foo#
127 $this->_cvar_regexp = '\#\w+\#';
129 // matches section vars:
130 // %foo.bar%
131 $this->_svar_regexp = '\%\w+\.\w+\%';
133 // matches all valid variables (no quotes, no modifiers)
134 $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
135 . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
137 // matches valid variable syntax:
138 // $foo
139 // $foo
140 // #foo#
141 // #foo#
142 // "text"
143 // "text"
144 $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
146 // matches valid object call (one level of object nesting allowed in parameters):
147 // $foo->bar
148 // $foo->bar()
149 // $foo->bar("text")
150 // $foo->bar($foo, $bar, "text")
151 // $foo->bar($foo, "foo")
152 // $foo->bar->foo()
153 // $foo->bar->foo->bar()
154 // $foo->bar($foo->bar)
155 // $foo->bar($foo->bar())
156 // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
157 $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
158 $this->_obj_restricted_param_regexp = '(?:'
159 . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')'
160 . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)';
161 $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
162 . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
163 $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
164 . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
165 $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
166 $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 . ')*)?)';
168 // matches valid modifier syntax:
169 // |foo
170 // |@foo
171 // |foo:"bar"
172 // |foo:$bar
173 // |foo:"bar":$foobar
174 // |foo|bar
175 // |foo:$foo->bar
176 $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|'
177 . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
179 // matches valid function name:
180 // foo123
181 // _foo_bar
182 $this->_func_regexp = '[a-zA-Z_]\w*';
184 // matches valid registered object:
185 // foo->bar
186 $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
188 // matches valid parameter values:
189 // true
190 // $foo
191 // $foo|bar
192 // #foo#
193 // #foo#|bar
194 // "text"
195 // "text"|bar
196 // $foo->bar
197 $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
198 . $this->_var_regexp . '|' . $this->_num_const_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
200 // matches valid parenthesised function parameters:
202 // "text"
203 // $foo, $bar, "text"
204 // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
205 $this->_parenth_param_regexp = '(?:\((?:\w+|'
206 . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
207 . $this->_param_regexp . ')))*)?\))';
209 // matches valid function call:
210 // foo()
211 // foo_bar($foo)
212 // _foo_bar($foo,"bar")
213 // foo123($foo,$foo->bar(),"foo")
214 $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
215 . $this->_parenth_param_regexp . '))';
219 * compile a resource
221 * sets $compiled_content to the compiled source
222 * @param string $resource_name
223 * @param string $source_content
224 * @param string $compiled_content
225 * @return true
227 function _compile_file($resource_name, $source_content, &$compiled_content)
230 if ($this->security) {
231 // do not allow php syntax to be executed unless specified
232 if ($this->php_handling == SMARTY_PHP_ALLOW &&
233 !$this->security_settings['PHP_HANDLING']) {
234 $this->php_handling = SMARTY_PHP_PASSTHRU;
238 $this->_load_filters();
240 $this->_current_file = $resource_name;
241 $this->_current_line_no = 1;
242 $ldq = preg_quote($this->left_delimiter, '~');
243 $rdq = preg_quote($this->right_delimiter, '~');
245 // run template source through prefilter functions
246 if (count($this->_plugins['prefilter']) > 0) {
247 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
248 if ($prefilter === false) continue;
249 if ($prefilter[3] || is_callable($prefilter[0])) {
250 $source_content = call_user_func_array($prefilter[0],
251 array($source_content, &$this));
252 $this->_plugins['prefilter'][$filter_name][3] = true;
253 } else {
254 $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
259 /* fetch all special blocks */
260 $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
262 preg_match_all($search, $source_content, $match, PREG_SET_ORDER);
263 $this->_folded_blocks = $match;
264 reset($this->_folded_blocks);
266 /* replace special blocks by "{php}" */
267 $source_content = preg_replace_callback($search, function($matches) {
268 return $this->_quote_replace($this->left_delimiter).'php'.
269 str_repeat("\n", substr_count($matches[1], "\n")).
270 $this->_quote_replace($this->right_delimiter);
271 }, $source_content);
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);
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*[\"\']?\s*php\s*[\"\']?)~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]);
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]);
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");
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;
322 /* Reformat $text_blocks between 'strip' and '/strip' tags,
323 removing spaces, tabs and newlines. */
324 $strip = false;
325 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
326 if ($compiled_tags[$i] == '{strip}') {
327 $compiled_tags[$i] = '';
328 $strip = true;
329 /* remove leading whitespaces */
330 $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
332 if ($strip) {
333 /* strip all $text_blocks before the next '/strip' */
334 for ($j = $i + 1; $j < $for_max; $j++) {
335 /* remove leading and trailing whitespaces of each line */
336 $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]);
337 if ($compiled_tags[$j] == '{/strip}') {
338 /* remove trailing whitespaces from the last text_block */
339 $text_blocks[$j] = rtrim($text_blocks[$j]);
341 $text_blocks[$j] = "<?php echo '" . strtr($text_blocks[$j], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>";
342 if ($compiled_tags[$j] == '{/strip}') {
343 $compiled_tags[$j] = "\n"; /* slurped by php, but necessary
344 if a newline is following the closing strip-tag */
345 $strip = false;
346 $i = $j;
347 break;
352 $compiled_content = '';
354 $tag_guard = '%%%SMARTYOTG' . md5(uniqid(rand(), true)) . '%%%';
356 /* Interleave the compiled contents and text blocks to get the final result. */
357 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
358 if ($compiled_tags[$i] == '') {
359 // tag result empty, remove first newline from following text block
360 $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]);
362 // replace legit PHP tags with placeholder
363 $text_blocks[$i] = str_replace('<?', $tag_guard, $text_blocks[$i]);
364 $compiled_tags[$i] = str_replace('<?', $tag_guard, $compiled_tags[$i]);
366 $compiled_content .= $text_blocks[$i] . $compiled_tags[$i];
368 $compiled_content .= str_replace('<?', $tag_guard, $text_blocks[$i]);
370 // escape php tags created by interleaving
371 $compiled_content = str_replace('<?', "<?php echo '<?' ?>\n", $compiled_content);
372 $compiled_content = preg_replace("~(?<!')language\s*=\s*[\"\']?\s*php\s*[\"\']?~", "<?php echo 'language=php' ?>\n", $compiled_content);
374 // recover legit tags
375 $compiled_content = str_replace($tag_guard, '<?', $compiled_content);
377 // remove \n from the end of the file, if any
378 if (strlen($compiled_content) && (substr($compiled_content, -1) == "\n") ) {
379 $compiled_content = substr($compiled_content, 0, -1);
382 if (!empty($this->_cache_serial)) {
383 $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
386 // run compiled template through postfilter functions
387 if (count($this->_plugins['postfilter']) > 0) {
388 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
389 if ($postfilter === false) continue;
390 if ($postfilter[3] || is_callable($postfilter[0])) {
391 $compiled_content = call_user_func_array($postfilter[0],
392 array($compiled_content, &$this));
393 $this->_plugins['postfilter'][$filter_name][3] = true;
394 } else {
395 $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
400 // put header at the top of the compiled template
401 $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
402 $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
404 /* Emit code to load needed plugins. */
405 $this->_plugins_code = '';
406 if (count($this->_plugin_info)) {
407 $_plugins_params = "array('plugins' => array(";
408 foreach ($this->_plugin_info as $plugin_type => $plugins) {
409 foreach ($plugins as $plugin_name => $plugin_info) {
410 $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
411 $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
414 $_plugins_params .= '))';
415 $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
416 $template_header .= $plugins_code;
417 $this->_plugin_info = array();
418 $this->_plugins_code = $plugins_code;
421 if ($this->_init_smarty_vars) {
422 $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
423 $this->_init_smarty_vars = false;
426 $compiled_content = $template_header . $compiled_content;
427 return true;
431 * Compile a template tag
433 * @param string $template_tag
434 * @return string
436 function _compile_tag($template_tag)
438 /* Matched comment. */
439 if (substr($template_tag, 0, 1) == '*' && substr($template_tag, -1) == '*')
440 return '';
442 /* Split tag into two three parts: command, command modifiers and the arguments. */
443 if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp
444 . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
445 (?:\s+(.*))?$
446 ~xs', $template_tag, $match)) {
447 $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
450 $tag_command = $match[1];
451 $tag_modifier = isset($match[2]) ? $match[2] : null;
452 $tag_args = isset($match[3]) ? $match[3] : null;
454 if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {
455 /* tag name is a variable or object */
456 $_return = $this->_parse_var_props($tag_command . $tag_modifier);
457 return "<?php echo $_return; ?>" . $this->_additional_newline;
460 /* If the tag name is a registered object, we process it. */
461 if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) {
462 return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
465 switch ($tag_command) {
466 case 'include':
467 return $this->_compile_include_tag($tag_args);
469 case 'include_php':
470 return $this->_compile_include_php_tag($tag_args);
472 case 'if':
473 $this->_push_tag('if');
474 return $this->_compile_if_tag($tag_args);
476 case 'else':
477 list($_open_tag) = end($this->_tag_stack);
478 if ($_open_tag != 'if' && $_open_tag != 'elseif')
479 $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
480 else
481 $this->_push_tag('else');
482 return '<?php else: ?>';
484 case 'elseif':
485 list($_open_tag) = end($this->_tag_stack);
486 if ($_open_tag != 'if' && $_open_tag != 'elseif')
487 $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
488 if ($_open_tag == 'if')
489 $this->_push_tag('elseif');
490 return $this->_compile_if_tag($tag_args, true);
492 case '/if':
493 $this->_pop_tag('if');
494 return '<?php endif; ?>';
496 case 'capture':
497 return $this->_compile_capture_tag(true, $tag_args);
499 case '/capture':
500 return $this->_compile_capture_tag(false);
502 case 'ldelim':
503 return $this->left_delimiter;
505 case 'rdelim':
506 return $this->right_delimiter;
508 case 'section':
509 $this->_push_tag('section');
510 return $this->_compile_section_start($tag_args);
512 case 'sectionelse':
513 $this->_push_tag('sectionelse');
514 return "<?php endfor; else: ?>";
515 break;
517 case '/section':
518 $_open_tag = $this->_pop_tag('section');
519 if ($_open_tag == 'sectionelse')
520 return "<?php endif; ?>";
521 else
522 return "<?php endfor; endif; ?>";
524 case 'foreach':
525 $this->_push_tag('foreach');
526 return $this->_compile_foreach_start($tag_args);
527 break;
529 case 'foreachelse':
530 $this->_push_tag('foreachelse');
531 return "<?php endforeach; else: ?>";
533 case '/foreach':
534 $_open_tag = $this->_pop_tag('foreach');
535 if ($_open_tag == 'foreachelse')
536 return "<?php endif; unset(\$_from); ?>";
537 else
538 return "<?php endforeach; endif; unset(\$_from); ?>";
539 break;
541 case 'strip':
542 case '/strip':
543 if (substr($tag_command, 0, 1)=='/') {
544 $this->_pop_tag('strip');
545 if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
546 $this->_additional_newline = "\n";
547 return '{' . $tag_command . '}';
549 } else {
550 $this->_push_tag('strip');
551 if ($this->_strip_depth++==0) { /* outermost opening {strip} */
552 $this->_additional_newline = "";
553 return '{' . $tag_command . '}';
556 return '';
558 case 'php':
559 /* handle folded tags replaced by {php} */
560 $block = current($this->_folded_blocks);
561 $this->_current_line_no += substr_count($block[0], "\n");
562 /* the number of matched elements in the regexp in _compile_file()
563 determins the type of folded tag that was found */
564 switch (count($block)) {
565 case 2: /* comment */
566 return '';
568 case 3: /* literal */
569 return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
571 case 4: /* php */
572 if ($this->security && !$this->security_settings['PHP_TAGS']) {
573 $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
574 return;
576 return '<?php ' . $block[3] .' ?>';
578 break;
580 case 'insert':
581 return $this->_compile_insert_tag($tag_args);
583 default:
584 if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
585 return $output;
586 } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
587 return $output;
588 } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
589 return $output;
590 } else {
591 $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
599 * compile the custom compiler tag
601 * sets $output to the compiled custom compiler tag
602 * @param string $tag_command
603 * @param string $tag_args
604 * @param string $output
605 * @return boolean
607 function _compile_compiler_tag($tag_command, $tag_args, &$output)
609 $found = false;
610 $have_function = true;
613 * First we check if the compiler function has already been registered
614 * or loaded from a plugin file.
616 if (isset($this->_plugins['compiler'][$tag_command])) {
617 $found = true;
618 $plugin_func = $this->_plugins['compiler'][$tag_command][0];
619 if (!is_callable($plugin_func)) {
620 $message = "compiler function '$tag_command' is not implemented";
621 $have_function = false;
625 * Otherwise we need to load plugin file and look for the function
626 * inside it.
628 else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
629 $found = true;
631 include_once $plugin_file;
633 $plugin_func = 'smarty_compiler_' . $tag_command;
634 if (!is_callable($plugin_func)) {
635 $message = "plugin function $plugin_func() not found in $plugin_file\n";
636 $have_function = false;
637 } else {
638 $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
643 * True return value means that we either found a plugin or a
644 * dynamically registered function. False means that we didn't and the
645 * compiler should now emit code to load custom function plugin for this
646 * tag.
648 if ($found) {
649 if ($have_function) {
650 $output = call_user_func_array($plugin_func, array($tag_args, &$this));
651 if($output != '') {
652 $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
653 . $output
654 . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
656 } else {
657 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
659 return true;
660 } else {
661 return false;
667 * compile block function tag
669 * sets $output to compiled block function tag
670 * @param string $tag_command
671 * @param string $tag_args
672 * @param string $tag_modifier
673 * @param string $output
674 * @return boolean
676 function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
678 if (substr($tag_command, 0, 1) == '/') {
679 $start_tag = false;
680 $tag_command = substr($tag_command, 1);
681 } else
682 $start_tag = true;
684 $found = false;
685 $have_function = true;
688 * First we check if the block function has already been registered
689 * or loaded from a plugin file.
691 if (isset($this->_plugins['block'][$tag_command])) {
692 $found = true;
693 $plugin_func = $this->_plugins['block'][$tag_command][0];
694 if (!is_callable($plugin_func)) {
695 $message = "block function '$tag_command' is not implemented";
696 $have_function = false;
700 * Otherwise we need to load plugin file and look for the function
701 * inside it.
703 else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
704 $found = true;
706 include_once $plugin_file;
708 $plugin_func = 'smarty_block_' . $tag_command;
709 if (!function_exists($plugin_func)) {
710 $message = "plugin function $plugin_func() not found in $plugin_file\n";
711 $have_function = false;
712 } else {
713 $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
718 if (!$found) {
719 return false;
720 } else if (!$have_function) {
721 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
722 return true;
726 * Even though we've located the plugin function, compilation
727 * happens only once, so the plugin will still need to be loaded
728 * at runtime for future requests.
730 $this->_add_plugin('block', $tag_command);
732 if ($start_tag)
733 $this->_push_tag($tag_command);
734 else
735 $this->_pop_tag($tag_command);
737 if ($start_tag) {
738 $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
739 $attrs = $this->_parse_attrs($tag_args);
740 $_cache_attrs='';
741 $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs);
742 $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
743 $output .= '$_block_repeat=true;' . $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat);';
744 $output .= 'while ($_block_repeat) { ob_start(); ?>';
745 } else {
746 $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); ';
747 $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat)';
748 if ($tag_modifier != '') {
749 $this->_parse_modifiers($_out_tag_text, $tag_modifier);
751 $output .= '$_block_repeat=false;echo ' . $_out_tag_text . '; } ';
752 $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
755 return true;
760 * compile custom function tag
762 * @param string $tag_command
763 * @param string $tag_args
764 * @param string $tag_modifier
765 * @return string
767 function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
769 $found = false;
770 $have_function = true;
773 * First we check if the custom function has already been registered
774 * or loaded from a plugin file.
776 if (isset($this->_plugins['function'][$tag_command])) {
777 $found = true;
778 $plugin_func = $this->_plugins['function'][$tag_command][0];
779 if (!is_callable($plugin_func)) {
780 $message = "custom function '$tag_command' is not implemented";
781 $have_function = false;
785 * Otherwise we need to load plugin file and look for the function
786 * inside it.
788 else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
789 $found = true;
791 include_once $plugin_file;
793 $plugin_func = 'smarty_function_' . $tag_command;
794 if (!function_exists($plugin_func)) {
795 $message = "plugin function $plugin_func() not found in $plugin_file\n";
796 $have_function = false;
797 } else {
798 $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true);
803 if (!$found) {
804 return false;
805 } else if (!$have_function) {
806 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
807 return true;
810 /* declare plugin to be loaded on display of the template that
811 we compile right now */
812 $this->_add_plugin('function', $tag_command);
814 $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
815 $attrs = $this->_parse_attrs($tag_args);
816 $_cache_attrs = '';
817 $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs);
819 $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
820 if($tag_modifier != '') {
821 $this->_parse_modifiers($output, $tag_modifier);
824 if($output != '') {
825 $output = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
826 . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
829 return true;
833 * compile a registered object tag
835 * @param string $tag_command
836 * @param array $attrs
837 * @param string $tag_modifier
838 * @return string
840 function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
842 if (substr($tag_command, 0, 1) == '/') {
843 $start_tag = false;
844 $tag_command = substr($tag_command, 1);
845 } else {
846 $start_tag = true;
849 list($object, $obj_comp) = explode('->', $tag_command);
851 $arg_list = array();
852 if(count($attrs)) {
853 $_assign_var = false;
854 foreach ($attrs as $arg_name => $arg_value) {
855 if($arg_name == 'assign') {
856 $_assign_var = $arg_value;
857 unset($attrs['assign']);
858 continue;
860 if (is_bool($arg_value))
861 $arg_value = $arg_value ? 'true' : 'false';
862 $arg_list[] = "'$arg_name' => $arg_value";
866 if($this->_reg_objects[$object][2]) {
867 // smarty object argument format
868 $args = "array(".implode(',', (array)$arg_list)."), \$this";
869 } else {
870 // traditional argument format
871 $args = implode(',', array_values($attrs));
872 if (empty($args)) {
873 $args = '';
877 $prefix = '';
878 $postfix = '';
879 $newline = '';
880 if(!is_object($this->_reg_objects[$object][0])) {
881 $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
882 } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
883 $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
884 } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
885 // method
886 if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
887 // block method
888 if ($start_tag) {
889 $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
890 $prefix .= "\$_block_repeat=true; \$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat); ";
891 $prefix .= "while (\$_block_repeat) { ob_start();";
892 $return = null;
893 $postfix = '';
894 } else {
895 $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); \$_block_repeat=false;";
896 $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat)";
897 $postfix = "} array_pop(\$this->_tag_stack);";
899 } else {
900 // non-block method
901 $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
903 } else {
904 // property
905 $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
908 if($return != null) {
909 if($tag_modifier != '') {
910 $this->_parse_modifiers($return, $tag_modifier);
913 if(!empty($_assign_var)) {
914 $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);";
915 } else {
916 $output = 'echo ' . $return . ';';
917 $newline = $this->_additional_newline;
919 } else {
920 $output = '';
923 return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
927 * Compile {insert ...} tag
929 * @param string $tag_args
930 * @return string
932 function _compile_insert_tag($tag_args)
934 $attrs = $this->_parse_attrs($tag_args);
935 $name = $this->_dequote($attrs['name']);
937 if (empty($name)) {
938 return $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
941 if (!preg_match('~^\w+$~', $name)) {
942 return $this->_syntax_error("'insert: 'name' must be an insert function name", E_USER_ERROR, __FILE__, __LINE__);
945 if (!empty($attrs['script'])) {
946 $delayed_loading = true;
947 } else {
948 $delayed_loading = false;
951 foreach ($attrs as $arg_name => $arg_value) {
952 if (is_bool($arg_value))
953 $arg_value = $arg_value ? 'true' : 'false';
954 $arg_list[] = "'$arg_name' => $arg_value";
957 $this->_add_plugin('insert', $name, $delayed_loading);
959 $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
961 return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
965 * Compile {include ...} tag
967 * @param string $tag_args
968 * @return string
970 function _compile_include_tag($tag_args)
972 $attrs = $this->_parse_attrs($tag_args);
973 $arg_list = array();
975 if (empty($attrs['file'])) {
976 $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
979 foreach ($attrs as $arg_name => $arg_value) {
980 if ($arg_name == 'file') {
981 $include_file = $arg_value;
982 continue;
983 } else if ($arg_name == 'assign') {
984 $assign_var = $arg_value;
985 continue;
987 if (is_bool($arg_value))
988 $arg_value = $arg_value ? 'true' : 'false';
989 $arg_list[] = "'$arg_name' => $arg_value";
992 $output = '<?php ';
994 if (isset($assign_var)) {
995 $output .= "ob_start();\n";
998 $output .=
999 "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
1002 $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
1003 $output .= "\$this->_smarty_include($_params);\n" .
1004 "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
1005 "unset(\$_smarty_tpl_vars);\n";
1007 if (isset($assign_var)) {
1008 $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
1011 $output .= ' ?>';
1013 return $output;
1018 * Compile {include ...} tag
1020 * @param string $tag_args
1021 * @return string
1023 function _compile_include_php_tag($tag_args)
1025 $attrs = $this->_parse_attrs($tag_args);
1027 if (empty($attrs['file'])) {
1028 $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
1031 $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
1032 $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
1034 $arg_list = array();
1035 foreach($attrs as $arg_name => $arg_value) {
1036 if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
1037 if(is_bool($arg_value))
1038 $arg_value = $arg_value ? 'true' : 'false';
1039 $arg_list[] = "'$arg_name' => $arg_value";
1043 $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
1045 return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
1050 * Compile {section ...} tag
1052 * @param string $tag_args
1053 * @return string
1055 function _compile_section_start($tag_args)
1057 $attrs = $this->_parse_attrs($tag_args);
1058 $arg_list = array();
1060 $output = '<?php ';
1061 $section_name = $attrs['name'];
1062 if (empty($section_name)) {
1063 $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
1066 $output .= "unset(\$this->_sections[$section_name]);\n";
1067 $section_props = "\$this->_sections[$section_name]";
1069 foreach ($attrs as $attr_name => $attr_value) {
1070 switch ($attr_name) {
1071 case 'loop':
1072 $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
1073 break;
1075 case 'show':
1076 if (is_bool($attr_value))
1077 $show_attr_value = $attr_value ? 'true' : 'false';
1078 else
1079 $show_attr_value = "(bool)$attr_value";
1080 $output .= "{$section_props}['show'] = $show_attr_value;\n";
1081 break;
1083 case 'name':
1084 $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
1085 break;
1087 case 'max':
1088 case 'start':
1089 $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
1090 break;
1092 case 'step':
1093 $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
1094 break;
1096 default:
1097 $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
1098 break;
1102 if (!isset($attrs['show']))
1103 $output .= "{$section_props}['show'] = true;\n";
1105 if (!isset($attrs['loop']))
1106 $output .= "{$section_props}['loop'] = 1;\n";
1108 if (!isset($attrs['max']))
1109 $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
1110 else
1111 $output .= "if ({$section_props}['max'] < 0)\n" .
1112 " {$section_props}['max'] = {$section_props}['loop'];\n";
1114 if (!isset($attrs['step']))
1115 $output .= "{$section_props}['step'] = 1;\n";
1117 if (!isset($attrs['start']))
1118 $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
1119 else {
1120 $output .= "if ({$section_props}['start'] < 0)\n" .
1121 " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
1122 "else\n" .
1123 " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
1126 $output .= "if ({$section_props}['show']) {\n";
1127 if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
1128 $output .= " {$section_props}['total'] = {$section_props}['loop'];\n";
1129 } else {
1130 $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";
1132 $output .= " if ({$section_props}['total'] == 0)\n" .
1133 " {$section_props}['show'] = false;\n" .
1134 "} else\n" .
1135 " {$section_props}['total'] = 0;\n";
1137 $output .= "if ({$section_props}['show']):\n";
1138 $output .= "
1139 for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
1140 {$section_props}['iteration'] <= {$section_props}['total'];
1141 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
1142 $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
1143 $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
1144 $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
1145 $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n";
1146 $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n";
1148 $output .= "?>";
1150 return $output;
1155 * Compile {foreach ...} tag.
1157 * @param string $tag_args
1158 * @return string
1160 function _compile_foreach_start($tag_args)
1162 $attrs = $this->_parse_attrs($tag_args);
1163 $arg_list = array();
1165 if (empty($attrs['from'])) {
1166 return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
1168 $from = $attrs['from'];
1170 if (empty($attrs['item'])) {
1171 return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
1173 $item = $this->_dequote($attrs['item']);
1174 if (!preg_match('~^\w+$~', $item)) {
1175 return $this->_syntax_error("foreach: 'item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1178 if (isset($attrs['key'])) {
1179 $key = $this->_dequote($attrs['key']);
1180 if (!preg_match('~^\w+$~', $key)) {
1181 return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1183 $key_part = "\$this->_tpl_vars['$key'] => ";
1184 } else {
1185 $key = null;
1186 $key_part = '';
1189 if (isset($attrs['name'])) {
1190 $name = $attrs['name'];
1191 } else {
1192 $name = null;
1195 $output = '<?php ';
1196 $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
1197 if (isset($name)) {
1198 $foreach_props = "\$this->_foreach[$name]";
1199 $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
1200 $output .= "if ({$foreach_props}['total'] > 0):\n";
1201 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1202 $output .= " {$foreach_props}['iteration']++;\n";
1203 } else {
1204 $output .= "if (count(\$_from)):\n";
1205 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1207 $output .= '?>';
1209 return $output;
1214 * Compile {capture} .. {/capture} tags
1216 * @param boolean $start true if this is the {capture} tag
1217 * @param string $tag_args
1218 * @return string
1221 function _compile_capture_tag($start, $tag_args = '')
1223 $attrs = $this->_parse_attrs($tag_args);
1225 if ($start) {
1226 $buffer = isset($attrs['name']) ? $attrs['name'] : "'default'";
1227 $assign = isset($attrs['assign']) ? $attrs['assign'] : null;
1228 $append = isset($attrs['append']) ? $attrs['append'] : null;
1230 $output = "<?php ob_start(); ?>";
1231 $this->_capture_stack[] = array($buffer, $assign, $append);
1232 } else {
1233 list($buffer, $assign, $append) = array_pop($this->_capture_stack);
1234 $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
1235 if (isset($assign)) {
1236 $output .= " \$this->assign($assign, ob_get_contents());";
1238 if (isset($append)) {
1239 $output .= " \$this->append($append, ob_get_contents());";
1241 $output .= "ob_end_clean(); ?>";
1244 return $output;
1248 * Compile {if ...} tag
1250 * @param string $tag_args
1251 * @param boolean $elseif if true, uses elseif instead of if
1252 * @return string
1254 function _compile_if_tag($tag_args, $elseif = false)
1257 /* Tokenize args for 'if' tag. */
1258 preg_match_all('~(?>
1259 ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
1260 ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)? | # var or quoted string
1261 \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token
1262 \b\w+\b | # valid word token
1263 \S+ # anything else
1264 )~x', $tag_args, $match);
1266 $tokens = $match[0];
1268 if(empty($tokens)) {
1269 $_error_msg = $elseif ? "'elseif'" : "'if'";
1270 $_error_msg .= ' statement requires arguments';
1271 $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__);
1275 // make sure we have balanced parenthesis
1276 $token_count = array_count_values($tokens);
1277 if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1278 $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
1281 $is_arg_stack = array();
1283 for ($i = 0; $i < count($tokens); $i++) {
1285 $token = &$tokens[$i];
1287 switch (strtolower($token)) {
1288 case '!':
1289 case '%':
1290 case '!==':
1291 case '==':
1292 case '===':
1293 case '>':
1294 case '<':
1295 case '!=':
1296 case '<>':
1297 case '<<':
1298 case '>>':
1299 case '<=':
1300 case '>=':
1301 case '&&':
1302 case '||':
1303 case '|':
1304 case '^':
1305 case '&':
1306 case '~':
1307 case ')':
1308 case ',':
1309 case '+':
1310 case '-':
1311 case '*':
1312 case '/':
1313 case '@':
1314 break;
1316 case 'eq':
1317 $token = '==';
1318 break;
1320 case 'ne':
1321 case 'neq':
1322 $token = '!=';
1323 break;
1325 case 'lt':
1326 $token = '<';
1327 break;
1329 case 'le':
1330 case 'lte':
1331 $token = '<=';
1332 break;
1334 case 'gt':
1335 $token = '>';
1336 break;
1338 case 'ge':
1339 case 'gte':
1340 $token = '>=';
1341 break;
1343 case 'and':
1344 $token = '&&';
1345 break;
1347 case 'or':
1348 $token = '||';
1349 break;
1351 case 'not':
1352 $token = '!';
1353 break;
1355 case 'mod':
1356 $token = '%';
1357 break;
1359 case '(':
1360 array_push($is_arg_stack, $i);
1361 break;
1363 case 'is':
1364 /* If last token was a ')', we operate on the parenthesized
1365 expression. The start of the expression is on the stack.
1366 Otherwise, we operate on the last encountered token. */
1367 if ($tokens[$i-1] == ')') {
1368 $is_arg_start = array_pop($is_arg_stack);
1369 if ($is_arg_start != 0) {
1370 if (preg_match('~^' . $this->_func_regexp . '$~', $tokens[$is_arg_start-1])) {
1371 $is_arg_start--;
1374 } else
1375 $is_arg_start = $i-1;
1376 /* Construct the argument for 'is' expression, so it knows
1377 what to operate on. */
1378 $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1380 /* Pass all tokens from next one until the end to the
1381 'is' expression parsing function. The function will
1382 return modified tokens, where the first one is the result
1383 of the 'is' expression and the rest are the tokens it
1384 didn't touch. */
1385 $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
1387 /* Replace the old tokens with the new ones. */
1388 array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1390 /* Adjust argument start so that it won't change from the
1391 current position for the next iteration. */
1392 $i = $is_arg_start;
1393 break;
1395 default:
1396 if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) {
1397 // function call
1398 if($this->security &&
1399 !in_array($token, $this->security_settings['IF_FUNCS'])) {
1400 $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1402 } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && (strpos('+-*/^%&|', substr($token, -1)) === false) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
1403 // variable function call
1404 $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1405 } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {
1406 // object or variable
1407 $token = $this->_parse_var_props($token);
1408 } elseif(is_numeric($token)) {
1409 // number, skip it
1410 } else {
1411 $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
1413 break;
1417 if ($elseif)
1418 return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1419 else
1420 return '<?php if ('.implode(' ', $tokens).'): ?>';
1424 function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1425 $arg_list = array();
1427 if (isset($type) && isset($name)
1428 && isset($this->_plugins[$type])
1429 && isset($this->_plugins[$type][$name])
1430 && empty($this->_plugins[$type][$name][4])
1431 && is_array($this->_plugins[$type][$name][5])
1433 /* we have a list of parameters that should be cached */
1434 $_cache_attrs = $this->_plugins[$type][$name][5];
1435 $_count = $this->_cache_attrs_count++;
1436 $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1438 } else {
1439 /* no parameters are cached */
1440 $_cache_attrs = null;
1443 foreach ($attrs as $arg_name => $arg_value) {
1444 if (is_bool($arg_value))
1445 $arg_value = $arg_value ? 'true' : 'false';
1446 if (is_null($arg_value))
1447 $arg_value = 'null';
1448 if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1449 $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1450 } else {
1451 $arg_list[] = "'$arg_name' => $arg_value";
1454 return $arg_list;
1458 * Parse is expression
1460 * @param string $is_arg
1461 * @param array $tokens
1462 * @return array
1464 function _parse_is_expr($is_arg, $tokens)
1466 $expr_end = 0;
1467 $negate_expr = false;
1469 if (($first_token = array_shift($tokens)) == 'not') {
1470 $negate_expr = true;
1471 $expr_type = array_shift($tokens);
1472 } else
1473 $expr_type = $first_token;
1475 switch ($expr_type) {
1476 case 'even':
1477 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1478 $expr_end++;
1479 $expr_arg = $tokens[$expr_end++];
1480 $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1481 } else
1482 $expr = "!(1 & $is_arg)";
1483 break;
1485 case 'odd':
1486 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1487 $expr_end++;
1488 $expr_arg = $tokens[$expr_end++];
1489 $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1490 } else
1491 $expr = "(1 & $is_arg)";
1492 break;
1494 case 'div':
1495 if (@$tokens[$expr_end] == 'by') {
1496 $expr_end++;
1497 $expr_arg = $tokens[$expr_end++];
1498 $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1499 } else {
1500 $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1502 break;
1504 default:
1505 $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1506 break;
1509 if ($negate_expr) {
1510 $expr = "!($expr)";
1513 array_splice($tokens, 0, $expr_end, $expr);
1515 return $tokens;
1520 * Parse attribute string
1522 * @param string $tag_args
1523 * @return array
1525 function _parse_attrs($tag_args)
1528 /* Tokenize tag attributes. */
1529 preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
1530 )+ |
1532 ~x', $tag_args, $match);
1533 $tokens = $match[0];
1535 $attrs = array();
1536 /* Parse state:
1537 0 - expecting attribute name
1538 1 - expecting '='
1539 2 - expecting attribute value (not '=') */
1540 $state = 0;
1542 foreach ($tokens as $token) {
1543 switch ($state) {
1544 case 0:
1545 /* If the token is a valid identifier, we set attribute name
1546 and go to state 1. */
1547 if (preg_match('~^\w+$~', $token)) {
1548 $attr_name = $token;
1549 $state = 1;
1550 } else
1551 $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1552 break;
1554 case 1:
1555 /* If the token is '=', then we go to state 2. */
1556 if ($token == '=') {
1557 $state = 2;
1558 } else
1559 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1560 break;
1562 case 2:
1563 /* If token is not '=', we set the attribute value and go to
1564 state 0. */
1565 if ($token != '=') {
1566 /* We booleanize the token if it's a non-quoted possible
1567 boolean value. */
1568 if (preg_match('~^(on|yes|true)$~', $token)) {
1569 $token = 'true';
1570 } else if (preg_match('~^(off|no|false)$~', $token)) {
1571 $token = 'false';
1572 } else if ($token == 'null') {
1573 $token = 'null';
1574 } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
1575 /* treat integer literally */
1576 } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {
1577 /* treat as a string, double-quote it escaping quotes */
1578 $token = '"'.addslashes($token).'"';
1581 $attrs[$attr_name] = $token;
1582 $state = 0;
1583 } else
1584 $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1585 break;
1587 $last_token = $token;
1590 if($state != 0) {
1591 if($state == 1) {
1592 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1593 } else {
1594 $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
1598 $this->_parse_vars_props($attrs);
1600 return $attrs;
1604 * compile multiple variables and section properties tokens into
1605 * PHP code
1607 * @param array $tokens
1609 function _parse_vars_props(&$tokens)
1611 foreach($tokens as $key => $val) {
1612 $tokens[$key] = $this->_parse_var_props($val);
1617 * compile single variable and section properties token into
1618 * PHP code
1620 * @param string $val
1621 * @param string $tag_attrs
1622 * @return string
1624 function _parse_var_props($val)
1626 $val = trim($val);
1628 if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {
1629 // $ variable or object
1630 $return = $this->_parse_var($match[1]);
1631 $modifiers = $match[2];
1632 if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
1633 $_default_mod_string = implode('|',(array)$this->default_modifiers);
1634 $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
1636 $this->_parse_modifiers($return, $modifiers);
1637 return $return;
1638 } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1639 // double quoted text
1640 preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1641 $return = $this->_expand_quoted_text($match[1]);
1642 if($match[2] != '') {
1643 $this->_parse_modifiers($return, $match[2]);
1645 return $return;
1647 elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1648 // numerical constant
1649 preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1650 if($match[2] != '') {
1651 $this->_parse_modifiers($match[1], $match[2]);
1652 return $match[1];
1655 elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1656 // single quoted text
1657 preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1658 if($match[2] != '') {
1659 $this->_parse_modifiers($match[1], $match[2]);
1660 return $match[1];
1663 elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1664 // config var
1665 return $this->_parse_conf_var($val);
1667 elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1668 // section var
1669 return $this->_parse_section_prop($val);
1671 elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
1672 // literal string
1673 return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"');
1675 return $val;
1679 * expand quoted text with embedded variables
1681 * @param string $var_expr
1682 * @return string
1684 function _expand_quoted_text($var_expr)
1686 // if contains unescaped $, expand it
1687 if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) {
1688 $_match = $_match[0];
1689 $_replace = array();
1690 foreach($_match as $_var) {
1691 $_replace[$_var] = '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."';
1693 $var_expr = strtr($var_expr, $_replace);
1694 $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr);
1695 } else {
1696 $_return = $var_expr;
1698 // replace double quoted literal string with single quotes
1699 $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return);
1700 return $_return;
1704 * parse variable expression into PHP code
1706 * @param string $var_expr
1707 * @param string $output
1708 * @return string
1710 function _parse_var($var_expr)
1712 $_has_math = false;
1713 $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
1715 if(count($_math_vars) > 1) {
1716 $_first_var = "";
1717 $_complete_var = "";
1718 $_output = "";
1719 // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
1720 foreach($_math_vars as $_k => $_math_var) {
1721 $_math_var = $_math_vars[$_k];
1723 if(!empty($_math_var) || is_numeric($_math_var)) {
1724 // hit a math operator, so process the stuff which came before it
1725 if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
1726 $_has_math = true;
1727 if(!empty($_complete_var) || is_numeric($_complete_var)) {
1728 $_output .= $this->_parse_var($_complete_var);
1731 // just output the math operator to php
1732 $_output .= $_math_var;
1734 if(empty($_first_var))
1735 $_first_var = $_complete_var;
1737 $_complete_var = "";
1738 } else {
1739 $_complete_var .= $_math_var;
1743 if($_has_math) {
1744 if(!empty($_complete_var) || is_numeric($_complete_var))
1745 $_output .= $this->_parse_var($_complete_var);
1747 // get the modifiers working (only the last var from math + modifier is left)
1748 $var_expr = $_complete_var;
1752 // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
1753 if(is_numeric(substr($var_expr, 0, 1)))
1754 $_var_ref = $var_expr;
1755 else
1756 $_var_ref = substr($var_expr, 1);
1758 if(!$_has_math) {
1760 // get [foo] and .foo and ->foo and (...) pieces
1761 preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match);
1763 $_indexes = $match[0];
1764 $_var_name = array_shift($_indexes);
1766 /* Handle $smarty.* variable references as a special case. */
1767 if ($_var_name == 'smarty') {
1769 * If the reference could be compiled, use the compiled output;
1770 * otherwise, fall back on the $smarty variable generated at
1771 * run-time.
1773 if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
1774 $_output = $smarty_ref;
1775 } else {
1776 $_var_name = substr(array_shift($_indexes), 1);
1777 $_output = "\$this->_smarty_vars['$_var_name']";
1779 } elseif(is_numeric($_var_name) && is_numeric(substr($var_expr, 0, 1))) {
1780 // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
1781 if(count($_indexes) > 0)
1783 $_var_name .= implode("", $_indexes);
1784 $_indexes = array();
1786 $_output = $_var_name;
1787 } else {
1788 $_output = "\$this->_tpl_vars['$_var_name']";
1791 foreach ($_indexes as $_index) {
1792 if (substr($_index, 0, 1) == '[') {
1793 $_index = substr($_index, 1, -1);
1794 if (is_numeric($_index)) {
1795 $_output .= "[$_index]";
1796 } elseif (substr($_index, 0, 1) == '$') {
1797 if (strpos($_index, '.') !== false) {
1798 $_output .= '[' . $this->_parse_var($_index) . ']';
1799 } else {
1800 $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
1802 } else {
1803 $_var_parts = explode('.', $_index);
1804 $_var_section = $_var_parts[0];
1805 $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
1806 $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
1808 } else if (substr($_index, 0, 1) == '.') {
1809 if (substr($_index, 1, 1) == '$')
1810 $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
1811 else
1812 $_output .= "['" . substr($_index, 1) . "']";
1813 } else if (substr($_index,0,2) == '->') {
1814 if(substr($_index,2,2) == '__') {
1815 $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1816 } elseif($this->security && substr($_index, 2, 1) == '_') {
1817 $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1818 } elseif (substr($_index, 2, 1) == '$') {
1819 if ($this->security) {
1820 $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1821 } else {
1822 $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
1824 } else {
1825 $_output .= $_index;
1827 } elseif (substr($_index, 0, 1) == '(') {
1828 $_index = $this->_parse_parenth_args($_index);
1829 $_output .= $_index;
1830 } else {
1831 $_output .= $_index;
1836 return $_output;
1840 * parse arguments in function call parenthesis
1842 * @param string $parenth_args
1843 * @return string
1845 function _parse_parenth_args($parenth_args)
1847 preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match);
1848 $orig_vals = $match = $match[0];
1849 $this->_parse_vars_props($match);
1850 $replace = array();
1851 for ($i = 0, $count = count($match); $i < $count; $i++) {
1852 $replace[$orig_vals[$i]] = $match[$i];
1854 return strtr($parenth_args, $replace);
1858 * parse configuration variable expression into PHP code
1860 * @param string $conf_var_expr
1862 function _parse_conf_var($conf_var_expr)
1864 $parts = explode('|', $conf_var_expr, 2);
1865 $var_ref = $parts[0];
1866 $modifiers = isset($parts[1]) ? $parts[1] : '';
1868 $var_name = substr($var_ref, 1, -1);
1870 $output = "\$this->_config[0]['vars']['$var_name']";
1872 $this->_parse_modifiers($output, $modifiers);
1874 return $output;
1878 * parse section property expression into PHP code
1880 * @param string $section_prop_expr
1881 * @return string
1883 function _parse_section_prop($section_prop_expr)
1885 $parts = explode('|', $section_prop_expr, 2);
1886 $var_ref = $parts[0];
1887 $modifiers = isset($parts[1]) ? $parts[1] : '';
1889 preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1890 $section_name = $match[1];
1891 $prop_name = $match[2];
1893 $output = "\$this->_sections['$section_name']['$prop_name']";
1895 $this->_parse_modifiers($output, $modifiers);
1897 return $output;
1902 * parse modifier chain into PHP code
1904 * sets $output to parsed modified chain
1905 * @param string $output
1906 * @param string $modifier_string
1908 function _parse_modifiers(&$output, $modifier_string)
1910 preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
1911 list(, $_modifiers, $modifier_arg_strings) = $_match;
1913 for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
1914 $_modifier_name = $_modifiers[$_i];
1916 if($_modifier_name == 'smarty') {
1917 // skip smarty modifier
1918 continue;
1921 preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
1922 $_modifier_args = $_match[1];
1924 if (substr($_modifier_name, 0, 1) == '@') {
1925 $_map_array = false;
1926 $_modifier_name = substr($_modifier_name, 1);
1927 } else {
1928 $_map_array = true;
1931 if (empty($this->_plugins['modifier'][$_modifier_name])
1932 && !$this->_get_plugin_filepath('modifier', $_modifier_name)
1933 && function_exists($_modifier_name)) {
1934 if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
1935 $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
1936 } else {
1937 $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false);
1940 $this->_add_plugin('modifier', $_modifier_name);
1942 $this->_parse_vars_props($_modifier_args);
1944 if($_modifier_name == 'default') {
1945 // supress notifications of default modifier vars and args
1946 if(substr($output, 0, 1) == '$') {
1947 $output = '@' . $output;
1949 if(isset($_modifier_args[0]) && substr($_modifier_args[0], 0, 1) == '$') {
1950 $_modifier_args[0] = '@' . $_modifier_args[0];
1953 if (count($_modifier_args) > 0)
1954 $_modifier_args = ', '.implode(', ', $_modifier_args);
1955 else
1956 $_modifier_args = '';
1958 if ($_map_array) {
1959 $output = "((is_array(\$_tmp=$output)) ? \$this->_run_mod_handler('$_modifier_name', true, \$_tmp$_modifier_args) : " . $this->_compile_plugin_call('modifier', $_modifier_name) . "(\$_tmp$_modifier_args))";
1961 } else {
1963 $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
1971 * add plugin
1973 * @param string $type
1974 * @param string $name
1975 * @param boolean? $delayed_loading
1977 function _add_plugin($type, $name, $delayed_loading = null)
1979 if (!isset($this->_plugin_info[$type])) {
1980 $this->_plugin_info[$type] = array();
1982 if (!isset($this->_plugin_info[$type][$name])) {
1983 $this->_plugin_info[$type][$name] = array($this->_current_file,
1984 $this->_current_line_no,
1985 $delayed_loading);
1991 * Compiles references of type $smarty.foo
1993 * @param string $indexes
1994 * @return string
1996 function _compile_smarty_ref(&$indexes)
1998 /* Extract the reference name. */
1999 $_ref = substr($indexes[0], 1);
2000 foreach($indexes as $_index_no=>$_index) {
2001 if (substr($_index, 0, 1) != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) {
2002 $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2006 switch ($_ref) {
2007 case 'now':
2008 $compiled_ref = 'time()';
2009 $_max_index = 1;
2010 break;
2012 case 'foreach':
2013 array_shift($indexes);
2014 $_var = $this->_parse_var_props(substr($indexes[0], 1));
2015 $_propname = substr($indexes[1], 1);
2016 $_max_index = 1;
2017 switch ($_propname) {
2018 case 'index':
2019 array_shift($indexes);
2020 $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)";
2021 break;
2023 case 'first':
2024 array_shift($indexes);
2025 $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)";
2026 break;
2028 case 'last':
2029 array_shift($indexes);
2030 $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])";
2031 break;
2033 case 'show':
2034 array_shift($indexes);
2035 $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)";
2036 break;
2038 default:
2039 unset($_max_index);
2040 $compiled_ref = "\$this->_foreach[$_var]";
2042 break;
2044 case 'section':
2045 array_shift($indexes);
2046 $_var = $this->_parse_var_props(substr($indexes[0], 1));
2047 $compiled_ref = "\$this->_sections[$_var]";
2048 break;
2050 case 'get':
2051 if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2052 $this->_syntax_error("(secure mode) super global access not permitted",
2053 E_USER_WARNING, __FILE__, __LINE__);
2054 return;
2056 $compiled_ref = "\$_GET";
2057 break;
2059 case 'post':
2060 if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2061 $this->_syntax_error("(secure mode) super global access not permitted",
2062 E_USER_WARNING, __FILE__, __LINE__);
2063 return;
2065 $compiled_ref = "\$_POST";
2066 break;
2068 case 'cookies':
2069 if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2070 $this->_syntax_error("(secure mode) super global access not permitted",
2071 E_USER_WARNING, __FILE__, __LINE__);
2072 return;
2074 $compiled_ref = "\$_COOKIE";
2075 break;
2077 case 'env':
2078 if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2079 $this->_syntax_error("(secure mode) super global access not permitted",
2080 E_USER_WARNING, __FILE__, __LINE__);
2081 return;
2083 $compiled_ref = "\$_ENV";
2084 break;
2086 case 'server':
2087 if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2088 $this->_syntax_error("(secure mode) super global access not permitted",
2089 E_USER_WARNING, __FILE__, __LINE__);
2090 return;
2092 $compiled_ref = "\$_SERVER";
2093 break;
2095 case 'session':
2096 if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2097 $this->_syntax_error("(secure mode) super global access not permitted",
2098 E_USER_WARNING, __FILE__, __LINE__);
2099 return;
2101 $compiled_ref = "\$_SESSION";
2102 break;
2105 * These cases are handled either at run-time or elsewhere in the
2106 * compiler.
2108 case 'request':
2109 if ($this->security && !$this->security_settings['ALLOW_SUPER_GLOBALS']) {
2110 $this->_syntax_error("(secure mode) super global access not permitted",
2111 E_USER_WARNING, __FILE__, __LINE__);
2112 return;
2114 if ($this->request_use_auto_globals) {
2115 $compiled_ref = "\$_REQUEST";
2116 break;
2117 } else {
2118 $this->_init_smarty_vars = true;
2120 return null;
2122 case 'capture':
2123 return null;
2125 case 'template':
2126 $compiled_ref = "'" . addslashes($this->_current_file) . "'";
2127 $_max_index = 1;
2128 break;
2130 case 'version':
2131 $compiled_ref = "'$this->_version'";
2132 $_max_index = 1;
2133 break;
2135 case 'const':
2136 if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
2137 $this->_syntax_error("(secure mode) constants not permitted",
2138 E_USER_WARNING, __FILE__, __LINE__);
2139 return;
2141 array_shift($indexes);
2142 if (preg_match('!^\.\w+$!', $indexes[0])) {
2143 $compiled_ref = '@' . substr($indexes[0], 1);
2144 } else {
2145 $_val = $this->_parse_var_props(substr($indexes[0], 1));
2146 $compiled_ref = '@constant(' . $_val . ')';
2148 $_max_index = 1;
2149 break;
2151 case 'config':
2152 $compiled_ref = "\$this->_config[0]['vars']";
2153 $_max_index = 3;
2154 break;
2156 case 'ldelim':
2157 $compiled_ref = "'$this->left_delimiter'";
2158 break;
2160 case 'rdelim':
2161 $compiled_ref = "'$this->right_delimiter'";
2162 break;
2164 default:
2165 $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
2166 break;
2169 if (isset($_max_index) && count($indexes) > $_max_index) {
2170 $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2173 array_shift($indexes);
2174 return $compiled_ref;
2178 * compiles call to plugin of type $type with name $name
2179 * returns a string containing the function-name or method call
2180 * without the paramter-list that would have follow to make the
2181 * call valid php-syntax
2183 * @param string $type
2184 * @param string $name
2185 * @return string
2187 function _compile_plugin_call($type, $name) {
2188 if (isset($this->_plugins[$type][$name])) {
2189 /* plugin loaded */
2190 if (is_array($this->_plugins[$type][$name][0])) {
2191 return ((is_object($this->_plugins[$type][$name][0][0])) ?
2192 "\$this->_plugins['$type']['$name'][0][0]->" /* method callback */
2193 : (string)($this->_plugins[$type][$name][0][0]).'::' /* class callback */
2194 ). $this->_plugins[$type][$name][0][1];
2196 } else {
2197 /* function callback */
2198 return $this->_plugins[$type][$name][0];
2201 } else {
2202 /* plugin not loaded -> auto-loadable-plugin */
2203 return 'smarty_'.$type.'_'.$name;
2209 * load pre- and post-filters
2211 function _load_filters()
2213 if (count($this->_plugins['prefilter']) > 0) {
2214 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
2215 if ($prefilter === false) {
2216 unset($this->_plugins['prefilter'][$filter_name]);
2217 $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
2218 require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2219 smarty_core_load_plugins($_params, $this);
2223 if (count($this->_plugins['postfilter']) > 0) {
2224 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
2225 if ($postfilter === false) {
2226 unset($this->_plugins['postfilter'][$filter_name]);
2227 $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
2228 require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2229 smarty_core_load_plugins($_params, $this);
2237 * Quote subpattern references
2239 * @param string $string
2240 * @return string
2242 function _quote_replace($string)
2244 return strtr($string, array('\\' => '\\\\', '$' => '\\$'));
2248 * display Smarty syntax error
2250 * @param string $error_msg
2251 * @param integer $error_type
2252 * @param string $file
2253 * @param integer $line
2255 function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
2257 $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
2262 * check if the compilation changes from cacheable to
2263 * non-cacheable state with the beginning of the current
2264 * plugin. return php-code to reflect the transition.
2265 * @return string
2267 function _push_cacheable_state($type, $name) {
2268 $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2269 if ($_cacheable
2270 || 0<$this->_cacheable_state++) return '';
2271 if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
2272 $_ret = 'if ($this->caching && !$this->_cache_including): echo \'{nocache:'
2273 . $this->_cache_serial . '#' . $this->_nocache_count
2274 . '}\'; endif;';
2275 return $_ret;
2280 * check if the compilation changes from non-cacheable to
2281 * cacheable state with the end of the current plugin return
2282 * php-code to reflect the transition.
2283 * @return string
2285 function _pop_cacheable_state($type, $name) {
2286 $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2287 if ($_cacheable
2288 || --$this->_cacheable_state>0) return '';
2289 return 'if ($this->caching && !$this->_cache_including): echo \'{/nocache:'
2290 . $this->_cache_serial . '#' . ($this->_nocache_count++)
2291 . '}\'; endif;';
2296 * push opening tag-name, file-name and line-number on the tag-stack
2297 * @param string the opening tag's name
2299 function _push_tag($open_tag)
2301 array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
2305 * pop closing tag-name
2306 * raise an error if this stack-top doesn't match with the closing tag
2307 * @param string the closing tag's name
2308 * @return string the opening tag's name
2310 function _pop_tag($close_tag)
2312 $message = '';
2313 if (count($this->_tag_stack)>0) {
2314 list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
2315 if ($close_tag == $_open_tag) {
2316 return $_open_tag;
2318 if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
2319 return $this->_pop_tag($close_tag);
2321 if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
2322 $this->_pop_tag($close_tag);
2323 return $_open_tag;
2325 if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
2326 $this->_pop_tag($close_tag);
2327 return $_open_tag;
2329 if ($_open_tag == 'else' || $_open_tag == 'elseif') {
2330 $_open_tag = 'if';
2331 } elseif ($_open_tag == 'sectionelse') {
2332 $_open_tag = 'section';
2333 } elseif ($_open_tag == 'foreachelse') {
2334 $_open_tag = 'foreach';
2336 $message = " expected {/$_open_tag} (opened line $_line_no).";
2338 $this->_syntax_error("mismatched tag {/$close_tag}.$message",
2339 E_USER_ERROR, __FILE__, __LINE__);
2345 * compare to values by their string length
2347 * @access private
2348 * @param string $a
2349 * @param string $b
2350 * @return 0|-1|1
2352 function _smarty_sort_length($a, $b)
2354 if($a == $b)
2355 return 0;
2357 if(strlen($a) == strlen($b))
2358 return ($a > $b) ? -1 : 1;
2360 return (strlen($a) > strlen($b)) ? -1 : 1;
2364 /* vim: set et: */