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 * You may contact the authors of Smarty by e-mail at:
27 * Director of Technology, ispi
28 * 237 S. 70th suite 220
31 * The latest version of Smarty can be obtained from:
32 * http://smarty.php.net/
34 * @link http://smarty.php.net/
35 * @author Monte Ohrt <monte@ispi.net>
36 * @author Andrei Zmievski <andrei@php.net>
38 * @copyright 2001-2003 ispi of Lincoln, Inc.
42 /* $Id: Smarty_Compiler.class.php,v 1.286 2003/10/08 23:43:34 mohrt Exp $ */
45 * Template compiling class
48 class Smarty_Compiler
extends Smarty
{
54 var $_sectionelse_stack = array(); // keeps track of whether section had 'else' part
55 var $_foreachelse_stack = array(); // keeps track of whether foreach had 'else' part
56 var $_literal_blocks = array(); // keeps literal template blocks
57 var $_php_blocks = array(); // keeps php code blocks
58 var $_current_file = null; // the current template being compiled
59 var $_current_line_no = 1; // line number for error messages
60 var $_capture_stack = array(); // keeps track of nested capture buffers
61 var $_plugin_info = array(); // keeps track of plugins to load
62 var $_init_smarty_vars = false;
63 var $_permitted_tokens = array('true','false','yes','no','on','off','null');
64 var $_output_type = 'php';
65 var $_db_qstr_regexp = null; // regexps are setup in the constructor
66 var $_si_qstr_regexp = null;
67 var $_qstr_regexp = null;
68 var $_func_regexp = null;
69 var $_var_bracket_regexp = null;
70 var $_dvar_guts_regexp = null;
71 var $_dvar_regexp = null;
72 var $_cvar_regexp = null;
73 var $_svar_regexp = null;
74 var $_avar_regexp = null;
75 var $_mod_regexp = null;
76 var $_var_regexp = null;
77 var $_parenth_param_regexp = null;
78 var $_func_call_regexp = null;
79 var $_obj_ext_regexp = null;
80 var $_obj_start_regexp = null;
81 var $_obj_params_regexp = null;
82 var $_obj_call_regexp = null;
84 var $_cacheable_state = 0;
85 var $_cache_attrs_count = 0;
86 var $_nocache_count = 0;
87 var $_cache_serial = null;
88 var $_cache_include = null;
90 var $_strip_depth = 0;
91 var $_additional_newline = "\n";
95 * The class constructor.
97 function Smarty_Compiler()
99 // matches double quoted strings:
102 $this->_db_qstr_regexp
= '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
104 // matches single quoted strings:
107 $this->_si_qstr_regexp
= '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
109 // matches single or double quoted strings
110 $this->_qstr_regexp
= '(?:' . $this->_db_qstr_regexp
. '|' . $this->_si_qstr_regexp
. ')';
112 // matches bracket portion of vars
116 $this->_var_bracket_regexp
= '\[\$?[\w\.]+\]';
118 // matches $ vars (not objects):
125 // $foo[5].bar[$foobar][4]
126 $this->_dvar_math_regexp
= '[\+\-\*\/\%]';
127 $this->_dvar_math_var_regexp
= '[\$\w\.\+\-\*\/\%\d\|\>\[\]]';
128 $this->_dvar_num_var_regexp
= '\-?\d+(?:\.\d+)?' . $this->_dvar_math_var_regexp
;
129 $this->_dvar_guts_regexp
= '\w+(?:' . $this->_var_bracket_regexp
130 . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp
. ')*)*(?:' . $this->_dvar_math_regexp
. '(?:\-?\d+(?:\.\d+)?|' . $this->_dvar_math_var_regexp
. '*))?';
131 $this->_dvar_regexp
= '\$' . $this->_dvar_guts_regexp
;
133 // matches config vars:
136 $this->_cvar_regexp
= '\#\w+\#';
138 // matches section vars:
140 $this->_svar_regexp
= '\%\w+\.\w+\%';
142 // matches all valid variables (no quotes, no modifiers)
143 $this->_avar_regexp
= '(?:' . $this->_dvar_regexp
. '|'
144 . $this->_cvar_regexp
. '|' . $this->_svar_regexp
. ')';
146 // matches valid variable syntax:
153 $this->_var_regexp
= '(?:' . $this->_avar_regexp
. '|' . $this->_qstr_regexp
. ')';
155 // matches valid object call (no objects allowed in parameters):
159 // $foo->bar($foo, $bar, "text")
160 // $foo->bar($foo, "foo")
162 // $foo->bar->foo->bar()
163 $this->_obj_ext_regexp
= '\->(?:\$?' . $this->_dvar_guts_regexp
. ')';
164 $this->_obj_params_regexp
= '\((?:\w+|'
165 . $this->_var_regexp
. '(?:\s*,\s*(?:(?:\w+|'
166 . $this->_var_regexp
. ')))*)?\)';
167 $this->_obj_start_regexp
= '(?:' . $this->_dvar_regexp
. '(?:' . $this->_obj_ext_regexp
. ')+)';
168 $this->_obj_call_regexp
= '(?:' . $this->_obj_start_regexp
. '(?:' . $this->_obj_params_regexp
. ')?)';
170 // matches valid modifier syntax:
175 // |foo:"bar":$foobar
178 $this->_mod_regexp
= '(?:\|@?\w+(?::(?>-?\w+|'
179 . $this->_obj_call_regexp
. '|' . $this->_avar_regexp
. '|' . $this->_qstr_regexp
.'))*)';
181 // matches valid function name:
184 $this->_func_regexp
= '[a-zA-Z_]\w*';
186 // matches valid registered object:
188 $this->_reg_obj_regexp
= '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
190 // matches valid parameter values:
199 $this->_param_regexp
= '(?:\s*(?:' . $this->_obj_call_regexp
. '|'
200 . $this->_var_regexp
. '|\w+)(?>' . $this->_mod_regexp
. '*)\s*)';
202 // matches valid parenthesised function parameters:
205 // $foo, $bar, "text"
206 // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
207 $this->_parenth_param_regexp
= '(?:\((?:\w+|'
208 . $this->_param_regexp
. '(?:\s*,\s*(?:(?:\w+|'
209 . $this->_param_regexp
. ')))*)?\))';
211 // matches valid function call:
214 // _foo_bar($foo,"bar")
215 // foo123($foo,$foo->bar(),"foo")
216 $this->_func_call_regexp
= '(?:' . $this->_func_regexp
. '\s*(?:'
217 . $this->_parenth_param_regexp
. '))';
223 * sets $compiled_content to the compiled source
224 * @param string $resource_name
225 * @param string $source_content
226 * @param string $compiled_content
229 function _compile_file($resource_name, $source_content, &$compiled_content)
232 if ($this->security
) {
233 // do not allow php syntax to be executed unless specified
234 if ($this->php_handling
== SMARTY_PHP_ALLOW
&&
235 !$this->security_settings
['PHP_HANDLING']) {
236 $this->php_handling
= SMARTY_PHP_PASSTHRU
;
240 $this->_load_filters();
242 $this->_current_file
= $resource_name;
243 $this->_current_line_no
= 1;
244 $ldq = preg_quote($this->left_delimiter
, '!');
245 $rdq = preg_quote($this->right_delimiter
, '!');
247 // run template source through prefilter functions
248 if (count($this->_plugins
['prefilter']) > 0) {
249 foreach ($this->_plugins
['prefilter'] as $filter_name => $prefilter) {
250 if ($prefilter === false) continue;
251 if ($prefilter[3] ||
is_callable($prefilter[0])) {
252 $source_content = call_user_func_array($prefilter[0],
253 array($source_content, &$this));
254 $this->_plugins
['prefilter'][$filter_name][3] = true;
256 $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
261 /* Annihilate the comments. */
262 $source_content = preg_replace("!({$ldq})\*(.*?)\*({$rdq})!se",
263 "'\\1*'.str_repeat(\"\n\", substr_count('\\2', \"\n\")) .'*\\3'",
266 /* Pull out the literal blocks. */
267 preg_match_all("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", $source_content, $_match);
268 $this->_literal_blocks
= $_match[1];
269 $source_content = preg_replace("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s",
270 $this->quote_replace($this->left_delimiter
.'literal'.$this->right_delimiter
), $source_content);
272 /* Pull out the php code blocks. */
273 preg_match_all("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s", $source_content, $_match);
274 $this->_php_blocks
= $_match[1];
275 $source_content = preg_replace("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s",
276 $this->quote_replace($this->left_delimiter
.'php'.$this->right_delimiter
), $source_content);
278 /* Gather all template tags. */
279 preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $source_content, $_match);
280 $template_tags = $_match[1];
281 /* Split content by template tags to obtain non-template content. */
282 $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $source_content);
284 /* loop through text blocks */
285 for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++
) {
286 /* match anything resembling php tags */
287 if (preg_match_all('!(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)!is', $text_blocks[$curr_tb], $sp_match)) {
288 /* replace tags with placeholders to prevent recursive replacements */
289 $sp_match[1] = array_unique($sp_match[1]);
290 usort($sp_match[1], '_smarty_sort_length');
291 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++
) {
292 $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
294 /* process each one */
295 for ($curr_sp = 0, $for_max2 = count($sp_match[0]); $curr_sp < $for_max2; $curr_sp++
) {
296 if ($this->php_handling
== SMARTY_PHP_PASSTHRU
) {
297 /* echo php contents */
298 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
299 } else if ($this->php_handling
== SMARTY_PHP_QUOTE
) {
301 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
302 } else if ($this->php_handling
== SMARTY_PHP_REMOVE
) {
303 /* remove php tags */
304 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
306 /* SMARTY_PHP_ALLOW, but echo non php starting tags */
307 $sp_match[1][$curr_sp] = preg_replace('%(<\?(?!php|=|$))%i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
308 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
314 /* Compile the template tags into PHP code. */
315 $compiled_tags = array();
316 for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++
) {
317 $this->_current_line_no +
= substr_count($text_blocks[$i], "\n");
318 $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
319 $this->_current_line_no +
= substr_count($template_tags[$i], "\n");
322 $compiled_content = '';
324 /* Interleave the compiled contents and text blocks to get the final result. */
325 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++
) {
326 if ($compiled_tags[$i] == '') {
327 // tag result empty, remove first newline from following text block
328 $text_blocks[$i+
1] = preg_replace('!^(\r\n|\r|\n)!', '', $text_blocks[$i+
1]);
330 $compiled_content .= $text_blocks[$i].$compiled_tags[$i];
332 $compiled_content .= $text_blocks[$i];
334 /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
335 if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_content, $_match)) {
336 $strip_tags = $_match[0];
337 $strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags);
338 $strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified);
339 for ($i = 0, $for_max = count($strip_tags); $i < $for_max; $i++
)
340 $compiled_content = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s",
341 $this->quote_replace($strip_tags_modified[$i]),
342 $compiled_content, 1);
345 // remove \n from the end of the file, if any
346 if (($_len=strlen($compiled_content)) && ($compiled_content{$_len - 1} == "\n" )) {
347 $compiled_content = substr($compiled_content, 0, -1);
350 if (!empty($this->_cache_serial
)) {
351 $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include
."'] = '".$this->_cache_serial
."'; ?>" . $compiled_content;
354 // remove unnecessary close/open tags
355 $compiled_content = preg_replace('!\?>\n?<\?php!', '', $compiled_content);
357 // run compiled template through postfilter functions
358 if (count($this->_plugins
['postfilter']) > 0) {
359 foreach ($this->_plugins
['postfilter'] as $filter_name => $postfilter) {
360 if ($postfilter === false) continue;
361 if ($postfilter[3] ||
is_callable($postfilter[0])) {
362 $compiled_content = call_user_func_array($postfilter[0],
363 array($compiled_content, &$this));
364 $this->_plugins
['postfilter'][$filter_name][3] = true;
366 $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
371 // put header at the top of the compiled template
372 $template_header = "<?php /* Smarty version ".$this->_version
.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
373 $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
375 /* Emit code to load needed plugins. */
376 $this->_plugins_code
= '';
377 if (count($this->_plugin_info
)) {
378 $_plugins_params = "array('plugins' => array(";
379 foreach ($this->_plugin_info
as $plugin_type => $plugins) {
380 foreach ($plugins as $plugin_name => $plugin_info) {
381 $_plugins_params .= "array('$plugin_type', '$plugin_name', '$plugin_info[0]', $plugin_info[1], ";
382 $_plugins_params .= $plugin_info[2] ?
'true),' : 'false),';
385 $_plugins_params .= '))';
386 $plugins_code = "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
387 $template_header .= $plugins_code;
388 $this->_plugin_info
= array();
389 $this->_plugins_code
= $plugins_code;
392 if ($this->_init_smarty_vars
) {
393 $template_header .= "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
394 $this->_init_smarty_vars
= false;
397 $compiled_content = $template_header . $compiled_content;
403 * Compile a template tag
405 * @param string $template_tag
408 function _compile_tag($template_tag)
410 // default to php output
411 $this->_output_type
= 'php';
413 /* Matched comment. */
414 if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
417 /* Split tag into two three parts: command, command modifiers and the arguments. */
418 if(! preg_match('/^(?:(' . $this->_obj_call_regexp
. '|' . $this->_var_regexp
419 . '|\/?' . $this->_reg_obj_regexp
. '|\/?' . $this->_func_regexp
. ')(' . $this->_mod_regexp
. '*))
421 /xs', $template_tag, $match)) {
422 $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR
, __FILE__
, __LINE__
);
425 $tag_command = $match[1];
426 $tag_modifier = isset($match[2]) ?
$match[2] : null;
427 $tag_args = isset($match[3]) ?
$match[3] : null;
429 if (preg_match('!^' . $this->_obj_call_regexp
. '|' . $this->_var_regexp
. '$!', $tag_command)) {
430 /* tag name is a variable or object */
431 $_return = $this->_parse_var_props($tag_command . $tag_modifier, $this->_parse_attrs($tag_args));
432 if(isset($_tag_attrs['assign'])) {
433 return "<?php \$this->assign('" . $this->_dequote($_tag_attrs['assign']) . "', $_return ); ?>\n";
434 } elseif ($this->_output_type
== 'php') {
435 return "<?php echo $_return; ?>" . $this->_additional_newline
;
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);
447 switch ($tag_command) {
449 return $this->_compile_include_tag($tag_args);
452 return $this->_compile_include_php_tag($tag_args);
455 return $this->_compile_if_tag($tag_args);
458 return '<?php else: ?>';
461 return $this->_compile_if_tag($tag_args, true);
464 return '<?php endif; ?>';
467 return $this->_compile_capture_tag(true, $tag_args);
470 return $this->_compile_capture_tag(false);
473 return $this->left_delimiter
;
476 return $this->right_delimiter
;
479 array_push($this->_sectionelse_stack
, false);
480 return $this->_compile_section_start($tag_args);
483 $this->_sectionelse_stack
[count($this->_sectionelse_stack
)-1] = true;
484 return "<?php endfor; else: ?>";
487 if (array_pop($this->_sectionelse_stack
))
488 return "<?php endif; ?>";
490 return "<?php endfor; endif; ?>";
493 array_push($this->_foreachelse_stack
, false);
494 return $this->_compile_foreach_start($tag_args);
498 $this->_foreachelse_stack
[count($this->_foreachelse_stack
)-1] = true;
499 return "<?php endforeach; unset(\$_from); else: ?>";
502 if (array_pop($this->_foreachelse_stack
))
503 return "<?php endif; ?>";
505 return "<?php endforeach; unset(\$_from); endif; ?>";
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
;
515 if ($this->_strip_depth++
==0) { /* outermost opening {strip} */
516 $this->_additional_newline
= "";
517 return $this->left_delimiter
.$tag_command.$this->right_delimiter
;
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
;
528 if ($this->security
&& !$this->security_settings
['PHP_TAGS']) {
529 $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING
, __FILE__
, __LINE__
);
532 list (,$php_block) = each($this->_php_blocks
);
533 $this->_current_line_no +
= substr_count($php_block, "\n");
534 return '<?php '.$php_block.' ?>';
537 return $this->_compile_insert_tag($tag_args);
540 if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
542 } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
545 return $this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier);
552 * compile the custom compiler tag
554 * sets $output to the compiled custom compiler tag
555 * @param string $tag_command
556 * @param string $tag_args
557 * @param string $output
560 function _compile_compiler_tag($tag_command, $tag_args, &$output)
563 $have_function = true;
566 * First we check if the compiler function has already been registered
567 * or loaded from a plugin file.
569 if (isset($this->_plugins
['compiler'][$tag_command])) {
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;
578 * Otherwise we need to load plugin file and look for the function
581 else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
584 include_once $plugin_file;
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;
591 $this->_plugins
['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
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
602 if ($have_function) {
603 $output = call_user_func_array($plugin_func, array($tag_args, &$this));
605 $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
607 . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
610 $this->_syntax_error($message, E_USER_WARNING
, __FILE__
, __LINE__
);
620 * compile block function tag
622 * sets $output to compiled block function tag
623 * @param string $tag_command
624 * @param string $tag_args
625 * @param string $tag_modifier
626 * @param string $output
629 function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
631 if ($tag_command{0} == '/') {
633 $tag_command = substr($tag_command, 1);
638 $have_function = true;
641 * First we check if the block function has already been registered
642 * or loaded from a plugin file.
644 if (isset($this->_plugins
['block'][$tag_command])) {
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;
653 * Otherwise we need to load plugin file and look for the function
656 else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
659 include_once $plugin_file;
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;
666 $this->_plugins
['block'][$tag_command] = array($plugin_func, null, null, null, true);
673 } else if (!$have_function) {
674 $this->_syntax_error($message, E_USER_WARNING
, __FILE__
, __LINE__
);
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.
683 $this->_add_plugin('block', $tag_command);
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(); ?>';
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);
698 $output .= 'echo '.$_out_tag_text.'; } ';
699 $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
707 * compile custom function tag
709 * @param string $tag_command
710 * @param string $tag_args
711 * @param string $tag_modifier
714 function _compile_custom_tag($tag_command, $tag_args, $tag_modifier)
716 $this->_add_plugin('function', $tag_command);
718 $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
719 $attrs = $this->_parse_attrs($tag_args);
720 $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs='');
722 $_return = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
723 if($tag_modifier != '') {
724 $this->_parse_modifiers($_return, $tag_modifier);
728 $_return = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $_return . ';'
729 . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline
;
736 * compile a registered object tag
738 * @param string $tag_command
739 * @param array $attrs
740 * @param string $tag_modifier
743 function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
745 if ($tag_command{0} == '/') {
747 $tag_command = substr($tag_command, 1);
752 list($object, $obj_comp) = explode('->', $tag_command);
756 $_assign_var = false;
757 foreach ($attrs as $arg_name => $arg_value) {
758 if($arg_name == 'assign') {
759 $_assign_var = $arg_value;
760 unset($attrs['assign']);
763 if (is_bool($arg_value))
764 $arg_value = $arg_value ?
'true' : 'false';
765 $arg_list[] = "'$arg_name' => $arg_value";
769 if($this->_reg_objects
[$object][2]) {
770 // smarty object argument format
771 $args = "array(".implode(',', (array)$arg_list)."), \$this";
773 // traditional argument format
774 $args = implode(',', array_values($attrs));
783 if(!is_object($this->_reg_objects
[$object][0])) {
784 $this->_trigger_fatal_error("registered '$object' is not an object");
785 } elseif(!empty($this->_reg_objects
[$object][1]) && !in_array($obj_comp, $this->_reg_objects
[$object][1])) {
786 $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'");
787 } elseif(method_exists($this->_reg_objects
[$object][0], $obj_comp)) {
789 if(in_array($obj_comp, $this->_reg_objects
[$object][3])) {
792 $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
793 $prefix .= "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat=true); ";
794 $prefix .= "while (\$_block_repeat) { ob_start();";
798 $prefix = "\$this->_obj_block_content = ob_get_contents(); ob_end_clean(); ";
799 $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)";
800 $postfix = "} array_pop(\$this->_tag_stack);";
804 $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
808 $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
811 if($return != null) {
812 if($tag_modifier != '') {
813 $this->_parse_modifiers($return, $tag_modifier);
816 if(!empty($_assign_var)) {
817 $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);";
819 $output = 'echo ' . $return . ';';
820 $newline = $this->_additional_newline
;
826 return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
830 * Compile {insert ...} tag
832 * @param string $tag_args
835 function _compile_insert_tag($tag_args)
837 $attrs = $this->_parse_attrs($tag_args);
838 $name = $this->_dequote($attrs['name']);
841 $this->_syntax_error("missing insert name", E_USER_ERROR
, __FILE__
, __LINE__
);
844 if (!empty($attrs['script'])) {
845 $delayed_loading = true;
847 $delayed_loading = false;
850 foreach ($attrs as $arg_name => $arg_value) {
851 if (is_bool($arg_value))
852 $arg_value = $arg_value ?
'true' : 'false';
853 $arg_list[] = "'$arg_name' => $arg_value";
856 $this->_add_plugin('insert', $name, $delayed_loading);
858 $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
860 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
;
864 * Compile {include ...} tag
866 * @param string $tag_args
869 function _compile_include_tag($tag_args)
871 $attrs = $this->_parse_attrs($tag_args);
874 if (empty($attrs['file'])) {
875 $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR
, __FILE__
, __LINE__
);
878 foreach ($attrs as $arg_name => $arg_value) {
879 if ($arg_name == 'file') {
880 $include_file = $arg_value;
882 } else if ($arg_name == 'assign') {
883 $assign_var = $arg_value;
886 if (is_bool($arg_value))
887 $arg_value = $arg_value ?
'true' : 'false';
888 $arg_list[] = "'$arg_name' => $arg_value";
893 if (isset($assign_var)) {
894 $output .= "ob_start();\n";
898 "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
901 $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
902 $output .= "\$this->_smarty_include($_params);\n" .
903 "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
904 "unset(\$_smarty_tpl_vars);\n";
906 if (isset($assign_var)) {
907 $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
917 * Compile {include ...} tag
919 * @param string $tag_args
922 function _compile_include_php_tag($tag_args)
924 $attrs = $this->_parse_attrs($tag_args);
926 if (empty($attrs['file'])) {
927 $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR
, __FILE__
, __LINE__
);
930 $assign_var = $this->_dequote($attrs['assign']);
931 $once_var = ( $attrs['once'] == 'false' ) ?
'false' : 'true';
933 foreach($attrs as $arg_name => $arg_value) {
934 if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
935 if(is_bool($arg_value))
936 $arg_value = $arg_value ?
'true' : 'false';
937 $arg_list[] = "'$arg_name' => $arg_value";
941 $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
943 return "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline
;
948 * Compile {section ...} tag
950 * @param string $tag_args
953 function _compile_section_start($tag_args)
955 $attrs = $this->_parse_attrs($tag_args);
959 $section_name = $attrs['name'];
960 if (empty($section_name)) {
961 $this->_syntax_error("missing section name", E_USER_ERROR
, __FILE__
, __LINE__
);
964 $output .= "if (isset(\$this->_sections[$section_name])) unset(\$this->_sections[$section_name]);\n";
965 $section_props = "\$this->_sections[$section_name]";
967 foreach ($attrs as $attr_name => $attr_value) {
968 switch ($attr_name) {
970 $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
974 if (is_bool($attr_value))
975 $show_attr_value = $attr_value ?
'true' : 'false';
977 $show_attr_value = "(bool)$attr_value";
978 $output .= "{$section_props}['show'] = $show_attr_value;\n";
982 $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
987 $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
991 $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
995 $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR
, __FILE__
, __LINE__
);
1000 if (!isset($attrs['show']))
1001 $output .= "{$section_props}['show'] = true;\n";
1003 if (!isset($attrs['loop']))
1004 $output .= "{$section_props}['loop'] = 1;\n";
1006 if (!isset($attrs['max']))
1007 $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
1009 $output .= "if ({$section_props}['max'] < 0)\n" .
1010 " {$section_props}['max'] = {$section_props}['loop'];\n";
1012 if (!isset($attrs['step']))
1013 $output .= "{$section_props}['step'] = 1;\n";
1015 if (!isset($attrs['start']))
1016 $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
1018 $output .= "if ({$section_props}['start'] < 0)\n" .
1019 " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
1021 " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
1024 $output .= "if ({$section_props}['show']) {\n";
1025 if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
1026 $output .= " {$section_props}['total'] = {$section_props}['loop'];\n";
1028 $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";
1030 $output .= " if ({$section_props}['total'] == 0)\n" .
1031 " {$section_props}['show'] = false;\n" .
1033 " {$section_props}['total'] = 0;\n";
1035 $output .= "if ({$section_props}['show']):\n";
1037 for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
1038 {$section_props}['iteration'] <= {$section_props}['total'];
1039 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
1040 $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
1041 $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
1042 $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
1043 $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n";
1044 $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n";
1053 * Compile {foreach ...} tag.
1055 * @param string $tag_args
1058 function _compile_foreach_start($tag_args)
1060 $attrs = $this->_parse_attrs($tag_args);
1061 $arg_list = array();
1063 if (empty($attrs['from'])) {
1064 $this->_syntax_error("missing 'from' attribute", E_USER_ERROR
, __FILE__
, __LINE__
);
1067 if (empty($attrs['item'])) {
1068 $this->_syntax_error("missing 'item' attribute", E_USER_ERROR
, __FILE__
, __LINE__
);
1071 $from = $attrs['from'];
1072 $item = $this->_dequote($attrs['item']);
1073 if (isset($attrs['name']))
1074 $name = $attrs['name'];
1078 $output .= "if (isset(\$this->_foreach[$name])) unset(\$this->_foreach[$name]);\n";
1079 $foreach_props = "\$this->_foreach[$name]";
1084 foreach ($attrs as $attr_name => $attr_value) {
1085 switch ($attr_name) {
1087 $key = $this->_dequote($attrs['key']);
1088 $key_part = "\$this->_tpl_vars['$key'] => ";
1092 $output .= "{$foreach_props}['$attr_name'] = $attr_value;\n";
1098 $output .= "{$foreach_props}['total'] = count(\$_from = (array)$from);\n";
1099 $output .= "{$foreach_props}['show'] = {$foreach_props}['total'] > 0;\n";
1100 $output .= "if ({$foreach_props}['show']):\n";
1101 $output .= "{$foreach_props}['iteration'] = 0;\n";
1102 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1103 $output .= " {$foreach_props}['iteration']++;\n";
1104 $output .= " {$foreach_props}['first'] = ({$foreach_props}['iteration'] == 1);\n";
1105 $output .= " {$foreach_props}['last'] = ({$foreach_props}['iteration'] == {$foreach_props}['total']);\n";
1107 $output .= "if (count(\$_from = (array)$from)):\n";
1108 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1117 * Compile {capture} .. {/capture} tags
1119 * @param boolean $start true if this is the {capture} tag
1120 * @param string $tag_args
1124 function _compile_capture_tag($start, $tag_args = '')
1126 $attrs = $this->_parse_attrs($tag_args);
1129 if (isset($attrs['name']))
1130 $buffer = $attrs['name'];
1132 $buffer = "'default'";
1134 if (isset($attrs['assign']))
1135 $assign = $attrs['assign'];
1138 $output = "<?php ob_start(); ?>";
1139 $this->_capture_stack
[] = array($buffer, $assign);
1141 list($buffer, $assign) = array_pop($this->_capture_stack
);
1142 $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
1143 if (isset($assign)) {
1144 $output .= " \$this->assign($assign, ob_get_contents());";
1146 $output .= "ob_end_clean(); ?>";
1153 * Compile {if ...} tag
1155 * @param string $tag_args
1156 * @param boolean $elseif if true, uses elseif instead of if
1159 function _compile_if_tag($tag_args, $elseif = false)
1162 /* Tokenize args for 'if' tag. */
1163 preg_match_all('/(?>
1164 ' . $this->_obj_call_regexp
. '(?:' . $this->_mod_regexp
. '*)? | # valid object call
1165 ' . $this->_var_regexp
. '(?:' . $this->_mod_regexp
. '*)? | # var or quoted string
1166 \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token
1167 \b\w+\b | # valid word token
1169 )/x', $tag_args, $match);
1171 $tokens = $match[0];
1173 // make sure we have balanced parenthesis
1174 $token_count = array_count_values($tokens);
1175 if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1176 $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR
, __FILE__
, __LINE__
);
1179 $is_arg_stack = array();
1181 for ($i = 0; $i < count($tokens); $i++
) {
1183 $token = &$tokens[$i];
1185 switch (strtolower($token)) {
1258 array_push($is_arg_stack, $i);
1262 /* If last token was a ')', we operate on the parenthesized
1263 expression. The start of the expression is on the stack.
1264 Otherwise, we operate on the last encountered token. */
1265 if ($tokens[$i-1] == ')')
1266 $is_arg_start = array_pop($is_arg_stack);
1268 $is_arg_start = $i-1;
1269 /* Construct the argument for 'is' expression, so it knows
1270 what to operate on. */
1271 $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1273 /* Pass all tokens from next one until the end to the
1274 'is' expression parsing function. The function will
1275 return modified tokens, where the first one is the result
1276 of the 'is' expression and the rest are the tokens it
1278 $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+
1));
1280 /* Replace the old tokens with the new ones. */
1281 array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1283 /* Adjust argument start so that it won't change from the
1284 current position for the next iteration. */
1289 if(preg_match('!^' . $this->_func_regexp
. '$!', $token) ) {
1291 if($this->security
&&
1292 !in_array($token, $this->security_settings
['IF_FUNCS'])) {
1293 $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR
, __FILE__
, __LINE__
);
1295 } elseif(preg_match('!^' . $this->_obj_call_regexp
. '|' . $this->_var_regexp
. '(?:' . $this->_mod_regexp
. '*)$!', $token)) {
1296 // object or variable
1297 $token = $this->_parse_var_props($token);
1298 } elseif(is_numeric($token)) {
1301 $this->_syntax_error("unidentified token '$token'", E_USER_ERROR
, __FILE__
, __LINE__
);
1308 return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1310 return '<?php if ('.implode(' ', $tokens).'): ?>';
1314 function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1315 $arg_list = array();
1317 if (isset($type) && isset($name)
1318 && isset($this->_plugins
[$type])
1319 && isset($this->_plugins
[$type][$name])
1320 && empty($this->_plugins
[$type][$name][4])
1321 && is_array($this->_plugins
[$type][$name][5])
1323 /* we have a list of parameters that should be cached */
1324 $_cache_attrs = $this->_plugins
[$type][$name][5];
1325 $_count = $this->_cache_attrs_count++
;
1326 $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1329 /* no parameters are cached */
1330 $_cache_attrs = null;
1333 foreach ($attrs as $arg_name => $arg_value) {
1334 if (is_bool($arg_value))
1335 $arg_value = $arg_value ?
'true' : 'false';
1336 if (is_null($arg_value))
1337 $arg_value = 'null';
1338 if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1339 $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1341 $arg_list[] = "'$arg_name' => $arg_value";
1348 * Parse is expression
1350 * @param string $is_arg
1351 * @param array $tokens
1354 function _parse_is_expr($is_arg, $tokens)
1357 $negate_expr = false;
1359 if (($first_token = array_shift($tokens)) == 'not') {
1360 $negate_expr = true;
1361 $expr_type = array_shift($tokens);
1363 $expr_type = $first_token;
1365 switch ($expr_type) {
1367 if (@$tokens[$expr_end] == 'by') {
1369 $expr_arg = $tokens[$expr_end++
];
1370 $expr = "!(($is_arg / $expr_arg) % " . $this->_parse_var_props($expr_arg) . ")";
1372 $expr = "!($is_arg % 2)";
1376 if (@$tokens[$expr_end] == 'by') {
1378 $expr_arg = $tokens[$expr_end++
];
1379 $expr = "(($is_arg / $expr_arg) % ". $this->_parse_var_props($expr_arg) . ")";
1381 $expr = "($is_arg % 2)";
1385 if (@$tokens[$expr_end] == 'by') {
1387 $expr_arg = $tokens[$expr_end++
];
1388 $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1390 $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR
, __FILE__
, __LINE__
);
1395 $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR
, __FILE__
, __LINE__
);
1403 array_splice($tokens, 0, $expr_end, $expr);
1410 * Parse attribute string
1412 * @param string $tag_args
1415 function _parse_attrs($tag_args)
1418 /* Tokenize tag attributes. */
1419 preg_match_all('/(?:' . $this->_obj_call_regexp
. '|' . $this->_qstr_regexp
. ' | (?>[^"\'=\s]+)
1422 /x', $tag_args, $match);
1423 $tokens = $match[0];
1427 0 - expecting attribute name
1429 2 - expecting attribute value (not '=') */
1432 foreach ($tokens as $token) {
1435 /* If the token is a valid identifier, we set attribute name
1436 and go to state 1. */
1437 if (preg_match('!^\w+$!', $token)) {
1438 $attr_name = $token;
1441 $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR
, __FILE__
, __LINE__
);
1445 /* If the token is '=', then we go to state 2. */
1446 if ($token == '=') {
1449 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR
, __FILE__
, __LINE__
);
1453 /* If token is not '=', we set the attribute value and go to
1455 if ($token != '=') {
1456 /* We booleanize the token if it's a non-quoted possible
1458 if (preg_match('!^(on|yes|true)$!', $token)) {
1460 } else if (preg_match('!^(off|no|false)$!', $token)) {
1462 } else if ($token == 'null') {
1464 } else if (preg_match('!^-?([0-9]+|0[xX][0-9a-fA-F]+)$!', $token)) {
1465 /* treat integer literally */
1466 } else if (!preg_match('!^' . $this->_obj_call_regexp
. '|' . $this->_var_regexp
. '(?:' . $this->_mod_regexp
. ')*$!', $token)) {
1467 /* treat as a string, double-quote it escaping quotes */
1468 $token = '"'.addslashes($token).'"';
1471 $attrs[$attr_name] = $token;
1474 $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR
, __FILE__
, __LINE__
);
1477 $last_token = $token;
1482 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR
, __FILE__
, __LINE__
);
1484 $this->_syntax_error("missing attribute value", E_USER_ERROR
, __FILE__
, __LINE__
);
1488 $this->_parse_vars_props($attrs);
1494 * compile multiple variables and section properties tokens into
1497 * @param array $tokens
1499 function _parse_vars_props(&$tokens)
1501 foreach($tokens as $key => $val) {
1502 $tokens[$key] = $this->_parse_var_props($val);
1507 * compile single variable and section properties token into
1510 * @param string $val
1511 * @param string $tag_attrs
1514 function _parse_var_props($val, $tag_attrs = null)
1518 if(preg_match('!^(' . $this->_obj_call_regexp
. '|' . $this->_dvar_regexp
. ')(?:' . $this->_mod_regexp
. '*)$!', $val)) {
1519 // $ variable or object
1520 return $this->_parse_var($val);
1522 elseif(preg_match('!^' . $this->_db_qstr_regexp
. '(?:' . $this->_mod_regexp
. '*)$!', $val)) {
1523 // double quoted text
1524 preg_match('!^(' . $this->_db_qstr_regexp
. ')('. $this->_mod_regexp
. '*)$!', $val, $match);
1525 $return = $this->_expand_quoted_text($match[1]);
1526 if($match[2] != '') {
1527 $this->_parse_modifiers($return, $match[2]);
1531 elseif(preg_match('!^' . $this->_si_qstr_regexp
. '(?:' . $this->_mod_regexp
. '*)$!', $val)) {
1532 // single quoted text
1533 preg_match('!^(' . $this->_si_qstr_regexp
. ')('. $this->_mod_regexp
. '*)$!', $val, $match);
1534 if($match[2] != '') {
1535 $this->_parse_modifiers($match[1], $match[2]);
1539 elseif(preg_match('!^' . $this->_cvar_regexp
. '(?:' . $this->_mod_regexp
. '*)$!', $val)) {
1541 return $this->_parse_conf_var($val);
1543 elseif(preg_match('!^' . $this->_svar_regexp
. '(?:' . $this->_mod_regexp
. '*)$!', $val)) {
1545 return $this->_parse_section_prop($val);
1547 elseif(!in_array($val, $this->_permitted_tokens
) && !is_numeric($val)) {
1549 return $this->_expand_quoted_text('"' . $val .'"');
1555 * expand quoted text with embedded variables
1557 * @param string $var_expr
1560 function _expand_quoted_text($var_expr)
1562 // if contains unescaped $, expand it
1563 if(preg_match_all('%(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp
. '\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)%', $var_expr, $_match)) {
1564 $_match = $_match[0];
1567 foreach($_match as $_var) {
1568 $var_expr = str_replace ($_var, '".' . $this->_parse_var(str_replace('`','',$_var)) . '."', $var_expr);
1570 $_return = preg_replace('%\.""|(?<!\\\\)""\.%', '', $var_expr);
1572 $_return = $var_expr;
1574 // replace double quoted literal string with single quotes
1575 $_return = preg_replace('!^"([\s\w]+)"$!',"'\\1'",$_return);
1580 * parse variable expression into PHP code or static value
1582 * @param string $var_expr
1585 function _parse_var($var_expr)
1587 // inform the calling expression the return type (php, static)
1588 $this->_output_type
= 'php';
1590 $_math_vars = preg_split('!('.$this->_dvar_math_regexp
.'|'.$this->_qstr_regexp
.')!', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE
);
1591 if(count($_math_vars) > 1)
1594 $_complete_var = "";
1595 // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
1597 foreach($_math_vars as $_k => $_math_var)
1599 $_math_var = $_math_vars[$_k];
1600 if(!empty($_math_var))
1602 // hit a math operator, so process the stuff which came before it
1603 if(preg_match('!^' . $this->_dvar_math_regexp
. '$!', $_math_var))
1606 if(!empty($_complete_var))
1608 $_output .= $this->_parse_var($_complete_var);
1611 // just output the math operator to php
1612 $_output .= $_math_var;
1614 $_complete_var = "";
1618 // fetch multiple -> (like $foo->bar->baz ) which wouldn't get fetched else, because it would only get $foo->bar and treat the ->baz as "-" ">baz" then
1619 for($_i = $_k +
1; $_i <= count($_math_vars); $_i +
= 2)
1621 // fetch -> because it gets splitted at - and move it back together
1622 if( /* prevent notice */ (isset($_math_vars[$_i]) && isset($_math_vars[$_i+
1])) && ($_math_vars[$_i] === '-' && $_math_vars[$_i+
1]{0} === '>'))
1624 $_math_var .= $_math_vars[$_i].$_math_vars[$_i+
1];
1625 $_math_vars[$_i] = $_math_vars[$_i+
1] = '';
1630 $_complete_var .= $_math_var;
1636 if(!empty($_complete_var))
1637 $_output .= $this->_parse_var($_complete_var);
1644 preg_match('!(' . $this->_dvar_num_var_regexp
. '*|' . $this->_obj_call_regexp
. '|' . $this->_var_regexp
. ')(' . $this->_mod_regexp
. '*)$!', $var_expr, $match);
1646 // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
1647 if(!is_numeric($match[1]{0}))
1648 $_var_ref = substr($match[1],1);
1650 $_var_ref = $match[1];
1652 $modifiers = $match[2];
1655 if(!empty($this->default_modifiers
) && !preg_match('!(^|\|)smarty:nodefaults($|\|)!',$modifiers)) {
1656 $_default_mod_string = implode('|',(array)$this->default_modifiers
);
1657 $modifiers = empty($modifiers) ?
$_default_mod_string : $_default_mod_string . '|' . $modifiers;
1660 // get [foo] and .foo and ->foo and (...) pieces
1661 preg_match_all('!(?:^\w+)|' . $this->_obj_params_regexp
. '|(?:' . $this->_var_bracket_regexp
. ')|->\$?\w+|\.\$?\w+|\S+!', $_var_ref, $match);
1663 $_indexes = $match[0];
1664 $_var_name = array_shift($_indexes);
1666 /* Handle $smarty.* variable references as a special case. */
1667 if ($_var_name == 'smarty') {
1669 * If the reference could be compiled, use the compiled output;
1670 * otherwise, fall back on the $smarty variable generated at
1673 if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
1674 $_output = $smarty_ref;
1676 $_var_name = substr(array_shift($_indexes), 1);
1677 $_output = "\$this->_smarty_vars['$_var_name']";
1679 } elseif(is_numeric($_var_name) && is_numeric($var_expr{0})) {
1680 // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
1681 if(count($_indexes) > 0)
1683 $_var_name .= implode("", $_indexes);
1684 $_indexes = array();
1686 $_output = $_var_name;
1688 $_output = "\$this->_tpl_vars['$_var_name']";
1691 foreach ($_indexes as $_index) {
1692 if ($_index{0} == '[') {
1693 $_index = substr($_index, 1, -1);
1694 if (is_numeric($_index)) {
1695 $_output .= "[$_index]";
1696 } elseif ($_index{0} == '$') {
1697 $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
1699 $_var_parts = explode('.', $_index);
1700 $_var_section = $_var_parts[0];
1701 $_var_section_prop = isset($_var_parts[1]) ?
$_var_parts[1] : 'index';
1702 $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
1704 } else if ($_index{0} == '.') {
1705 if ($_index{1} == '$')
1706 $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
1708 $_output .= "['" . substr($_index, 1) . "']";
1709 } else if (substr($_index,0,2) == '->') {
1710 if(substr($_index,2,2) == '__') {
1711 $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR
, __FILE__
, __LINE__
);
1712 } elseif($this->security
&& substr($_index, 2, 1) == '_') {
1713 $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR
, __FILE__
, __LINE__
);
1714 } elseif ($_index{2} == '$') {
1715 if ($this->security
) {
1716 $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR
, __FILE__
, __LINE__
);
1718 $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
1721 $_output .= $_index;
1723 } elseif ($_index{0} == '(') {
1724 $_index = $this->_parse_parenth_args($_index);
1725 $_output .= $_index;
1727 $_output .= $_index;
1731 $this->_parse_modifiers($_output, $modifiers);
1737 * parse arguments in function call parenthesis
1739 * @param string $parenth_args
1742 function _parse_parenth_args($parenth_args)
1744 preg_match_all('!' . $this->_param_regexp
. '!',$parenth_args, $match);
1748 $orig_vals = $match;
1749 $this->_parse_vars_props($match);
1750 return str_replace($orig_vals, $match, $parenth_args);
1754 * parse configuration variable expression into PHP code
1756 * @param string $conf_var_expr
1758 function _parse_conf_var($conf_var_expr)
1760 $parts = explode('|', $conf_var_expr, 2);
1761 $var_ref = $parts[0];
1762 $modifiers = isset($parts[1]) ?
$parts[1] : '';
1764 $var_name = substr($var_ref, 1, -1);
1766 $output = "\$this->_config[0]['vars']['$var_name']";
1768 $this->_parse_modifiers($output, $modifiers);
1774 * parse section property expression into PHP code
1776 * @param string $section_prop_expr
1779 function _parse_section_prop($section_prop_expr)
1781 $parts = explode('|', $section_prop_expr, 2);
1782 $var_ref = $parts[0];
1783 $modifiers = isset($parts[1]) ?
$parts[1] : '';
1785 preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1786 $section_name = $match[1];
1787 $prop_name = $match[2];
1789 $output = "\$this->_sections['$section_name']['$prop_name']";
1791 $this->_parse_modifiers($output, $modifiers);
1798 * parse modifier chain into PHP code
1800 * sets $output to parsed modified chain
1801 * @param string $output
1802 * @param string $modifier_string
1804 function _parse_modifiers(&$output, $modifier_string)
1806 preg_match_all('!\|(@?\w+)((?>:(?:'. $this->_qstr_regexp
. '|[^|]+))*)!', '|' . $modifier_string, $_match);
1807 list(, $_modifiers, $modifier_arg_strings) = $_match;
1809 for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++
) {
1810 $_modifier_name = $_modifiers[$_i];
1812 if($_modifier_name == 'smarty') {
1813 // skip smarty modifier
1817 preg_match_all('!:(' . $this->_qstr_regexp
. '|[^:]+)!', $modifier_arg_strings[$_i], $_match);
1818 $_modifier_args = $_match[1];
1820 if ($_modifier_name{0} == '@') {
1821 $_map_array = false;
1822 $_modifier_name = substr($_modifier_name, 1);
1827 $this->_add_plugin('modifier', $_modifier_name);
1828 if (empty($this->_plugins
['modifier'][$_modifier_name])
1829 && !$this->_get_plugin_filepath('modifier', $_modifier_name)
1830 && function_exists($_modifier_name)) {
1831 if ($this->security
&& !in_array($_modifier_name, $this->security_settings
['MODIFIER_FUNCS'])) {
1832 $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $_tpl_file, $_tpl_line, __FILE__
, __LINE__
);
1834 $this->_plugins
['modifier'][$_modifier_name] = array($_modifier_name, null, null, false);
1838 $this->_parse_vars_props($_modifier_args);
1840 if($_modifier_name == 'default') {
1841 // supress notifications of default modifier vars and args
1842 if($output{0} == '$') {
1843 $output = '@' . $output;
1845 if(isset($_modifier_args[0]) && $_modifier_args[0]{0} == '$') {
1846 $_modifier_args[0] = '@' . $_modifier_args[0];
1849 if (count($_modifier_args) > 0)
1850 $_modifier_args = ', '.implode(', ', $_modifier_args);
1852 $_modifier_args = '';
1855 $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))";
1859 $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
1869 * @param string $type
1870 * @param string $name
1871 * @param boolean? $delayed_loading
1873 function _add_plugin($type, $name, $delayed_loading = null)
1875 if (!isset($this->_plugin_info
[$type])) {
1876 $this->_plugin_info
[$type] = array();
1878 if (!isset($this->_plugin_info
[$type][$name])) {
1879 $this->_plugin_info
[$type][$name] = array($this->_current_file
,
1880 $this->_current_line_no
,
1887 * Compiles references of type $smarty.foo
1889 * @param string $indexes
1892 function _compile_smarty_ref(&$indexes)
1894 /* Extract the reference name. */
1895 $_ref = substr($indexes[0], 1);
1896 foreach($indexes as $_index_no=>$_index) {
1897 if ($_index{0} != '.' && $_index_no<2 ||
!preg_match('!^(\.|\[|->)!', $_index)) {
1898 $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR
, __FILE__
, __LINE__
);
1904 $compiled_ref = 'time()';
1910 array_shift($indexes);
1911 $_var = $this->_parse_var_props(substr($indexes[0], 1));
1912 if ($_ref == 'foreach')
1913 $compiled_ref = "\$this->_foreach[$_var]";
1915 $compiled_ref = "\$this->_sections[$_var]";
1919 $compiled_ref = ($this->request_use_auto_globals
) ?
'$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
1923 $compiled_ref = ($this->request_use_auto_globals
) ?
'$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
1927 $compiled_ref = ($this->request_use_auto_globals
) ?
'$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
1931 $compiled_ref = ($this->request_use_auto_globals
) ?
'$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
1935 $compiled_ref = ($this->request_use_auto_globals
) ?
'$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
1939 $compiled_ref = ($this->request_use_auto_globals
) ?
'$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
1943 * These cases are handled either at run-time or elsewhere in the
1947 if ($this->request_use_auto_globals
) {
1948 $compiled_ref = '$_REQUEST';
1951 $this->_init_smarty_vars
= true;
1959 $compiled_ref = "'$this->_current_file'";
1964 $compiled_ref = "'$this->_version'";
1969 array_shift($indexes);
1970 $_val = $this->_parse_var_props(substr($indexes[0],1));
1971 $compiled_ref = '@constant(' . $_val . ')';
1976 $compiled_ref = "\$this->_config[0]['vars']";
1981 $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR
, __FILE__
, __LINE__
);
1985 if (isset($_max_index) && count($indexes) > $_max_index) {
1986 $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR
, __FILE__
, __LINE__
);
1989 array_shift($indexes);
1990 return $compiled_ref;
1994 * compiles call to plugin of type $type with name $name
1995 * returns a string containing the function-name or method call
1996 * without the paramter-list that would have follow to make the
1997 * call valid php-syntax
1999 * @param string $type
2000 * @param string $name
2003 function _compile_plugin_call($type, $name) {
2004 if (isset($this->_plugins
[$type][$name])) {
2006 if (is_array($this->_plugins
[$type][$name][0])) {
2007 return ((is_object($this->_plugins
[$type][$name][0][0])) ?
2008 "\$this->_plugins['$type']['$name'][0][0]->" /* method callback */
2009 : (string)($this->_plugins
[$type][$name][0][0]).'::' /* class callback */
2010 ). $this->_plugins
[$type][$name][0][1];
2013 /* function callback */
2014 return $this->_plugins
[$type][$name][0];
2018 /* plugin not loaded -> auto-loadable-plugin */
2019 return 'smarty_'.$type.'_'.$name;
2025 * load pre- and post-filters
2027 function _load_filters()
2029 if (count($this->_plugins
['prefilter']) > 0) {
2030 foreach ($this->_plugins
['prefilter'] as $filter_name => $prefilter) {
2031 if ($prefilter === false) {
2032 unset($this->_plugins
['prefilter'][$filter_name]);
2033 $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
2034 require_once(SMARTY_DIR
. 'core' . DIRECTORY_SEPARATOR
. 'core.load_plugins.php');
2035 smarty_core_load_plugins($_params, $this);
2039 if (count($this->_plugins
['postfilter']) > 0) {
2040 foreach ($this->_plugins
['postfilter'] as $filter_name => $postfilter) {
2041 if ($postfilter === false) {
2042 unset($this->_plugins
['postfilter'][$filter_name]);
2043 $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
2044 require_once(SMARTY_DIR
. 'core' . DIRECTORY_SEPARATOR
. 'core.load_plugins.php');
2045 smarty_core_load_plugins($_params, $this);
2053 * display Smarty syntax error
2055 * @param string $error_msg
2056 * @param integer $error_type
2057 * @param string $file
2058 * @param integer $line
2060 function _syntax_error($error_msg, $error_type = E_USER_ERROR
, $file=null, $line=null)
2062 if(isset($file) && isset($line)) {
2063 $info = ' ('.basename($file).", line $line)";
2067 trigger_error('Smarty: [in ' . $this->_current_file
. ' line ' .
2068 $this->_current_line_no
. "]: syntax error: $error_msg$info", $error_type);
2073 * check if the compilation changes from cacheable to
2074 * non-cacheable state with the beginning of the current
2075 * plugin. return php-code to reflect the transition.
2078 function _push_cacheable_state($type, $name) {
2079 $_cacheable = !isset($this->_plugins
[$type][$name]) ||
$this->_plugins
[$type][$name][4];
2081 ||
0<$this->_cacheable_state++
) return '';
2082 if (!isset($this->_cache_serial
)) $this->_cache_serial
= md5(uniqid('Smarty'));
2083 $_ret = 'if ($this->caching) { echo \'{nocache:'
2084 . $this->_cache_serial
. '#' . $this->_nocache_count
2091 * check if the compilation changes from non-cacheable to
2092 * cacheable state with the end of the current plugin return
2093 * php-code to reflect the transition.
2096 function _pop_cacheable_state($type, $name) {
2097 $_cacheable = !isset($this->_plugins
[$type][$name]) ||
$this->_plugins
[$type][$name][4];
2099 ||
--$this->_cacheable_state
>0) return '';
2100 return 'if ($this->caching) { echo \'{/nocache:'
2101 . $this->_cache_serial
. '#' . ($this->_nocache_count++
)
2108 * compare to values by their string length
2115 function _smarty_sort_length($a, $b)
2120 if(strlen($a) == strlen($b))
2121 return ($a > $b) ?
-1 : 1;
2123 return (strlen($a) > strlen($b)) ?
-1 : 1;
This page took 1.789686 seconds and 4 git commands to generate.