Merge commit 'catalyst/MOODLE_19_STABLE' into mdl19-linuxchix
[moodle-linuxchix.git] / lib / smarty / Smarty_Compiler.class.php
blobaba1fb65f328d3eb4f3883762e495060af74e462
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.9
25 * @copyright 2001-2005 New Digital Group, Inc.
26 * @package Smarty
29 /* $Id$ */
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 Smarty_Compiler()
83 // matches double quoted strings:
84 // "foobar"
85 // "foo\"bar"
86 $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
88 // matches single quoted strings:
89 // 'foobar'
90 // 'foo\'bar'
91 $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
93 // matches single or double quoted strings
94 $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
96 // matches bracket portion of vars
97 // [0]
98 // [foo]
99 // [$bar]
100 $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
102 // matches numerical constants
103 // 30
104 // -12
105 // 13.22
106 $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
108 // matches $ vars (not objects):
109 // $foo
110 // $foo.bar
111 // $foo.bar.foobar
112 // $foo[0]
113 // $foo[$bar]
114 // $foo[5][blah]
115 // $foo[5].bar[$foobar][4]
116 $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))';
117 $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
118 $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
119 . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
120 $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
122 // matches config vars:
123 // #foo#
124 // #foobar123_foo#
125 $this->_cvar_regexp = '\#\w+\#';
127 // matches section vars:
128 // %foo.bar%
129 $this->_svar_regexp = '\%\w+\.\w+\%';
131 // matches all valid variables (no quotes, no modifiers)
132 $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
133 . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
135 // matches valid variable syntax:
136 // $foo
137 // $foo
138 // #foo#
139 // #foo#
140 // "text"
141 // "text"
142 $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
144 // matches valid object call (one level of object nesting allowed in parameters):
145 // $foo->bar
146 // $foo->bar()
147 // $foo->bar("text")
148 // $foo->bar($foo, $bar, "text")
149 // $foo->bar($foo, "foo")
150 // $foo->bar->foo()
151 // $foo->bar->foo->bar()
152 // $foo->bar($foo->bar)
153 // $foo->bar($foo->bar())
154 // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
155 $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
156 $this->_obj_restricted_param_regexp = '(?:'
157 . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')'
158 . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)';
159 $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
160 . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
161 $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
162 . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
163 $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
164 $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?)';
166 // matches valid modifier syntax:
167 // |foo
168 // |@foo
169 // |foo:"bar"
170 // |foo:$bar
171 // |foo:"bar":$foobar
172 // |foo|bar
173 // |foo:$foo->bar
174 $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|'
175 . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
177 // matches valid function name:
178 // foo123
179 // _foo_bar
180 $this->_func_regexp = '[a-zA-Z_]\w*';
182 // matches valid registered object:
183 // foo->bar
184 $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
186 // matches valid parameter values:
187 // true
188 // $foo
189 // $foo|bar
190 // #foo#
191 // #foo#|bar
192 // "text"
193 // "text"|bar
194 // $foo->bar
195 $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
196 . $this->_var_regexp . '|' . $this->_num_const_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
198 // matches valid parenthesised function parameters:
200 // "text"
201 // $foo, $bar, "text"
202 // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
203 $this->_parenth_param_regexp = '(?:\((?:\w+|'
204 . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
205 . $this->_param_regexp . ')))*)?\))';
207 // matches valid function call:
208 // foo()
209 // foo_bar($foo)
210 // _foo_bar($foo,"bar")
211 // foo123($foo,$foo->bar(),"foo")
212 $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
213 . $this->_parenth_param_regexp . '))';
217 * compile a resource
219 * sets $compiled_content to the compiled source
220 * @param string $resource_name
221 * @param string $source_content
222 * @param string $compiled_content
223 * @return true
225 function _compile_file($resource_name, $source_content, &$compiled_content)
228 if ($this->security) {
229 // do not allow php syntax to be executed unless specified
230 if ($this->php_handling == SMARTY_PHP_ALLOW &&
231 !$this->security_settings['PHP_HANDLING']) {
232 $this->php_handling = SMARTY_PHP_PASSTHRU;
236 $this->_load_filters();
238 $this->_current_file = $resource_name;
239 $this->_current_line_no = 1;
240 $ldq = preg_quote($this->left_delimiter, '~');
241 $rdq = preg_quote($this->right_delimiter, '~');
243 // run template source through prefilter functions
244 if (count($this->_plugins['prefilter']) > 0) {
245 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
246 if ($prefilter === false) continue;
247 if ($prefilter[3] || is_callable($prefilter[0])) {
248 $source_content = call_user_func_array($prefilter[0],
249 array($source_content, &$this));
250 $this->_plugins['prefilter'][$filter_name][3] = true;
251 } else {
252 $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
257 /* fetch all special blocks */
258 $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
260 preg_match_all($search, $source_content, $match, PREG_SET_ORDER);
261 $this->_folded_blocks = $match;
262 reset($this->_folded_blocks);
264 /* replace special blocks by "{php}" */
265 $source_content = preg_replace($search.'e', "'"
266 . $this->_quote_replace($this->left_delimiter) . 'php'
267 . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
268 . $this->_quote_replace($this->right_delimiter)
269 . "'"
270 , $source_content);
272 /* Gather all template tags. */
273 preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match);
274 $template_tags = $_match[1];
275 /* Split content by template tags to obtain non-template content. */
276 $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);
278 /* loop through text blocks */
279 for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
280 /* match anything resembling php tags */
281 if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) {
282 /* replace tags with placeholders to prevent recursive replacements */
283 $sp_match[1] = array_unique($sp_match[1]);
284 usort($sp_match[1], '_smarty_sort_length');
285 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
286 $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
288 /* process each one */
289 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
290 if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
291 /* echo php contents */
292 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
293 } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
294 /* quote php tags */
295 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
296 } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
297 /* remove php tags */
298 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
299 } else {
300 /* SMARTY_PHP_ALLOW, but echo non php starting tags */
301 $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
302 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
308 /* Compile the template tags into PHP code. */
309 $compiled_tags = array();
310 for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
311 $this->_current_line_no += substr_count($text_blocks[$i], "\n");
312 $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
313 $this->_current_line_no += substr_count($template_tags[$i], "\n");
315 if (count($this->_tag_stack)>0) {
316 list($_open_tag, $_line_no) = end($this->_tag_stack);
317 $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
318 return;
321 /* Reformat $text_blocks between 'strip' and '/strip' tags,
322 removing spaces, tabs and newlines. */
323 $strip = false;
324 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
325 if ($compiled_tags[$i] == '{strip}') {
326 $compiled_tags[$i] = '';
327 $strip = true;
328 /* remove leading whitespaces */
329 $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
331 if ($strip) {
332 /* strip all $text_blocks before the next '/strip' */
333 for ($j = $i + 1; $j < $for_max; $j++) {
334 /* remove leading and trailing whitespaces of each line */
335 $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]);
336 if ($compiled_tags[$j] == '{/strip}') {
337 /* remove trailing whitespaces from the last text_block */
338 $text_blocks[$j] = rtrim($text_blocks[$j]);
340 $text_blocks[$j] = "<?php echo '" . strtr($text_blocks[$j], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>";
341 if ($compiled_tags[$j] == '{/strip}') {
342 $compiled_tags[$j] = "\n"; /* slurped by php, but necessary
343 if a newline is following the closing strip-tag */
344 $strip = false;
345 $i = $j;
346 break;
351 $compiled_content = '';
353 /* Interleave the compiled contents and text blocks to get the final result. */
354 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
355 if ($compiled_tags[$i] == '') {
356 // tag result empty, remove first newline from following text block
357 $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]);
359 $compiled_content .= $text_blocks[$i].$compiled_tags[$i];
361 $compiled_content .= $text_blocks[$i];
363 // remove \n from the end of the file, if any
364 if (($_len=strlen($compiled_content)) && ($compiled_content{$_len - 1} == "\n" )) {
365 $compiled_content = substr($compiled_content, 0, -1);
368 if (!empty($this->_cache_serial)) {
369 $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
372 // remove unnecessary close/open tags
373 $compiled_content = preg_replace('~\?>\n?<\?php~', '', $compiled_content);
375 // run compiled template through postfilter functions
376 if (count($this->_plugins['postfilter']) > 0) {
377 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
378 if ($postfilter === false) continue;
379 if ($postfilter[3] || is_callable($postfilter[0])) {
380 $compiled_content = call_user_func_array($postfilter[0],
381 array($compiled_content, &$this));
382 $this->_plugins['postfilter'][$filter_name][3] = true;
383 } else {
384 $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
389 // put header at the top of the compiled template
390 $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
391 $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
393 /* Emit code to load needed plugins. */
394 $this->_plugins_code = '';
395 if (count($this->_plugin_info)) {
396 $_plugins_params = "array('plugins' => array(";
397 foreach ($this->_plugin_info as $plugin_type => $plugins) {
398 foreach ($plugins as $plugin_name => $plugin_info) {
399 $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
400 $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
403 $_plugins_params .= '))';
404 $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
405 $template_header .= $plugins_code;
406 $this->_plugin_info = array();
407 $this->_plugins_code = $plugins_code;
410 if ($this->_init_smarty_vars) {
411 $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
412 $this->_init_smarty_vars = false;
415 $compiled_content = $template_header . $compiled_content;
416 return true;
420 * Compile a template tag
422 * @param string $template_tag
423 * @return string
425 function _compile_tag($template_tag)
427 /* Matched comment. */
428 if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
429 return '';
431 /* Split tag into two three parts: command, command modifiers and the arguments. */
432 if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp
433 . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
434 (?:\s+(.*))?$
435 ~xs', $template_tag, $match)) {
436 $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
439 $tag_command = $match[1];
440 $tag_modifier = isset($match[2]) ? $match[2] : null;
441 $tag_args = isset($match[3]) ? $match[3] : null;
443 if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {
444 /* tag name is a variable or object */
445 $_return = $this->_parse_var_props($tag_command . $tag_modifier);
446 return "<?php echo $_return; ?>" . $this->_additional_newline;
449 /* If the tag name is a registered object, we process it. */
450 if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) {
451 return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
454 switch ($tag_command) {
455 case 'include':
456 return $this->_compile_include_tag($tag_args);
458 case 'include_php':
459 return $this->_compile_include_php_tag($tag_args);
461 case 'if':
462 $this->_push_tag('if');
463 return $this->_compile_if_tag($tag_args);
465 case 'else':
466 list($_open_tag) = end($this->_tag_stack);
467 if ($_open_tag != 'if' && $_open_tag != 'elseif')
468 $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
469 else
470 $this->_push_tag('else');
471 return '<?php else: ?>';
473 case 'elseif':
474 list($_open_tag) = end($this->_tag_stack);
475 if ($_open_tag != 'if' && $_open_tag != 'elseif')
476 $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
477 if ($_open_tag == 'if')
478 $this->_push_tag('elseif');
479 return $this->_compile_if_tag($tag_args, true);
481 case '/if':
482 $this->_pop_tag('if');
483 return '<?php endif; ?>';
485 case 'capture':
486 return $this->_compile_capture_tag(true, $tag_args);
488 case '/capture':
489 return $this->_compile_capture_tag(false);
491 case 'ldelim':
492 return $this->left_delimiter;
494 case 'rdelim':
495 return $this->right_delimiter;
497 case 'section':
498 $this->_push_tag('section');
499 return $this->_compile_section_start($tag_args);
501 case 'sectionelse':
502 $this->_push_tag('sectionelse');
503 return "<?php endfor; else: ?>";
504 break;
506 case '/section':
507 $_open_tag = $this->_pop_tag('section');
508 if ($_open_tag == 'sectionelse')
509 return "<?php endif; ?>";
510 else
511 return "<?php endfor; endif; ?>";
513 case 'foreach':
514 $this->_push_tag('foreach');
515 return $this->_compile_foreach_start($tag_args);
516 break;
518 case 'foreachelse':
519 $this->_push_tag('foreachelse');
520 return "<?php endforeach; else: ?>";
522 case '/foreach':
523 $_open_tag = $this->_pop_tag('foreach');
524 if ($_open_tag == 'foreachelse')
525 return "<?php endif; unset(\$_from); ?>";
526 else
527 return "<?php endforeach; endif; unset(\$_from); ?>";
528 break;
530 case 'strip':
531 case '/strip':
532 if ($tag_command{0}=='/') {
533 $this->_pop_tag('strip');
534 if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
535 $this->_additional_newline = "\n";
536 return '{' . $tag_command . '}';
538 } else {
539 $this->_push_tag('strip');
540 if ($this->_strip_depth++==0) { /* outermost opening {strip} */
541 $this->_additional_newline = "";
542 return '{' . $tag_command . '}';
545 return '';
547 case 'php':
548 /* handle folded tags replaced by {php} */
549 list(, $block) = each($this->_folded_blocks);
550 $this->_current_line_no += substr_count($block[0], "\n");
551 /* the number of matched elements in the regexp in _compile_file()
552 determins the type of folded tag that was found */
553 switch (count($block)) {
554 case 2: /* comment */
555 return '';
557 case 3: /* literal */
558 return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
560 case 4: /* php */
561 if ($this->security && !$this->security_settings['PHP_TAGS']) {
562 $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
563 return;
565 return '<?php ' . $block[3] .' ?>';
567 break;
569 case 'insert':
570 return $this->_compile_insert_tag($tag_args);
572 default:
573 if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
574 return $output;
575 } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
576 return $output;
577 } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
578 return $output;
579 } else {
580 $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
588 * compile the custom compiler tag
590 * sets $output to the compiled custom compiler tag
591 * @param string $tag_command
592 * @param string $tag_args
593 * @param string $output
594 * @return boolean
596 function _compile_compiler_tag($tag_command, $tag_args, &$output)
598 $found = false;
599 $have_function = true;
602 * First we check if the compiler function has already been registered
603 * or loaded from a plugin file.
605 if (isset($this->_plugins['compiler'][$tag_command])) {
606 $found = true;
607 $plugin_func = $this->_plugins['compiler'][$tag_command][0];
608 if (!is_callable($plugin_func)) {
609 $message = "compiler function '$tag_command' is not implemented";
610 $have_function = false;
614 * Otherwise we need to load plugin file and look for the function
615 * inside it.
617 else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
618 $found = true;
620 include_once $plugin_file;
622 $plugin_func = 'smarty_compiler_' . $tag_command;
623 if (!is_callable($plugin_func)) {
624 $message = "plugin function $plugin_func() not found in $plugin_file\n";
625 $have_function = false;
626 } else {
627 $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
632 * True return value means that we either found a plugin or a
633 * dynamically registered function. False means that we didn't and the
634 * compiler should now emit code to load custom function plugin for this
635 * tag.
637 if ($found) {
638 if ($have_function) {
639 $output = call_user_func_array($plugin_func, array($tag_args, &$this));
640 if($output != '') {
641 $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
642 . $output
643 . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
645 } else {
646 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
648 return true;
649 } else {
650 return false;
656 * compile block function tag
658 * sets $output to compiled block function tag
659 * @param string $tag_command
660 * @param string $tag_args
661 * @param string $tag_modifier
662 * @param string $output
663 * @return boolean
665 function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
667 if ($tag_command{0} == '/') {
668 $start_tag = false;
669 $tag_command = substr($tag_command, 1);
670 } else
671 $start_tag = true;
673 $found = false;
674 $have_function = true;
677 * First we check if the block function has already been registered
678 * or loaded from a plugin file.
680 if (isset($this->_plugins['block'][$tag_command])) {
681 $found = true;
682 $plugin_func = $this->_plugins['block'][$tag_command][0];
683 if (!is_callable($plugin_func)) {
684 $message = "block function '$tag_command' is not implemented";
685 $have_function = false;
689 * Otherwise we need to load plugin file and look for the function
690 * inside it.
692 else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
693 $found = true;
695 include_once $plugin_file;
697 $plugin_func = 'smarty_block_' . $tag_command;
698 if (!function_exists($plugin_func)) {
699 $message = "plugin function $plugin_func() not found in $plugin_file\n";
700 $have_function = false;
701 } else {
702 $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
707 if (!$found) {
708 return false;
709 } else if (!$have_function) {
710 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
711 return true;
715 * Even though we've located the plugin function, compilation
716 * happens only once, so the plugin will still need to be loaded
717 * at runtime for future requests.
719 $this->_add_plugin('block', $tag_command);
721 if ($start_tag)
722 $this->_push_tag($tag_command);
723 else
724 $this->_pop_tag($tag_command);
726 if ($start_tag) {
727 $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
728 $attrs = $this->_parse_attrs($tag_args);
729 $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs='');
730 $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
731 $output .= $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat=true);';
732 $output .= 'while ($_block_repeat) { ob_start(); ?>';
733 } else {
734 $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); ';
735 $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat=false)';
736 if ($tag_modifier != '') {
737 $this->_parse_modifiers($_out_tag_text, $tag_modifier);
739 $output .= 'echo '.$_out_tag_text.'; } ';
740 $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
743 return true;
748 * compile custom function tag
750 * @param string $tag_command
751 * @param string $tag_args
752 * @param string $tag_modifier
753 * @return string
755 function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
757 $found = false;
758 $have_function = true;
761 * First we check if the custom function has already been registered
762 * or loaded from a plugin file.
764 if (isset($this->_plugins['function'][$tag_command])) {
765 $found = true;
766 $plugin_func = $this->_plugins['function'][$tag_command][0];
767 if (!is_callable($plugin_func)) {
768 $message = "custom function '$tag_command' is not implemented";
769 $have_function = false;
773 * Otherwise we need to load plugin file and look for the function
774 * inside it.
776 else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
777 $found = true;
779 include_once $plugin_file;
781 $plugin_func = 'smarty_function_' . $tag_command;
782 if (!function_exists($plugin_func)) {
783 $message = "plugin function $plugin_func() not found in $plugin_file\n";
784 $have_function = false;
785 } else {
786 $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true);
791 if (!$found) {
792 return false;
793 } else if (!$have_function) {
794 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
795 return true;
798 /* declare plugin to be loaded on display of the template that
799 we compile right now */
800 $this->_add_plugin('function', $tag_command);
802 $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
803 $attrs = $this->_parse_attrs($tag_args);
804 $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs='');
806 $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
807 if($tag_modifier != '') {
808 $this->_parse_modifiers($output, $tag_modifier);
811 if($output != '') {
812 $output = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
813 . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
816 return true;
820 * compile a registered object tag
822 * @param string $tag_command
823 * @param array $attrs
824 * @param string $tag_modifier
825 * @return string
827 function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
829 if ($tag_command{0} == '/') {
830 $start_tag = false;
831 $tag_command = substr($tag_command, 1);
832 } else {
833 $start_tag = true;
836 list($object, $obj_comp) = explode('->', $tag_command);
838 $arg_list = array();
839 if(count($attrs)) {
840 $_assign_var = false;
841 foreach ($attrs as $arg_name => $arg_value) {
842 if($arg_name == 'assign') {
843 $_assign_var = $arg_value;
844 unset($attrs['assign']);
845 continue;
847 if (is_bool($arg_value))
848 $arg_value = $arg_value ? 'true' : 'false';
849 $arg_list[] = "'$arg_name' => $arg_value";
853 if($this->_reg_objects[$object][2]) {
854 // smarty object argument format
855 $args = "array(".implode(',', (array)$arg_list)."), \$this";
856 } else {
857 // traditional argument format
858 $args = implode(',', array_values($attrs));
859 if (empty($args)) {
860 $args = 'null';
864 $prefix = '';
865 $postfix = '';
866 $newline = '';
867 if(!is_object($this->_reg_objects[$object][0])) {
868 $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
869 } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
870 $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
871 } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
872 // method
873 if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
874 // block method
875 if ($start_tag) {
876 $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
877 $prefix .= "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat=true); ";
878 $prefix .= "while (\$_block_repeat) { ob_start();";
879 $return = null;
880 $postfix = '';
881 } else {
882 $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); ";
883 $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat=false)";
884 $postfix = "} array_pop(\$this->_tag_stack);";
886 } else {
887 // non-block method
888 $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
890 } else {
891 // property
892 $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
895 if($return != null) {
896 if($tag_modifier != '') {
897 $this->_parse_modifiers($return, $tag_modifier);
900 if(!empty($_assign_var)) {
901 $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);";
902 } else {
903 $output = 'echo ' . $return . ';';
904 $newline = $this->_additional_newline;
906 } else {
907 $output = '';
910 return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
914 * Compile {insert ...} tag
916 * @param string $tag_args
917 * @return string
919 function _compile_insert_tag($tag_args)
921 $attrs = $this->_parse_attrs($tag_args);
922 $name = $this->_dequote($attrs['name']);
924 if (empty($name)) {
925 $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
928 if (!empty($attrs['script'])) {
929 $delayed_loading = true;
930 } else {
931 $delayed_loading = false;
934 foreach ($attrs as $arg_name => $arg_value) {
935 if (is_bool($arg_value))
936 $arg_value = $arg_value ? 'true' : 'false';
937 $arg_list[] = "'$arg_name' => $arg_value";
940 $this->_add_plugin('insert', $name, $delayed_loading);
942 $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
944 return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
948 * Compile {include ...} tag
950 * @param string $tag_args
951 * @return string
953 function _compile_include_tag($tag_args)
955 $attrs = $this->_parse_attrs($tag_args);
956 $arg_list = array();
958 if (empty($attrs['file'])) {
959 $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
962 foreach ($attrs as $arg_name => $arg_value) {
963 if ($arg_name == 'file') {
964 $include_file = $arg_value;
965 continue;
966 } else if ($arg_name == 'assign') {
967 $assign_var = $arg_value;
968 continue;
970 if (is_bool($arg_value))
971 $arg_value = $arg_value ? 'true' : 'false';
972 $arg_list[] = "'$arg_name' => $arg_value";
975 $output = '<?php ';
977 if (isset($assign_var)) {
978 $output .= "ob_start();\n";
981 $output .=
982 "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
985 $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
986 $output .= "\$this->_smarty_include($_params);\n" .
987 "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
988 "unset(\$_smarty_tpl_vars);\n";
990 if (isset($assign_var)) {
991 $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
994 $output .= ' ?>';
996 return $output;
1001 * Compile {include ...} tag
1003 * @param string $tag_args
1004 * @return string
1006 function _compile_include_php_tag($tag_args)
1008 $attrs = $this->_parse_attrs($tag_args);
1010 if (empty($attrs['file'])) {
1011 $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
1014 $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
1015 $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
1017 $arg_list = array();
1018 foreach($attrs as $arg_name => $arg_value) {
1019 if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
1020 if(is_bool($arg_value))
1021 $arg_value = $arg_value ? 'true' : 'false';
1022 $arg_list[] = "'$arg_name' => $arg_value";
1026 $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
1028 return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
1033 * Compile {section ...} tag
1035 * @param string $tag_args
1036 * @return string
1038 function _compile_section_start($tag_args)
1040 $attrs = $this->_parse_attrs($tag_args);
1041 $arg_list = array();
1043 $output = '<?php ';
1044 $section_name = $attrs['name'];
1045 if (empty($section_name)) {
1046 $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
1049 $output .= "unset(\$this->_sections[$section_name]);\n";
1050 $section_props = "\$this->_sections[$section_name]";
1052 foreach ($attrs as $attr_name => $attr_value) {
1053 switch ($attr_name) {
1054 case 'loop':
1055 $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
1056 break;
1058 case 'show':
1059 if (is_bool($attr_value))
1060 $show_attr_value = $attr_value ? 'true' : 'false';
1061 else
1062 $show_attr_value = "(bool)$attr_value";
1063 $output .= "{$section_props}['show'] = $show_attr_value;\n";
1064 break;
1066 case 'name':
1067 $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
1068 break;
1070 case 'max':
1071 case 'start':
1072 $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
1073 break;
1075 case 'step':
1076 $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
1077 break;
1079 default:
1080 $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
1081 break;
1085 if (!isset($attrs['show']))
1086 $output .= "{$section_props}['show'] = true;\n";
1088 if (!isset($attrs['loop']))
1089 $output .= "{$section_props}['loop'] = 1;\n";
1091 if (!isset($attrs['max']))
1092 $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
1093 else
1094 $output .= "if ({$section_props}['max'] < 0)\n" .
1095 " {$section_props}['max'] = {$section_props}['loop'];\n";
1097 if (!isset($attrs['step']))
1098 $output .= "{$section_props}['step'] = 1;\n";
1100 if (!isset($attrs['start']))
1101 $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
1102 else {
1103 $output .= "if ({$section_props}['start'] < 0)\n" .
1104 " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
1105 "else\n" .
1106 " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
1109 $output .= "if ({$section_props}['show']) {\n";
1110 if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
1111 $output .= " {$section_props}['total'] = {$section_props}['loop'];\n";
1112 } else {
1113 $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";
1115 $output .= " if ({$section_props}['total'] == 0)\n" .
1116 " {$section_props}['show'] = false;\n" .
1117 "} else\n" .
1118 " {$section_props}['total'] = 0;\n";
1120 $output .= "if ({$section_props}['show']):\n";
1121 $output .= "
1122 for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
1123 {$section_props}['iteration'] <= {$section_props}['total'];
1124 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
1125 $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
1126 $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
1127 $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
1128 $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n";
1129 $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n";
1131 $output .= "?>";
1133 return $output;
1138 * Compile {foreach ...} tag.
1140 * @param string $tag_args
1141 * @return string
1143 function _compile_foreach_start($tag_args)
1145 $attrs = $this->_parse_attrs($tag_args);
1146 $arg_list = array();
1148 if (empty($attrs['from'])) {
1149 return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
1151 $from = $attrs['from'];
1153 if (empty($attrs['item'])) {
1154 return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
1156 $item = $this->_dequote($attrs['item']);
1157 if (!preg_match('~^\w+$~', $item)) {
1158 return $this->_syntax_error("'foreach: item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1161 if (isset($attrs['key'])) {
1162 $key = $this->_dequote($attrs['key']);
1163 if (!preg_match('~^\w+$~', $key)) {
1164 return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
1166 $key_part = "\$this->_tpl_vars['$key'] => ";
1167 } else {
1168 $key = null;
1169 $key_part = '';
1172 if (isset($attrs['name'])) {
1173 $name = $attrs['name'];
1174 } else {
1175 $name = null;
1178 $output = '<?php ';
1179 $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
1180 if (isset($name)) {
1181 $foreach_props = "\$this->_foreach[$name]";
1182 $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
1183 $output .= "if ({$foreach_props}['total'] > 0):\n";
1184 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1185 $output .= " {$foreach_props}['iteration']++;\n";
1186 } else {
1187 $output .= "if (count(\$_from)):\n";
1188 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1190 $output .= '?>';
1192 return $output;
1197 * Compile {capture} .. {/capture} tags
1199 * @param boolean $start true if this is the {capture} tag
1200 * @param string $tag_args
1201 * @return string
1204 function _compile_capture_tag($start, $tag_args = '')
1206 $attrs = $this->_parse_attrs($tag_args);
1208 if ($start) {
1209 if (isset($attrs['name']))
1210 $buffer = $attrs['name'];
1211 else
1212 $buffer = "'default'";
1214 if (isset($attrs['assign']))
1215 $assign = $attrs['assign'];
1216 else
1217 $assign = null;
1218 $output = "<?php ob_start(); ?>";
1219 $this->_capture_stack[] = array($buffer, $assign);
1220 } else {
1221 list($buffer, $assign) = array_pop($this->_capture_stack);
1222 $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
1223 if (isset($assign)) {
1224 $output .= " \$this->assign($assign, ob_get_contents());";
1226 $output .= "ob_end_clean(); ?>";
1229 return $output;
1233 * Compile {if ...} tag
1235 * @param string $tag_args
1236 * @param boolean $elseif if true, uses elseif instead of if
1237 * @return string
1239 function _compile_if_tag($tag_args, $elseif = false)
1242 /* Tokenize args for 'if' tag. */
1243 preg_match_all('~(?>
1244 ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
1245 ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)? | # var or quoted string
1246 \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token
1247 \b\w+\b | # valid word token
1248 \S+ # anything else
1249 )~x', $tag_args, $match);
1251 $tokens = $match[0];
1253 // make sure we have balanced parenthesis
1254 $token_count = array_count_values($tokens);
1255 if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1256 $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
1259 $is_arg_stack = array();
1261 for ($i = 0; $i < count($tokens); $i++) {
1263 $token = &$tokens[$i];
1265 switch (strtolower($token)) {
1266 case '!':
1267 case '%':
1268 case '!==':
1269 case '==':
1270 case '===':
1271 case '>':
1272 case '<':
1273 case '!=':
1274 case '<>':
1275 case '<<':
1276 case '>>':
1277 case '<=':
1278 case '>=':
1279 case '&&':
1280 case '||':
1281 case '|':
1282 case '^':
1283 case '&':
1284 case '~':
1285 case ')':
1286 case ',':
1287 case '+':
1288 case '-':
1289 case '*':
1290 case '/':
1291 case '@':
1292 break;
1294 case 'eq':
1295 $token = '==';
1296 break;
1298 case 'ne':
1299 case 'neq':
1300 $token = '!=';
1301 break;
1303 case 'lt':
1304 $token = '<';
1305 break;
1307 case 'le':
1308 case 'lte':
1309 $token = '<=';
1310 break;
1312 case 'gt':
1313 $token = '>';
1314 break;
1316 case 'ge':
1317 case 'gte':
1318 $token = '>=';
1319 break;
1321 case 'and':
1322 $token = '&&';
1323 break;
1325 case 'or':
1326 $token = '||';
1327 break;
1329 case 'not':
1330 $token = '!';
1331 break;
1333 case 'mod':
1334 $token = '%';
1335 break;
1337 case '(':
1338 array_push($is_arg_stack, $i);
1339 break;
1341 case 'is':
1342 /* If last token was a ')', we operate on the parenthesized
1343 expression. The start of the expression is on the stack.
1344 Otherwise, we operate on the last encountered token. */
1345 if ($tokens[$i-1] == ')')
1346 $is_arg_start = array_pop($is_arg_stack);
1347 else
1348 $is_arg_start = $i-1;
1349 /* Construct the argument for 'is' expression, so it knows
1350 what to operate on. */
1351 $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1353 /* Pass all tokens from next one until the end to the
1354 'is' expression parsing function. The function will
1355 return modified tokens, where the first one is the result
1356 of the 'is' expression and the rest are the tokens it
1357 didn't touch. */
1358 $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
1360 /* Replace the old tokens with the new ones. */
1361 array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1363 /* Adjust argument start so that it won't change from the
1364 current position for the next iteration. */
1365 $i = $is_arg_start;
1366 break;
1368 default:
1369 if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) {
1370 // function call
1371 if($this->security &&
1372 !in_array($token, $this->security_settings['IF_FUNCS'])) {
1373 $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1375 } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
1376 // variable function call
1377 $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1378 } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {
1379 // object or variable
1380 $token = $this->_parse_var_props($token);
1381 } elseif(is_numeric($token)) {
1382 // number, skip it
1383 } else {
1384 $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
1386 break;
1390 if ($elseif)
1391 return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1392 else
1393 return '<?php if ('.implode(' ', $tokens).'): ?>';
1397 function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1398 $arg_list = array();
1400 if (isset($type) && isset($name)
1401 && isset($this->_plugins[$type])
1402 && isset($this->_plugins[$type][$name])
1403 && empty($this->_plugins[$type][$name][4])
1404 && is_array($this->_plugins[$type][$name][5])
1406 /* we have a list of parameters that should be cached */
1407 $_cache_attrs = $this->_plugins[$type][$name][5];
1408 $_count = $this->_cache_attrs_count++;
1409 $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1411 } else {
1412 /* no parameters are cached */
1413 $_cache_attrs = null;
1416 foreach ($attrs as $arg_name => $arg_value) {
1417 if (is_bool($arg_value))
1418 $arg_value = $arg_value ? 'true' : 'false';
1419 if (is_null($arg_value))
1420 $arg_value = 'null';
1421 if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1422 $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1423 } else {
1424 $arg_list[] = "'$arg_name' => $arg_value";
1427 return $arg_list;
1431 * Parse is expression
1433 * @param string $is_arg
1434 * @param array $tokens
1435 * @return array
1437 function _parse_is_expr($is_arg, $tokens)
1439 $expr_end = 0;
1440 $negate_expr = false;
1442 if (($first_token = array_shift($tokens)) == 'not') {
1443 $negate_expr = true;
1444 $expr_type = array_shift($tokens);
1445 } else
1446 $expr_type = $first_token;
1448 switch ($expr_type) {
1449 case 'even':
1450 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1451 $expr_end++;
1452 $expr_arg = $tokens[$expr_end++];
1453 $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1454 } else
1455 $expr = "!(1 & $is_arg)";
1456 break;
1458 case 'odd':
1459 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1460 $expr_end++;
1461 $expr_arg = $tokens[$expr_end++];
1462 $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1463 } else
1464 $expr = "(1 & $is_arg)";
1465 break;
1467 case 'div':
1468 if (@$tokens[$expr_end] == 'by') {
1469 $expr_end++;
1470 $expr_arg = $tokens[$expr_end++];
1471 $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1472 } else {
1473 $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1475 break;
1477 default:
1478 $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1479 break;
1482 if ($negate_expr) {
1483 $expr = "!($expr)";
1486 array_splice($tokens, 0, $expr_end, $expr);
1488 return $tokens;
1493 * Parse attribute string
1495 * @param string $tag_args
1496 * @return array
1498 function _parse_attrs($tag_args)
1501 /* Tokenize tag attributes. */
1502 preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
1503 )+ |
1505 ~x', $tag_args, $match);
1506 $tokens = $match[0];
1508 $attrs = array();
1509 /* Parse state:
1510 0 - expecting attribute name
1511 1 - expecting '='
1512 2 - expecting attribute value (not '=') */
1513 $state = 0;
1515 foreach ($tokens as $token) {
1516 switch ($state) {
1517 case 0:
1518 /* If the token is a valid identifier, we set attribute name
1519 and go to state 1. */
1520 if (preg_match('~^\w+$~', $token)) {
1521 $attr_name = $token;
1522 $state = 1;
1523 } else
1524 $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1525 break;
1527 case 1:
1528 /* If the token is '=', then we go to state 2. */
1529 if ($token == '=') {
1530 $state = 2;
1531 } else
1532 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1533 break;
1535 case 2:
1536 /* If token is not '=', we set the attribute value and go to
1537 state 0. */
1538 if ($token != '=') {
1539 /* We booleanize the token if it's a non-quoted possible
1540 boolean value. */
1541 if (preg_match('~^(on|yes|true)$~', $token)) {
1542 $token = 'true';
1543 } else if (preg_match('~^(off|no|false)$~', $token)) {
1544 $token = 'false';
1545 } else if ($token == 'null') {
1546 $token = 'null';
1547 } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
1548 /* treat integer literally */
1549 } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {
1550 /* treat as a string, double-quote it escaping quotes */
1551 $token = '"'.addslashes($token).'"';
1554 $attrs[$attr_name] = $token;
1555 $state = 0;
1556 } else
1557 $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1558 break;
1560 $last_token = $token;
1563 if($state != 0) {
1564 if($state == 1) {
1565 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1566 } else {
1567 $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
1571 $this->_parse_vars_props($attrs);
1573 return $attrs;
1577 * compile multiple variables and section properties tokens into
1578 * PHP code
1580 * @param array $tokens
1582 function _parse_vars_props(&$tokens)
1584 foreach($tokens as $key => $val) {
1585 $tokens[$key] = $this->_parse_var_props($val);
1590 * compile single variable and section properties token into
1591 * PHP code
1593 * @param string $val
1594 * @param string $tag_attrs
1595 * @return string
1597 function _parse_var_props($val)
1599 $val = trim($val);
1601 if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {
1602 // $ variable or object
1603 $return = $this->_parse_var($match[1]);
1604 $modifiers = $match[2];
1605 if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
1606 $_default_mod_string = implode('|',(array)$this->default_modifiers);
1607 $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
1609 $this->_parse_modifiers($return, $modifiers);
1610 return $return;
1611 } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1612 // double quoted text
1613 preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1614 $return = $this->_expand_quoted_text($match[1]);
1615 if($match[2] != '') {
1616 $this->_parse_modifiers($return, $match[2]);
1618 return $return;
1620 elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1621 // numerical constant
1622 preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1623 if($match[2] != '') {
1624 $this->_parse_modifiers($match[1], $match[2]);
1625 return $match[1];
1628 elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1629 // single quoted text
1630 preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1631 if($match[2] != '') {
1632 $this->_parse_modifiers($match[1], $match[2]);
1633 return $match[1];
1636 elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1637 // config var
1638 return $this->_parse_conf_var($val);
1640 elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1641 // section var
1642 return $this->_parse_section_prop($val);
1644 elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
1645 // literal string
1646 return $this->_expand_quoted_text('"' . $val .'"');
1648 return $val;
1652 * expand quoted text with embedded variables
1654 * @param string $var_expr
1655 * @return string
1657 function _expand_quoted_text($var_expr)
1659 // if contains unescaped $, expand it
1660 if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) {
1661 $_match = $_match[0];
1662 rsort($_match);
1663 reset($_match);
1664 foreach($_match as $_var) {
1665 $var_expr = str_replace ($_var, '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."', $var_expr);
1667 $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr);
1668 } else {
1669 $_return = $var_expr;
1671 // replace double quoted literal string with single quotes
1672 $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return);
1673 return $_return;
1677 * parse variable expression into PHP code
1679 * @param string $var_expr
1680 * @param string $output
1681 * @return string
1683 function _parse_var($var_expr)
1685 $_has_math = false;
1686 $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
1688 if(count($_math_vars) > 1) {
1689 $_first_var = "";
1690 $_complete_var = "";
1691 $_output = "";
1692 // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
1693 foreach($_math_vars as $_k => $_math_var) {
1694 $_math_var = $_math_vars[$_k];
1696 if(!empty($_math_var) || is_numeric($_math_var)) {
1697 // hit a math operator, so process the stuff which came before it
1698 if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
1699 $_has_math = true;
1700 if(!empty($_complete_var) || is_numeric($_complete_var)) {
1701 $_output .= $this->_parse_var($_complete_var);
1704 // just output the math operator to php
1705 $_output .= $_math_var;
1707 if(empty($_first_var))
1708 $_first_var = $_complete_var;
1710 $_complete_var = "";
1711 } else {
1712 $_complete_var .= $_math_var;
1716 if($_has_math) {
1717 if(!empty($_complete_var) || is_numeric($_complete_var))
1718 $_output .= $this->_parse_var($_complete_var);
1720 // get the modifiers working (only the last var from math + modifier is left)
1721 $var_expr = $_complete_var;
1725 // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
1726 if(is_numeric($var_expr{0}))
1727 $_var_ref = $var_expr;
1728 else
1729 $_var_ref = substr($var_expr, 1);
1731 if(!$_has_math) {
1733 // get [foo] and .foo and ->foo and (...) pieces
1734 preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match);
1736 $_indexes = $match[0];
1737 $_var_name = array_shift($_indexes);
1739 /* Handle $smarty.* variable references as a special case. */
1740 if ($_var_name == 'smarty') {
1742 * If the reference could be compiled, use the compiled output;
1743 * otherwise, fall back on the $smarty variable generated at
1744 * run-time.
1746 if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
1747 $_output = $smarty_ref;
1748 } else {
1749 $_var_name = substr(array_shift($_indexes), 1);
1750 $_output = "\$this->_smarty_vars['$_var_name']";
1752 } elseif(is_numeric($_var_name) && is_numeric($var_expr{0})) {
1753 // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
1754 if(count($_indexes) > 0)
1756 $_var_name .= implode("", $_indexes);
1757 $_indexes = array();
1759 $_output = $_var_name;
1760 } else {
1761 $_output = "\$this->_tpl_vars['$_var_name']";
1764 foreach ($_indexes as $_index) {
1765 if ($_index{0} == '[') {
1766 $_index = substr($_index, 1, -1);
1767 if (is_numeric($_index)) {
1768 $_output .= "[$_index]";
1769 } elseif ($_index{0} == '$') {
1770 if (strpos($_index, '.') !== false) {
1771 $_output .= '[' . $this->_parse_var($_index) . ']';
1772 } else {
1773 $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
1775 } else {
1776 $_var_parts = explode('.', $_index);
1777 $_var_section = $_var_parts[0];
1778 $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
1779 $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
1781 } else if ($_index{0} == '.') {
1782 if ($_index{1} == '$')
1783 $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
1784 else
1785 $_output .= "['" . substr($_index, 1) . "']";
1786 } else if (substr($_index,0,2) == '->') {
1787 if(substr($_index,2,2) == '__') {
1788 $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1789 } elseif($this->security && substr($_index, 2, 1) == '_') {
1790 $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1791 } elseif ($_index{2} == '$') {
1792 if ($this->security) {
1793 $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1794 } else {
1795 $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
1797 } else {
1798 $_output .= $_index;
1800 } elseif ($_index{0} == '(') {
1801 $_index = $this->_parse_parenth_args($_index);
1802 $_output .= $_index;
1803 } else {
1804 $_output .= $_index;
1809 return $_output;
1813 * parse arguments in function call parenthesis
1815 * @param string $parenth_args
1816 * @return string
1818 function _parse_parenth_args($parenth_args)
1820 preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match);
1821 $orig_vals = $match = $match[0];
1822 $this->_parse_vars_props($match);
1823 $replace = array();
1824 for ($i = 0, $count = count($match); $i < $count; $i++) {
1825 $replace[$orig_vals[$i]] = $match[$i];
1827 return strtr($parenth_args, $replace);
1831 * parse configuration variable expression into PHP code
1833 * @param string $conf_var_expr
1835 function _parse_conf_var($conf_var_expr)
1837 $parts = explode('|', $conf_var_expr, 2);
1838 $var_ref = $parts[0];
1839 $modifiers = isset($parts[1]) ? $parts[1] : '';
1841 $var_name = substr($var_ref, 1, -1);
1843 $output = "\$this->_config[0]['vars']['$var_name']";
1845 $this->_parse_modifiers($output, $modifiers);
1847 return $output;
1851 * parse section property expression into PHP code
1853 * @param string $section_prop_expr
1854 * @return string
1856 function _parse_section_prop($section_prop_expr)
1858 $parts = explode('|', $section_prop_expr, 2);
1859 $var_ref = $parts[0];
1860 $modifiers = isset($parts[1]) ? $parts[1] : '';
1862 preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1863 $section_name = $match[1];
1864 $prop_name = $match[2];
1866 $output = "\$this->_sections['$section_name']['$prop_name']";
1868 $this->_parse_modifiers($output, $modifiers);
1870 return $output;
1875 * parse modifier chain into PHP code
1877 * sets $output to parsed modified chain
1878 * @param string $output
1879 * @param string $modifier_string
1881 function _parse_modifiers(&$output, $modifier_string)
1883 preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
1884 list(, $_modifiers, $modifier_arg_strings) = $_match;
1886 for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
1887 $_modifier_name = $_modifiers[$_i];
1889 if($_modifier_name == 'smarty') {
1890 // skip smarty modifier
1891 continue;
1894 preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
1895 $_modifier_args = $_match[1];
1897 if ($_modifier_name{0} == '@') {
1898 $_map_array = false;
1899 $_modifier_name = substr($_modifier_name, 1);
1900 } else {
1901 $_map_array = true;
1904 if (empty($this->_plugins['modifier'][$_modifier_name])
1905 && !$this->_get_plugin_filepath('modifier', $_modifier_name)
1906 && function_exists($_modifier_name)) {
1907 if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
1908 $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
1909 } else {
1910 $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false);
1913 $this->_add_plugin('modifier', $_modifier_name);
1915 $this->_parse_vars_props($_modifier_args);
1917 if($_modifier_name == 'default') {
1918 // supress notifications of default modifier vars and args
1919 if($output{0} == '$') {
1920 $output = '@' . $output;
1922 if(isset($_modifier_args[0]) && $_modifier_args[0]{0} == '$') {
1923 $_modifier_args[0] = '@' . $_modifier_args[0];
1926 if (count($_modifier_args) > 0)
1927 $_modifier_args = ', '.implode(', ', $_modifier_args);
1928 else
1929 $_modifier_args = '';
1931 if ($_map_array) {
1932 $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))";
1934 } else {
1936 $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
1944 * add plugin
1946 * @param string $type
1947 * @param string $name
1948 * @param boolean? $delayed_loading
1950 function _add_plugin($type, $name, $delayed_loading = null)
1952 if (!isset($this->_plugin_info[$type])) {
1953 $this->_plugin_info[$type] = array();
1955 if (!isset($this->_plugin_info[$type][$name])) {
1956 $this->_plugin_info[$type][$name] = array($this->_current_file,
1957 $this->_current_line_no,
1958 $delayed_loading);
1964 * Compiles references of type $smarty.foo
1966 * @param string $indexes
1967 * @return string
1969 function _compile_smarty_ref(&$indexes)
1971 /* Extract the reference name. */
1972 $_ref = substr($indexes[0], 1);
1973 foreach($indexes as $_index_no=>$_index) {
1974 if ($_index{0} != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) {
1975 $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
1979 switch ($_ref) {
1980 case 'now':
1981 $compiled_ref = 'time()';
1982 $_max_index = 1;
1983 break;
1985 case 'foreach':
1986 array_shift($indexes);
1987 $_var = $this->_parse_var_props(substr($indexes[0], 1));
1988 $_propname = substr($indexes[1], 1);
1989 $_max_index = 1;
1990 switch ($_propname) {
1991 case 'index':
1992 array_shift($indexes);
1993 $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)";
1994 break;
1996 case 'first':
1997 array_shift($indexes);
1998 $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)";
1999 break;
2001 case 'last':
2002 array_shift($indexes);
2003 $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])";
2004 break;
2006 case 'show':
2007 array_shift($indexes);
2008 $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)";
2009 break;
2011 default:
2012 unset($_max_index);
2013 $compiled_ref = "\$this->_foreach[$_var]";
2015 break;
2017 case 'section':
2018 array_shift($indexes);
2019 $_var = $this->_parse_var_props(substr($indexes[0], 1));
2020 $compiled_ref = "\$this->_sections[$_var]";
2021 break;
2023 case 'get':
2024 $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
2025 break;
2027 case 'post':
2028 $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
2029 break;
2031 case 'cookies':
2032 $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
2033 break;
2035 case 'env':
2036 $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
2037 break;
2039 case 'server':
2040 $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
2041 break;
2043 case 'session':
2044 $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
2045 break;
2048 * These cases are handled either at run-time or elsewhere in the
2049 * compiler.
2051 case 'request':
2052 if ($this->request_use_auto_globals) {
2053 $compiled_ref = '$_REQUEST';
2054 break;
2055 } else {
2056 $this->_init_smarty_vars = true;
2058 return null;
2060 case 'capture':
2061 return null;
2063 case 'template':
2064 $compiled_ref = "'$this->_current_file'";
2065 $_max_index = 1;
2066 break;
2068 case 'version':
2069 $compiled_ref = "'$this->_version'";
2070 $_max_index = 1;
2071 break;
2073 case 'const':
2074 if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
2075 $this->_syntax_error("(secure mode) constants not permitted",
2076 E_USER_WARNING, __FILE__, __LINE__);
2077 return;
2079 array_shift($indexes);
2080 if (preg_match('!^\.\w+$!', $indexes[0])) {
2081 $compiled_ref = '@' . substr($indexes[0], 1);
2082 } else {
2083 $_val = $this->_parse_var_props(substr($indexes[0], 1));
2084 $compiled_ref = '@constant(' . $_val . ')';
2086 $_max_index = 1;
2087 break;
2089 case 'config':
2090 $compiled_ref = "\$this->_config[0]['vars']";
2091 $_max_index = 3;
2092 break;
2094 case 'ldelim':
2095 $compiled_ref = "'$this->left_delimiter'";
2096 break;
2098 case 'rdelim':
2099 $compiled_ref = "'$this->right_delimiter'";
2100 break;
2102 default:
2103 $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
2104 break;
2107 if (isset($_max_index) && count($indexes) > $_max_index) {
2108 $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2111 array_shift($indexes);
2112 return $compiled_ref;
2116 * compiles call to plugin of type $type with name $name
2117 * returns a string containing the function-name or method call
2118 * without the paramter-list that would have follow to make the
2119 * call valid php-syntax
2121 * @param string $type
2122 * @param string $name
2123 * @return string
2125 function _compile_plugin_call($type, $name) {
2126 if (isset($this->_plugins[$type][$name])) {
2127 /* plugin loaded */
2128 if (is_array($this->_plugins[$type][$name][0])) {
2129 return ((is_object($this->_plugins[$type][$name][0][0])) ?
2130 "\$this->_plugins['$type']['$name'][0][0]->" /* method callback */
2131 : (string)($this->_plugins[$type][$name][0][0]).'::' /* class callback */
2132 ). $this->_plugins[$type][$name][0][1];
2134 } else {
2135 /* function callback */
2136 return $this->_plugins[$type][$name][0];
2139 } else {
2140 /* plugin not loaded -> auto-loadable-plugin */
2141 return 'smarty_'.$type.'_'.$name;
2147 * load pre- and post-filters
2149 function _load_filters()
2151 if (count($this->_plugins['prefilter']) > 0) {
2152 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
2153 if ($prefilter === false) {
2154 unset($this->_plugins['prefilter'][$filter_name]);
2155 $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
2156 require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2157 smarty_core_load_plugins($_params, $this);
2161 if (count($this->_plugins['postfilter']) > 0) {
2162 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
2163 if ($postfilter === false) {
2164 unset($this->_plugins['postfilter'][$filter_name]);
2165 $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
2166 require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
2167 smarty_core_load_plugins($_params, $this);
2175 * Quote subpattern references
2177 * @param string $string
2178 * @return string
2180 function _quote_replace($string)
2182 return strtr($string, array('\\' => '\\\\', '$' => '\\$'));
2186 * display Smarty syntax error
2188 * @param string $error_msg
2189 * @param integer $error_type
2190 * @param string $file
2191 * @param integer $line
2193 function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
2195 $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
2200 * check if the compilation changes from cacheable to
2201 * non-cacheable state with the beginning of the current
2202 * plugin. return php-code to reflect the transition.
2203 * @return string
2205 function _push_cacheable_state($type, $name) {
2206 $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2207 if ($_cacheable
2208 || 0<$this->_cacheable_state++) return '';
2209 if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
2210 $_ret = 'if ($this->caching && !$this->_cache_including) { echo \'{nocache:'
2211 . $this->_cache_serial . '#' . $this->_nocache_count
2212 . '}\';}';
2213 return $_ret;
2218 * check if the compilation changes from non-cacheable to
2219 * cacheable state with the end of the current plugin return
2220 * php-code to reflect the transition.
2221 * @return string
2223 function _pop_cacheable_state($type, $name) {
2224 $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2225 if ($_cacheable
2226 || --$this->_cacheable_state>0) return '';
2227 return 'if ($this->caching && !$this->_cache_including) { echo \'{/nocache:'
2228 . $this->_cache_serial . '#' . ($this->_nocache_count++)
2229 . '}\';}';
2234 * push opening tag-name, file-name and line-number on the tag-stack
2235 * @param string the opening tag's name
2237 function _push_tag($open_tag)
2239 array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
2243 * pop closing tag-name
2244 * raise an error if this stack-top doesn't match with the closing tag
2245 * @param string the closing tag's name
2246 * @return string the opening tag's name
2248 function _pop_tag($close_tag)
2250 $message = '';
2251 if (count($this->_tag_stack)>0) {
2252 list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
2253 if ($close_tag == $_open_tag) {
2254 return $_open_tag;
2256 if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
2257 return $this->_pop_tag($close_tag);
2259 if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
2260 $this->_pop_tag($close_tag);
2261 return $_open_tag;
2263 if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
2264 $this->_pop_tag($close_tag);
2265 return $_open_tag;
2267 if ($_open_tag == 'else' || $_open_tag == 'elseif') {
2268 $_open_tag = 'if';
2269 } elseif ($_open_tag == 'sectionelse') {
2270 $_open_tag = 'section';
2271 } elseif ($_open_tag == 'foreachelse') {
2272 $_open_tag = 'foreach';
2274 $message = " expected {/$_open_tag} (opened line $_line_no).";
2276 $this->_syntax_error("mismatched tag {/$close_tag}.$message",
2277 E_USER_ERROR, __FILE__, __LINE__);
2283 * compare to values by their string length
2285 * @access private
2286 * @param string $a
2287 * @param string $b
2288 * @return 0|-1|1
2290 function _smarty_sort_length($a, $b)
2292 if($a == $b)
2293 return 0;
2295 if(strlen($a) == strlen($b))
2296 return ($a > $b) ? -1 : 1;
2298 return (strlen($a) > strlen($b)) ? -1 : 1;
2302 /* vim: set et: */