minor bug fix
[openemr.git] / library / Smarty_Compiler.class.php
blob597c86b85583583db1607a841e304274d9ccfc73
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@ispi.net>
23 * @author Andrei Zmievski <andrei@php.net>
24 * @version 2.6.2
25 * @copyright 2001-2004 ispi of Lincoln, 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 $_var_bracket_regexp = null;
53 var $_dvar_guts_regexp = null;
54 var $_dvar_regexp = null;
55 var $_cvar_regexp = null;
56 var $_svar_regexp = null;
57 var $_avar_regexp = null;
58 var $_mod_regexp = null;
59 var $_var_regexp = null;
60 var $_parenth_param_regexp = null;
61 var $_func_call_regexp = null;
62 var $_obj_ext_regexp = null;
63 var $_obj_start_regexp = null;
64 var $_obj_params_regexp = null;
65 var $_obj_call_regexp = null;
66 var $_cacheable_state = 0;
67 var $_cache_attrs_count = 0;
68 var $_nocache_count = 0;
69 var $_cache_serial = null;
70 var $_cache_include = null;
72 var $_strip_depth = 0;
73 var $_additional_newline = "\n";
75 /**#@-*/
76 /**
77 * The class constructor.
79 function Smarty_Compiler()
81 // matches double quoted strings:
82 // "foobar"
83 // "foo\"bar"
84 $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
86 // matches single quoted strings:
87 // 'foobar'
88 // 'foo\'bar'
89 $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
91 // matches single or double quoted strings
92 $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
94 // matches bracket portion of vars
95 // [0]
96 // [foo]
97 // [$bar]
98 $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
100 // matches $ vars (not objects):
101 // $foo
102 // $foo.bar
103 // $foo.bar.foobar
104 // $foo[0]
105 // $foo[$bar]
106 // $foo[5][blah]
107 // $foo[5].bar[$foobar][4]
108 $this->_dvar_math_regexp = '[\+\-\*\/\%]';
109 $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
110 $this->_dvar_num_var_regexp = '\-?\d+(?:\.\d+)?' . $this->_dvar_math_var_regexp;
111 $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
112 . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:\-?\d+(?:\.\d+)?|' . $this->_dvar_math_var_regexp . ')*)?';
113 $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
115 // matches config vars:
116 // #foo#
117 // #foobar123_foo#
118 $this->_cvar_regexp = '\#\w+\#';
120 // matches section vars:
121 // %foo.bar%
122 $this->_svar_regexp = '\%\w+\.\w+\%';
124 // matches all valid variables (no quotes, no modifiers)
125 $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
126 . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
128 // matches valid variable syntax:
129 // $foo
130 // $foo
131 // #foo#
132 // #foo#
133 // "text"
134 // "text"
135 $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
137 // matches valid object call (no objects allowed in parameters):
138 // $foo->bar
139 // $foo->bar()
140 // $foo->bar("text")
141 // $foo->bar($foo, $bar, "text")
142 // $foo->bar($foo, "foo")
143 // $foo->bar->foo()
144 // $foo->bar->foo->bar()
145 $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
146 $this->_obj_params_regexp = '\((?:\w+|'
147 . $this->_var_regexp . '(?:\s*,\s*(?:(?:\w+|'
148 . $this->_var_regexp . ')))*)?\)';
149 $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
150 $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?)';
152 // matches valid modifier syntax:
153 // |foo
154 // |@foo
155 // |foo:"bar"
156 // |foo:$bar
157 // |foo:"bar":$foobar
158 // |foo|bar
159 // |foo:$foo->bar
160 $this->_mod_regexp = '(?:\|@?\w+(?::(?>-?\w+|'
161 . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
163 // matches valid function name:
164 // foo123
165 // _foo_bar
166 $this->_func_regexp = '[a-zA-Z_]\w*';
168 // matches valid registered object:
169 // foo->bar
170 $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
172 // matches valid parameter values:
173 // true
174 // $foo
175 // $foo|bar
176 // #foo#
177 // #foo#|bar
178 // "text"
179 // "text"|bar
180 // $foo->bar
181 $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
182 . $this->_var_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
184 // matches valid parenthesised function parameters:
186 // "text"
187 // $foo, $bar, "text"
188 // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
189 $this->_parenth_param_regexp = '(?:\((?:\w+|'
190 . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
191 . $this->_param_regexp . ')))*)?\))';
193 // matches valid function call:
194 // foo()
195 // foo_bar($foo)
196 // _foo_bar($foo,"bar")
197 // foo123($foo,$foo->bar(),"foo")
198 $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
199 . $this->_parenth_param_regexp . '))';
203 * compile a resource
205 * sets $compiled_content to the compiled source
206 * @param string $resource_name
207 * @param string $source_content
208 * @param string $compiled_content
209 * @return true
211 function _compile_file($resource_name, $source_content, &$compiled_content)
214 if ($this->security) {
215 // do not allow php syntax to be executed unless specified
216 if ($this->php_handling == SMARTY_PHP_ALLOW &&
217 !$this->security_settings['PHP_HANDLING']) {
218 $this->php_handling = SMARTY_PHP_PASSTHRU;
222 $this->_load_filters();
224 $this->_current_file = $resource_name;
225 $this->_current_line_no = 1;
226 $ldq = preg_quote($this->left_delimiter, '!');
227 $rdq = preg_quote($this->right_delimiter, '!');
229 // run template source through prefilter functions
230 if (count($this->_plugins['prefilter']) > 0) {
231 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
232 if ($prefilter === false) continue;
233 if ($prefilter[3] || is_callable($prefilter[0])) {
234 $source_content = call_user_func_array($prefilter[0],
235 array($source_content, &$this));
236 $this->_plugins['prefilter'][$filter_name][3] = true;
237 } else {
238 $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
243 /* fetch all special blocks */
244 $search = "!{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}!s";
246 preg_match_all($search, $source_content, $match, PREG_SET_ORDER);
247 $this->_folded_blocks = $match;
248 reset($this->_folded_blocks);
250 /* replace special blocks by "{php}" */
251 $source_content = preg_replace($search.'e', "'"
252 . $this->_quote_replace($this->left_delimiter) . 'php'
253 . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
254 . $this->_quote_replace($this->right_delimiter)
255 . "'"
256 , $source_content);
258 /* Gather all template tags. */
259 preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $source_content, $_match);
260 $template_tags = $_match[1];
261 /* Split content by template tags to obtain non-template content. */
262 $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $source_content);
264 /* loop through text blocks */
265 for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
266 /* match anything resembling php tags */
267 if (preg_match_all('!(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)!is', $text_blocks[$curr_tb], $sp_match)) {
268 /* replace tags with placeholders to prevent recursive replacements */
269 $sp_match[1] = array_unique($sp_match[1]);
270 usort($sp_match[1], '_smarty_sort_length');
271 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
272 $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
274 /* process each one */
275 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
276 if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
277 /* echo php contents */
278 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
279 } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
280 /* quote php tags */
281 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
282 } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
283 /* remove php tags */
284 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
285 } else {
286 /* SMARTY_PHP_ALLOW, but echo non php starting tags */
287 $sp_match[1][$curr_sp] = preg_replace('%(<\?(?!php|=|$))%i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
288 $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
294 /* Compile the template tags into PHP code. */
295 $compiled_tags = array();
296 for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
297 $this->_current_line_no += substr_count($text_blocks[$i], "\n");
298 $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
299 $this->_current_line_no += substr_count($template_tags[$i], "\n");
301 if (count($this->_tag_stack)>0) {
302 list($_open_tag, $_line_no) = end($this->_tag_stack);
303 $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
304 return;
307 $compiled_content = '';
309 /* Interleave the compiled contents and text blocks to get the final result. */
310 for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
311 if ($compiled_tags[$i] == '') {
312 // tag result empty, remove first newline from following text block
313 $text_blocks[$i+1] = preg_replace('!^(\r\n|\r|\n)!', '', $text_blocks[$i+1]);
315 $compiled_content .= $text_blocks[$i].$compiled_tags[$i];
317 $compiled_content .= $text_blocks[$i];
319 /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
320 if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_content, $_match)) {
321 $strip_tags = $_match[0];
322 $strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags);
323 $strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified);
324 for ($i = 0, $for_max = count($strip_tags); $i < $for_max; $i++)
325 $compiled_content = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s",
326 $this->_quote_replace($strip_tags_modified[$i]),
327 $compiled_content, 1);
330 // remove \n from the end of the file, if any
331 if (($_len=strlen($compiled_content)) && ($compiled_content{$_len - 1} == "\n" )) {
332 $compiled_content = substr($compiled_content, 0, -1);
335 if (!empty($this->_cache_serial)) {
336 $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
339 // remove unnecessary close/open tags
340 $compiled_content = preg_replace('!\?>\n?<\?php!', '', $compiled_content);
342 // run compiled template through postfilter functions
343 if (count($this->_plugins['postfilter']) > 0) {
344 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
345 if ($postfilter === false) continue;
346 if ($postfilter[3] || is_callable($postfilter[0])) {
347 $compiled_content = call_user_func_array($postfilter[0],
348 array($compiled_content, &$this));
349 $this->_plugins['postfilter'][$filter_name][3] = true;
350 } else {
351 $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
356 // put header at the top of the compiled template
357 $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
358 $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
360 /* Emit code to load needed plugins. */
361 $this->_plugins_code = '';
362 if (count($this->_plugin_info)) {
363 $_plugins_params = "array('plugins' => array(";
364 foreach ($this->_plugin_info as $plugin_type => $plugins) {
365 foreach ($plugins as $plugin_name => $plugin_info) {
366 $_plugins_params .= "array('$plugin_type', '$plugin_name', '$plugin_info[0]', $plugin_info[1], ";
367 $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
370 $_plugins_params .= '))';
371 $plugins_code = "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
372 $template_header .= $plugins_code;
373 $this->_plugin_info = array();
374 $this->_plugins_code = $plugins_code;
377 if ($this->_init_smarty_vars) {
378 $template_header .= "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
379 $this->_init_smarty_vars = false;
382 $compiled_content = $template_header . $compiled_content;
383 return true;
387 * Compile a template tag
389 * @param string $template_tag
390 * @return string
392 function _compile_tag($template_tag)
394 /* Matched comment. */
395 if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
396 return '';
398 /* Split tag into two three parts: command, command modifiers and the arguments. */
399 if(! preg_match('/^(?:(' . $this->_obj_call_regexp . '|' . $this->_var_regexp
400 . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
401 (?:\s+(.*))?$
402 /xs', $template_tag, $match)) {
403 $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
406 $tag_command = $match[1];
407 $tag_modifier = isset($match[2]) ? $match[2] : null;
408 $tag_args = isset($match[3]) ? $match[3] : null;
410 if (preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$!', $tag_command)) {
411 /* tag name is a variable or object */
412 $_return = $this->_parse_var_props($tag_command . $tag_modifier, $this->_parse_attrs($tag_args));
413 if(isset($_tag_attrs['assign'])) {
414 return "<?php \$this->assign('" . $this->_dequote($_tag_attrs['assign']) . "', $_return ); ?>\n";
415 } else {
416 return "<?php echo $_return; ?>" . $this->_additional_newline;
420 /* If the tag name is a registered object, we process it. */
421 if (preg_match('!^\/?' . $this->_reg_obj_regexp . '$!', $tag_command)) {
422 return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
425 switch ($tag_command) {
426 case 'include':
427 return $this->_compile_include_tag($tag_args);
429 case 'include_php':
430 return $this->_compile_include_php_tag($tag_args);
432 case 'if':
433 $this->_push_tag('if');
434 return $this->_compile_if_tag($tag_args);
436 case 'else':
437 list($_open_tag) = end($this->_tag_stack);
438 if ($_open_tag != 'if' && $_open_tag != 'elseif')
439 $this->_syntax_error('unxepected {else}', E_USER_ERROR, __FILE__, __LINE__);
440 else
441 $this->_push_tag('else');
442 return '<?php else: ?>';
444 case 'elseif':
445 list($_open_tag) = end($this->_tag_stack);
446 if ($_open_tag != 'if' && $_open_tag != 'elseif')
447 $this->_syntax_error('unxepected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
448 if ($_open_tag == 'if')
449 $this->_push_tag('elseif');
450 return $this->_compile_if_tag($tag_args, true);
452 case '/if':
453 $this->_pop_tag('if');
454 return '<?php endif; ?>';
456 case 'capture':
457 return $this->_compile_capture_tag(true, $tag_args);
459 case '/capture':
460 return $this->_compile_capture_tag(false);
462 case 'ldelim':
463 return $this->left_delimiter;
465 case 'rdelim':
466 return $this->right_delimiter;
468 case 'section':
469 $this->_push_tag('section');
470 return $this->_compile_section_start($tag_args);
472 case 'sectionelse':
473 $this->_push_tag('sectionelse');
474 return "<?php endfor; else: ?>";
475 break;
477 case '/section':
478 $_open_tag = $this->_pop_tag('section');
479 if ($_open_tag == 'sectionelse')
480 return "<?php endif; ?>";
481 else
482 return "<?php endfor; endif; ?>";
484 case 'foreach':
485 $this->_push_tag('foreach');
486 return $this->_compile_foreach_start($tag_args);
487 break;
489 case 'foreachelse':
490 $this->_push_tag('foreachelse');
491 return "<?php endforeach; unset(\$_from); else: ?>";
493 case '/foreach':
494 $_open_tag = $this->_pop_tag('foreach');
495 if ($_open_tag == 'foreachelse')
496 return "<?php endif; ?>";
497 else
498 return "<?php endforeach; unset(\$_from); endif; ?>";
499 break;
501 case 'strip':
502 case '/strip':
503 if ($tag_command{0}=='/') {
504 $this->_pop_tag('strip');
505 if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
506 $this->_additional_newline = "\n";
507 return $this->left_delimiter.$tag_command.$this->right_delimiter;
509 } else {
510 $this->_push_tag('strip');
511 if ($this->_strip_depth++==0) { /* outermost opening {strip} */
512 $this->_additional_newline = "";
513 return $this->left_delimiter.$tag_command.$this->right_delimiter;
516 return '';
518 case 'php':
519 /* handle folded tags replaced by {php} */
520 list(, $block) = each($this->_folded_blocks);
521 $this->_current_line_no += substr_count($block[0], "\n");
522 /* the number of matched elements in the regexp in _compile_file()
523 determins the type of folded tag that was found */
524 switch (count($block)) {
525 case 2: /* comment */
526 return '';
528 case 3: /* literal */
529 return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
531 case 4: /* php */
532 if ($this->security && !$this->security_settings['PHP_TAGS']) {
533 $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
534 return;
536 return '<?php ' . $block[3] .' ?>';
538 break;
540 case 'insert':
541 return $this->_compile_insert_tag($tag_args);
543 default:
544 if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
545 return $output;
546 } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
547 return $output;
548 } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
549 return $output;
550 } else {
551 $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
559 * compile the custom compiler tag
561 * sets $output to the compiled custom compiler tag
562 * @param string $tag_command
563 * @param string $tag_args
564 * @param string $output
565 * @return boolean
567 function _compile_compiler_tag($tag_command, $tag_args, &$output)
569 $found = false;
570 $have_function = true;
573 * First we check if the compiler function has already been registered
574 * or loaded from a plugin file.
576 if (isset($this->_plugins['compiler'][$tag_command])) {
577 $found = true;
578 $plugin_func = $this->_plugins['compiler'][$tag_command][0];
579 if (!is_callable($plugin_func)) {
580 $message = "compiler function '$tag_command' is not implemented";
581 $have_function = false;
585 * Otherwise we need to load plugin file and look for the function
586 * inside it.
588 else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
589 $found = true;
591 include_once $plugin_file;
593 $plugin_func = 'smarty_compiler_' . $tag_command;
594 if (!is_callable($plugin_func)) {
595 $message = "plugin function $plugin_func() not found in $plugin_file\n";
596 $have_function = false;
597 } else {
598 $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
603 * True return value means that we either found a plugin or a
604 * dynamically registered function. False means that we didn't and the
605 * compiler should now emit code to load custom function plugin for this
606 * tag.
608 if ($found) {
609 if ($have_function) {
610 $output = call_user_func_array($plugin_func, array($tag_args, &$this));
611 if($output != '') {
612 $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
613 . $output
614 . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
616 } else {
617 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
619 return true;
620 } else {
621 return false;
627 * compile block function tag
629 * sets $output to compiled block function tag
630 * @param string $tag_command
631 * @param string $tag_args
632 * @param string $tag_modifier
633 * @param string $output
634 * @return boolean
636 function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
638 if ($tag_command{0} == '/') {
639 $start_tag = false;
640 $tag_command = substr($tag_command, 1);
641 } else
642 $start_tag = true;
644 $found = false;
645 $have_function = true;
648 * First we check if the block function has already been registered
649 * or loaded from a plugin file.
651 if (isset($this->_plugins['block'][$tag_command])) {
652 $found = true;
653 $plugin_func = $this->_plugins['block'][$tag_command][0];
654 if (!is_callable($plugin_func)) {
655 $message = "block function '$tag_command' is not implemented";
656 $have_function = false;
660 * Otherwise we need to load plugin file and look for the function
661 * inside it.
663 else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
664 $found = true;
666 include_once $plugin_file;
668 $plugin_func = 'smarty_block_' . $tag_command;
669 if (!function_exists($plugin_func)) {
670 $message = "plugin function $plugin_func() not found in $plugin_file\n";
671 $have_function = false;
672 } else {
673 $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
678 if (!$found) {
679 return false;
680 } else if (!$have_function) {
681 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
682 return true;
686 * Even though we've located the plugin function, compilation
687 * happens only once, so the plugin will still need to be loaded
688 * at runtime for future requests.
690 $this->_add_plugin('block', $tag_command);
692 if ($start_tag)
693 $this->_push_tag($tag_command);
694 else
695 $this->_pop_tag($tag_command);
697 if ($start_tag) {
698 $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
699 $attrs = $this->_parse_attrs($tag_args);
700 $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs='');
701 $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
702 $output .= $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat=true);';
703 $output .= 'while ($_block_repeat) { ob_start(); ?>';
704 } else {
705 $output = '<?php $this->_block_content = ob_get_contents(); ob_end_clean(); ';
706 $_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)';
707 if ($tag_modifier != '') {
708 $this->_parse_modifiers($_out_tag_text, $tag_modifier);
710 $output .= 'echo '.$_out_tag_text.'; } ';
711 $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
714 return true;
719 * compile custom function tag
721 * @param string $tag_command
722 * @param string $tag_args
723 * @param string $tag_modifier
724 * @return string
726 function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
728 $found = false;
729 $have_function = true;
732 * First we check if the custom function has already been registered
733 * or loaded from a plugin file.
735 if (isset($this->_plugins['function'][$tag_command])) {
736 $found = true;
737 $plugin_func = $this->_plugins['function'][$tag_command][0];
738 if (!is_callable($plugin_func)) {
739 $message = "custom function '$tag_command' is not implemented";
740 $have_function = false;
744 * Otherwise we need to load plugin file and look for the function
745 * inside it.
747 else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
748 $found = true;
750 include_once $plugin_file;
752 $plugin_func = 'smarty_function_' . $tag_command;
753 if (!function_exists($plugin_func)) {
754 $message = "plugin function $plugin_func() not found in $plugin_file\n";
755 $have_function = false;
756 } else {
757 $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true);
762 if (!$found) {
763 return false;
764 } else if (!$have_function) {
765 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
766 return true;
769 /* declare plugin to be loaded on display of the template that
770 we compile right now */
771 $this->_add_plugin('function', $tag_command);
773 $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
774 $attrs = $this->_parse_attrs($tag_args);
775 $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs='');
777 $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
778 if($tag_modifier != '') {
779 $this->_parse_modifiers($output, $tag_modifier);
782 if($output != '') {
783 $output = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
784 . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
787 return true;
791 * compile a registered object tag
793 * @param string $tag_command
794 * @param array $attrs
795 * @param string $tag_modifier
796 * @return string
798 function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
800 if ($tag_command{0} == '/') {
801 $start_tag = false;
802 $tag_command = substr($tag_command, 1);
803 } else {
804 $start_tag = true;
807 list($object, $obj_comp) = explode('->', $tag_command);
809 $arg_list = array();
810 if(count($attrs)) {
811 $_assign_var = false;
812 foreach ($attrs as $arg_name => $arg_value) {
813 if($arg_name == 'assign') {
814 $_assign_var = $arg_value;
815 unset($attrs['assign']);
816 continue;
818 if (is_bool($arg_value))
819 $arg_value = $arg_value ? 'true' : 'false';
820 $arg_list[] = "'$arg_name' => $arg_value";
824 if($this->_reg_objects[$object][2]) {
825 // smarty object argument format
826 $args = "array(".implode(',', (array)$arg_list)."), \$this";
827 } else {
828 // traditional argument format
829 $args = implode(',', array_values($attrs));
830 if (empty($args)) {
831 $args = 'null';
835 $prefix = '';
836 $postfix = '';
837 $newline = '';
838 if(!is_object($this->_reg_objects[$object][0])) {
839 $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
840 } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
841 $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
842 } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
843 // method
844 if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
845 // block method
846 if ($start_tag) {
847 $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
848 $prefix .= "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat=true); ";
849 $prefix .= "while (\$_block_repeat) { ob_start();";
850 $return = null;
851 $postfix = '';
852 } else {
853 $prefix = "\$this->_obj_block_content = ob_get_contents(); ob_end_clean(); ";
854 $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)";
855 $postfix = "} array_pop(\$this->_tag_stack);";
857 } else {
858 // non-block method
859 $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
861 } else {
862 // property
863 $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
866 if($return != null) {
867 if($tag_modifier != '') {
868 $this->_parse_modifiers($return, $tag_modifier);
871 if(!empty($_assign_var)) {
872 $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);";
873 } else {
874 $output = 'echo ' . $return . ';';
875 $newline = $this->_additional_newline;
877 } else {
878 $output = '';
881 return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
885 * Compile {insert ...} tag
887 * @param string $tag_args
888 * @return string
890 function _compile_insert_tag($tag_args)
892 $attrs = $this->_parse_attrs($tag_args);
893 $name = $this->_dequote($attrs['name']);
895 if (empty($name)) {
896 $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
899 if (!empty($attrs['script'])) {
900 $delayed_loading = true;
901 } else {
902 $delayed_loading = false;
905 foreach ($attrs as $arg_name => $arg_value) {
906 if (is_bool($arg_value))
907 $arg_value = $arg_value ? 'true' : 'false';
908 $arg_list[] = "'$arg_name' => $arg_value";
911 $this->_add_plugin('insert', $name, $delayed_loading);
913 $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
915 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;
919 * Compile {include ...} tag
921 * @param string $tag_args
922 * @return string
924 function _compile_include_tag($tag_args)
926 $attrs = $this->_parse_attrs($tag_args);
927 $arg_list = array();
929 if (empty($attrs['file'])) {
930 $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
933 foreach ($attrs as $arg_name => $arg_value) {
934 if ($arg_name == 'file') {
935 $include_file = $arg_value;
936 continue;
937 } else if ($arg_name == 'assign') {
938 $assign_var = $arg_value;
939 continue;
941 if (is_bool($arg_value))
942 $arg_value = $arg_value ? 'true' : 'false';
943 $arg_list[] = "'$arg_name' => $arg_value";
946 $output = '<?php ';
948 if (isset($assign_var)) {
949 $output .= "ob_start();\n";
952 $output .=
953 "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
956 $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
957 $output .= "\$this->_smarty_include($_params);\n" .
958 "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
959 "unset(\$_smarty_tpl_vars);\n";
961 if (isset($assign_var)) {
962 $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
965 $output .= ' ?>';
967 return $output;
972 * Compile {include ...} tag
974 * @param string $tag_args
975 * @return string
977 function _compile_include_php_tag($tag_args)
979 $attrs = $this->_parse_attrs($tag_args);
981 if (empty($attrs['file'])) {
982 $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
985 $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
986 $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
988 $arg_list = array();
989 foreach($attrs as $arg_name => $arg_value) {
990 if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
991 if(is_bool($arg_value))
992 $arg_value = $arg_value ? 'true' : 'false';
993 $arg_list[] = "'$arg_name' => $arg_value";
997 $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
999 return "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
1004 * Compile {section ...} tag
1006 * @param string $tag_args
1007 * @return string
1009 function _compile_section_start($tag_args)
1011 $attrs = $this->_parse_attrs($tag_args);
1012 $arg_list = array();
1014 $output = '<?php ';
1015 $section_name = $attrs['name'];
1016 if (empty($section_name)) {
1017 $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
1020 $output .= "if (isset(\$this->_sections[$section_name])) unset(\$this->_sections[$section_name]);\n";
1021 $section_props = "\$this->_sections[$section_name]";
1023 foreach ($attrs as $attr_name => $attr_value) {
1024 switch ($attr_name) {
1025 case 'loop':
1026 $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
1027 break;
1029 case 'show':
1030 if (is_bool($attr_value))
1031 $show_attr_value = $attr_value ? 'true' : 'false';
1032 else
1033 $show_attr_value = "(bool)$attr_value";
1034 $output .= "{$section_props}['show'] = $show_attr_value;\n";
1035 break;
1037 case 'name':
1038 $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
1039 break;
1041 case 'max':
1042 case 'start':
1043 $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
1044 break;
1046 case 'step':
1047 $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
1048 break;
1050 default:
1051 $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
1052 break;
1056 if (!isset($attrs['show']))
1057 $output .= "{$section_props}['show'] = true;\n";
1059 if (!isset($attrs['loop']))
1060 $output .= "{$section_props}['loop'] = 1;\n";
1062 if (!isset($attrs['max']))
1063 $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
1064 else
1065 $output .= "if ({$section_props}['max'] < 0)\n" .
1066 " {$section_props}['max'] = {$section_props}['loop'];\n";
1068 if (!isset($attrs['step']))
1069 $output .= "{$section_props}['step'] = 1;\n";
1071 if (!isset($attrs['start']))
1072 $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
1073 else {
1074 $output .= "if ({$section_props}['start'] < 0)\n" .
1075 " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
1076 "else\n" .
1077 " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
1080 $output .= "if ({$section_props}['show']) {\n";
1081 if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
1082 $output .= " {$section_props}['total'] = {$section_props}['loop'];\n";
1083 } else {
1084 $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";
1086 $output .= " if ({$section_props}['total'] == 0)\n" .
1087 " {$section_props}['show'] = false;\n" .
1088 "} else\n" .
1089 " {$section_props}['total'] = 0;\n";
1091 $output .= "if ({$section_props}['show']):\n";
1092 $output .= "
1093 for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
1094 {$section_props}['iteration'] <= {$section_props}['total'];
1095 {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
1096 $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
1097 $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
1098 $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
1099 $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n";
1100 $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n";
1102 $output .= "?>";
1104 return $output;
1109 * Compile {foreach ...} tag.
1111 * @param string $tag_args
1112 * @return string
1114 function _compile_foreach_start($tag_args)
1116 $attrs = $this->_parse_attrs($tag_args);
1117 $arg_list = array();
1119 if (empty($attrs['from'])) {
1120 $this->_syntax_error("missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
1123 if (empty($attrs['item'])) {
1124 $this->_syntax_error("missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
1127 $from = $attrs['from'];
1128 $item = $this->_dequote($attrs['item']);
1129 if (isset($attrs['name']))
1130 $name = $attrs['name'];
1132 $output = '<?php ';
1133 if (isset($name)) {
1134 $output .= "if (isset(\$this->_foreach[$name])) unset(\$this->_foreach[$name]);\n";
1135 $foreach_props = "\$this->_foreach[$name]";
1138 $key_part = '';
1140 foreach ($attrs as $attr_name => $attr_value) {
1141 switch ($attr_name) {
1142 case 'key':
1143 $key = $this->_dequote($attrs['key']);
1144 $key_part = "\$this->_tpl_vars['$key'] => ";
1145 break;
1147 case 'name':
1148 $output .= "{$foreach_props}['$attr_name'] = $attr_value;\n";
1149 break;
1153 if (isset($name)) {
1154 $output .= "{$foreach_props}['total'] = count(\$_from = (array)$from);\n";
1155 $output .= "{$foreach_props}['show'] = {$foreach_props}['total'] > 0;\n";
1156 $output .= "if ({$foreach_props}['show']):\n";
1157 $output .= "{$foreach_props}['iteration'] = 0;\n";
1158 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1159 $output .= " {$foreach_props}['iteration']++;\n";
1160 $output .= " {$foreach_props}['first'] = ({$foreach_props}['iteration'] == 1);\n";
1161 $output .= " {$foreach_props}['last'] = ({$foreach_props}['iteration'] == {$foreach_props}['total']);\n";
1162 } else {
1163 $output .= "if (count(\$_from = (array)$from)):\n";
1164 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1166 $output .= '?>';
1168 return $output;
1173 * Compile {capture} .. {/capture} tags
1175 * @param boolean $start true if this is the {capture} tag
1176 * @param string $tag_args
1177 * @return string
1180 function _compile_capture_tag($start, $tag_args = '')
1182 $attrs = $this->_parse_attrs($tag_args);
1184 if ($start) {
1185 if (isset($attrs['name']))
1186 $buffer = $attrs['name'];
1187 else
1188 $buffer = "'default'";
1190 if (isset($attrs['assign']))
1191 $assign = $attrs['assign'];
1192 else
1193 $assign = null;
1194 $output = "<?php ob_start(); ?>";
1195 $this->_capture_stack[] = array($buffer, $assign);
1196 } else {
1197 list($buffer, $assign) = array_pop($this->_capture_stack);
1198 $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
1199 if (isset($assign)) {
1200 $output .= " \$this->assign($assign, ob_get_contents());";
1202 $output .= "ob_end_clean(); ?>";
1205 return $output;
1209 * Compile {if ...} tag
1211 * @param string $tag_args
1212 * @param boolean $elseif if true, uses elseif instead of if
1213 * @return string
1215 function _compile_if_tag($tag_args, $elseif = false)
1218 /* Tokenize args for 'if' tag. */
1219 preg_match_all('/(?>
1220 ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
1221 ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)? | # var or quoted string
1222 \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token
1223 \b\w+\b | # valid word token
1224 \S+ # anything else
1225 )/x', $tag_args, $match);
1227 $tokens = $match[0];
1229 // make sure we have balanced parenthesis
1230 $token_count = array_count_values($tokens);
1231 if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1232 $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
1235 $is_arg_stack = array();
1237 for ($i = 0; $i < count($tokens); $i++) {
1239 $token = &$tokens[$i];
1241 switch (strtolower($token)) {
1242 case '!':
1243 case '%':
1244 case '!==':
1245 case '==':
1246 case '===':
1247 case '>':
1248 case '<':
1249 case '!=':
1250 case '<>':
1251 case '<<':
1252 case '>>':
1253 case '<=':
1254 case '>=':
1255 case '&&':
1256 case '||':
1257 case '|':
1258 case '^':
1259 case '&':
1260 case '~':
1261 case ')':
1262 case ',':
1263 case '+':
1264 case '-':
1265 case '*':
1266 case '/':
1267 case '@':
1268 break;
1270 case 'eq':
1271 $token = '==';
1272 break;
1274 case 'ne':
1275 case 'neq':
1276 $token = '!=';
1277 break;
1279 case 'lt':
1280 $token = '<';
1281 break;
1283 case 'le':
1284 case 'lte':
1285 $token = '<=';
1286 break;
1288 case 'gt':
1289 $token = '>';
1290 break;
1292 case 'ge':
1293 case 'gte':
1294 $token = '>=';
1295 break;
1297 case 'and':
1298 $token = '&&';
1299 break;
1301 case 'or':
1302 $token = '||';
1303 break;
1305 case 'not':
1306 $token = '!';
1307 break;
1309 case 'mod':
1310 $token = '%';
1311 break;
1313 case '(':
1314 array_push($is_arg_stack, $i);
1315 break;
1317 case 'is':
1318 /* If last token was a ')', we operate on the parenthesized
1319 expression. The start of the expression is on the stack.
1320 Otherwise, we operate on the last encountered token. */
1321 if ($tokens[$i-1] == ')')
1322 $is_arg_start = array_pop($is_arg_stack);
1323 else
1324 $is_arg_start = $i-1;
1325 /* Construct the argument for 'is' expression, so it knows
1326 what to operate on. */
1327 $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1329 /* Pass all tokens from next one until the end to the
1330 'is' expression parsing function. The function will
1331 return modified tokens, where the first one is the result
1332 of the 'is' expression and the rest are the tokens it
1333 didn't touch. */
1334 $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
1336 /* Replace the old tokens with the new ones. */
1337 array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1339 /* Adjust argument start so that it won't change from the
1340 current position for the next iteration. */
1341 $i = $is_arg_start;
1342 break;
1344 default:
1345 if(preg_match('!^' . $this->_func_regexp . '$!', $token) ) {
1346 // function call
1347 if($this->security &&
1348 !in_array($token, $this->security_settings['IF_FUNCS'])) {
1349 $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1351 } elseif(preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$!', $token)) {
1352 // object or variable
1353 $token = $this->_parse_var_props($token);
1354 } elseif(is_numeric($token)) {
1355 // number, skip it
1356 } else {
1357 $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
1359 break;
1363 if ($elseif)
1364 return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1365 else
1366 return '<?php if ('.implode(' ', $tokens).'): ?>';
1370 function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1371 $arg_list = array();
1373 if (isset($type) && isset($name)
1374 && isset($this->_plugins[$type])
1375 && isset($this->_plugins[$type][$name])
1376 && empty($this->_plugins[$type][$name][4])
1377 && is_array($this->_plugins[$type][$name][5])
1379 /* we have a list of parameters that should be cached */
1380 $_cache_attrs = $this->_plugins[$type][$name][5];
1381 $_count = $this->_cache_attrs_count++;
1382 $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1384 } else {
1385 /* no parameters are cached */
1386 $_cache_attrs = null;
1389 foreach ($attrs as $arg_name => $arg_value) {
1390 if (is_bool($arg_value))
1391 $arg_value = $arg_value ? 'true' : 'false';
1392 if (is_null($arg_value))
1393 $arg_value = 'null';
1394 if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1395 $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1396 } else {
1397 $arg_list[] = "'$arg_name' => $arg_value";
1400 return $arg_list;
1404 * Parse is expression
1406 * @param string $is_arg
1407 * @param array $tokens
1408 * @return array
1410 function _parse_is_expr($is_arg, $tokens)
1412 $expr_end = 0;
1413 $negate_expr = false;
1415 if (($first_token = array_shift($tokens)) == 'not') {
1416 $negate_expr = true;
1417 $expr_type = array_shift($tokens);
1418 } else
1419 $expr_type = $first_token;
1421 switch ($expr_type) {
1422 case 'even':
1423 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1424 $expr_end++;
1425 $expr_arg = $tokens[$expr_end++];
1426 $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1427 } else
1428 $expr = "!(1 & $is_arg)";
1429 break;
1431 case 'odd':
1432 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1433 $expr_end++;
1434 $expr_arg = $tokens[$expr_end++];
1435 $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1436 } else
1437 $expr = "(1 & $is_arg)";
1438 break;
1440 case 'div':
1441 if (@$tokens[$expr_end] == 'by') {
1442 $expr_end++;
1443 $expr_arg = $tokens[$expr_end++];
1444 $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1445 } else {
1446 $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1448 break;
1450 default:
1451 $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1452 break;
1455 if ($negate_expr) {
1456 $expr = "!($expr)";
1459 array_splice($tokens, 0, $expr_end, $expr);
1461 return $tokens;
1466 * Parse attribute string
1468 * @param string $tag_args
1469 * @return array
1471 function _parse_attrs($tag_args)
1474 /* Tokenize tag attributes. */
1475 preg_match_all('/(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
1476 )+ |
1478 /x', $tag_args, $match);
1479 $tokens = $match[0];
1481 $attrs = array();
1482 /* Parse state:
1483 0 - expecting attribute name
1484 1 - expecting '='
1485 2 - expecting attribute value (not '=') */
1486 $state = 0;
1488 foreach ($tokens as $token) {
1489 switch ($state) {
1490 case 0:
1491 /* If the token is a valid identifier, we set attribute name
1492 and go to state 1. */
1493 if (preg_match('!^\w+$!', $token)) {
1494 $attr_name = $token;
1495 $state = 1;
1496 } else
1497 $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1498 break;
1500 case 1:
1501 /* If the token is '=', then we go to state 2. */
1502 if ($token == '=') {
1503 $state = 2;
1504 } else
1505 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1506 break;
1508 case 2:
1509 /* If token is not '=', we set the attribute value and go to
1510 state 0. */
1511 if ($token != '=') {
1512 /* We booleanize the token if it's a non-quoted possible
1513 boolean value. */
1514 if (preg_match('!^(on|yes|true)$!', $token)) {
1515 $token = 'true';
1516 } else if (preg_match('!^(off|no|false)$!', $token)) {
1517 $token = 'false';
1518 } else if ($token == 'null') {
1519 $token = 'null';
1520 } else if (preg_match('!^-?([0-9]+|0[xX][0-9a-fA-F]+)$!', $token)) {
1521 /* treat integer literally */
1522 } else if (!preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$!', $token)) {
1523 /* treat as a string, double-quote it escaping quotes */
1524 $token = '"'.addslashes($token).'"';
1527 $attrs[$attr_name] = $token;
1528 $state = 0;
1529 } else
1530 $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1531 break;
1533 $last_token = $token;
1536 if($state != 0) {
1537 if($state == 1) {
1538 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1539 } else {
1540 $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
1544 $this->_parse_vars_props($attrs);
1546 return $attrs;
1550 * compile multiple variables and section properties tokens into
1551 * PHP code
1553 * @param array $tokens
1555 function _parse_vars_props(&$tokens)
1557 foreach($tokens as $key => $val) {
1558 $tokens[$key] = $this->_parse_var_props($val);
1563 * compile single variable and section properties token into
1564 * PHP code
1566 * @param string $val
1567 * @param string $tag_attrs
1568 * @return string
1570 function _parse_var_props($val)
1572 $val = trim($val);
1574 if(preg_match('!^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$!', $val, $match)) {
1575 // $ variable or object
1576 $return = $this->_parse_var($match[1]);
1577 $modifiers = $match[2];
1578 if (!empty($this->default_modifiers) && !preg_match('!(^|\|)smarty:nodefaults($|\|)!',$modifiers)) {
1579 $_default_mod_string = implode('|',(array)$this->default_modifiers);
1580 $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
1582 $this->_parse_modifiers($return, $modifiers);
1583 return $return;
1584 } elseif (preg_match('!^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
1585 // double quoted text
1586 preg_match('!^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$!', $val, $match);
1587 $return = $this->_expand_quoted_text($match[1]);
1588 if($match[2] != '') {
1589 $this->_parse_modifiers($return, $match[2]);
1591 return $return;
1593 elseif(preg_match('!^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
1594 // single quoted text
1595 preg_match('!^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$!', $val, $match);
1596 if($match[2] != '') {
1597 $this->_parse_modifiers($match[1], $match[2]);
1598 return $match[1];
1601 elseif(preg_match('!^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
1602 // config var
1603 return $this->_parse_conf_var($val);
1605 elseif(preg_match('!^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
1606 // section var
1607 return $this->_parse_section_prop($val);
1609 elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
1610 // literal string
1611 return $this->_expand_quoted_text('"' . $val .'"');
1613 return $val;
1617 * expand quoted text with embedded variables
1619 * @param string $var_expr
1620 * @return string
1622 function _expand_quoted_text($var_expr)
1624 // if contains unescaped $, expand it
1625 if(preg_match_all('%(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)%', $var_expr, $_match)) {
1626 $_match = $_match[0];
1627 rsort($_match);
1628 reset($_match);
1629 foreach($_match as $_var) {
1630 $var_expr = str_replace ($_var, '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."', $var_expr);
1632 $_return = preg_replace('%\.""|(?<!\\\\)""\.%', '', $var_expr);
1633 } else {
1634 $_return = $var_expr;
1636 // replace double quoted literal string with single quotes
1637 $_return = preg_replace('!^"([\s\w]+)"$!',"'\\1'",$_return);
1638 return $_return;
1642 * parse variable expression into PHP code
1644 * @param string $var_expr
1645 * @param string $output
1646 * @return string
1648 function _parse_var($var_expr)
1650 $_has_math = false;
1651 $_math_vars = preg_split('!('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')!', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
1653 if(count($_math_vars) > 1) {
1654 $_first_var = "";
1655 $_complete_var = "";
1656 $_output = "";
1657 // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
1658 foreach($_math_vars as $_k => $_math_var) {
1659 $_math_var = $_math_vars[$_k];
1661 if(!empty($_math_var) || is_numeric($_math_var)) {
1662 // hit a math operator, so process the stuff which came before it
1663 if(preg_match('!^' . $this->_dvar_math_regexp . '$!', $_math_var)) {
1664 $_has_math = true;
1665 if(!empty($_complete_var) || is_numeric($_complete_var)) {
1666 $_output .= $this->_parse_var($_complete_var);
1669 // just output the math operator to php
1670 $_output .= $_math_var;
1672 if(empty($_first_var))
1673 $_first_var = $_complete_var;
1675 $_complete_var = "";
1676 } else {
1677 // 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
1678 for($_i = $_k + 1; $_i <= count($_math_vars); $_i += 2) {
1679 // fetch -> because it gets splitted at - and move it back together
1680 if( /* prevent notice */ (isset($_math_vars[$_i]) && isset($_math_vars[$_i+1])) && ($_math_vars[$_i] === '-' && $_math_vars[$_i+1]{0} === '>')) {
1681 $_math_var .= $_math_vars[$_i].$_math_vars[$_i+1];
1682 $_math_vars[$_i] = $_math_vars[$_i+1] = '';
1683 } else {
1684 break;
1687 $_complete_var .= $_math_var;
1691 if($_has_math) {
1692 if(!empty($_complete_var) || is_numeric($_complete_var))
1693 $_output .= $this->_parse_var($_complete_var, true);
1695 // get the modifiers working (only the last var from math + modifier is left)
1696 $var_expr = $_complete_var;
1700 // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
1701 if(is_numeric($var_expr{0}))
1702 $_var_ref = $var_expr;
1703 else
1704 $_var_ref = substr($var_expr, 1);
1706 if(!$_has_math) {
1707 // get [foo] and .foo and ->foo and (...) pieces
1708 preg_match_all('!(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+!', $_var_ref, $match);
1710 $_indexes = $match[0];
1711 $_var_name = array_shift($_indexes);
1713 /* Handle $smarty.* variable references as a special case. */
1714 if ($_var_name == 'smarty') {
1716 * If the reference could be compiled, use the compiled output;
1717 * otherwise, fall back on the $smarty variable generated at
1718 * run-time.
1720 if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
1721 $_output = $smarty_ref;
1722 } else {
1723 $_var_name = substr(array_shift($_indexes), 1);
1724 $_output = "\$this->_smarty_vars['$_var_name']";
1726 } elseif(is_numeric($_var_name) && is_numeric($var_expr{0})) {
1727 // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
1728 if(count($_indexes) > 0)
1730 $_var_name .= implode("", $_indexes);
1731 $_indexes = array();
1733 $_output = $_var_name;
1734 } else {
1735 $_output = "\$this->_tpl_vars['$_var_name']";
1738 foreach ($_indexes as $_index) {
1739 if ($_index{0} == '[') {
1740 $_index = substr($_index, 1, -1);
1741 if (is_numeric($_index)) {
1742 $_output .= "[$_index]";
1743 } elseif ($_index{0} == '$') {
1744 if (strpos($_index, '.') !== false) {
1745 $_output .= '[' . $this->_parse_var($_index) . ']';
1746 } else {
1747 $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
1749 } else {
1750 $_var_parts = explode('.', $_index);
1751 $_var_section = $_var_parts[0];
1752 $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
1753 $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
1755 } else if ($_index{0} == '.') {
1756 if ($_index{1} == '$')
1757 $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
1758 else
1759 $_output .= "['" . substr($_index, 1) . "']";
1760 } else if (substr($_index,0,2) == '->') {
1761 if(substr($_index,2,2) == '__') {
1762 $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1763 } elseif($this->security && substr($_index, 2, 1) == '_') {
1764 $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1765 } elseif ($_index{2} == '$') {
1766 if ($this->security) {
1767 $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1768 } else {
1769 $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
1771 } else {
1772 $_output .= $_index;
1774 } elseif ($_index{0} == '(') {
1775 $_index = $this->_parse_parenth_args($_index);
1776 $_output .= $_index;
1777 } else {
1778 $_output .= $_index;
1783 return $_output;
1787 * parse arguments in function call parenthesis
1789 * @param string $parenth_args
1790 * @return string
1792 function _parse_parenth_args($parenth_args)
1794 preg_match_all('!' . $this->_param_regexp . '!',$parenth_args, $match);
1795 $match = $match[0];
1796 rsort($match);
1797 reset($match);
1798 $orig_vals = $match;
1799 $this->_parse_vars_props($match);
1800 return str_replace($orig_vals, $match, $parenth_args);
1804 * parse configuration variable expression into PHP code
1806 * @param string $conf_var_expr
1808 function _parse_conf_var($conf_var_expr)
1810 $parts = explode('|', $conf_var_expr, 2);
1811 $var_ref = $parts[0];
1812 $modifiers = isset($parts[1]) ? $parts[1] : '';
1814 $var_name = substr($var_ref, 1, -1);
1816 $output = "\$this->_config[0]['vars']['$var_name']";
1818 $this->_parse_modifiers($output, $modifiers);
1820 return $output;
1824 * parse section property expression into PHP code
1826 * @param string $section_prop_expr
1827 * @return string
1829 function _parse_section_prop($section_prop_expr)
1831 $parts = explode('|', $section_prop_expr, 2);
1832 $var_ref = $parts[0];
1833 $modifiers = isset($parts[1]) ? $parts[1] : '';
1835 preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1836 $section_name = $match[1];
1837 $prop_name = $match[2];
1839 $output = "\$this->_sections['$section_name']['$prop_name']";
1841 $this->_parse_modifiers($output, $modifiers);
1843 return $output;
1848 * parse modifier chain into PHP code
1850 * sets $output to parsed modified chain
1851 * @param string $output
1852 * @param string $modifier_string
1854 function _parse_modifiers(&$output, $modifier_string)
1856 preg_match_all('!\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)!', '|' . $modifier_string, $_match);
1857 list(, $_modifiers, $modifier_arg_strings) = $_match;
1859 for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
1860 $_modifier_name = $_modifiers[$_i];
1862 if($_modifier_name == 'smarty') {
1863 // skip smarty modifier
1864 continue;
1867 preg_match_all('!:(' . $this->_qstr_regexp . '|[^:]+)!', $modifier_arg_strings[$_i], $_match);
1868 $_modifier_args = $_match[1];
1870 if ($_modifier_name{0} == '@') {
1871 $_map_array = false;
1872 $_modifier_name = substr($_modifier_name, 1);
1873 } else {
1874 $_map_array = true;
1877 if (empty($this->_plugins['modifier'][$_modifier_name])
1878 && !$this->_get_plugin_filepath('modifier', $_modifier_name)
1879 && function_exists($_modifier_name)) {
1880 if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
1881 $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
1882 } else {
1883 $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false);
1886 $this->_add_plugin('modifier', $_modifier_name);
1888 $this->_parse_vars_props($_modifier_args);
1890 if($_modifier_name == 'default') {
1891 // supress notifications of default modifier vars and args
1892 if($output{0} == '$') {
1893 $output = '@' . $output;
1895 if(isset($_modifier_args[0]) && $_modifier_args[0]{0} == '$') {
1896 $_modifier_args[0] = '@' . $_modifier_args[0];
1899 if (count($_modifier_args) > 0)
1900 $_modifier_args = ', '.implode(', ', $_modifier_args);
1901 else
1902 $_modifier_args = '';
1904 if ($_map_array) {
1905 $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))";
1907 } else {
1909 $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
1917 * add plugin
1919 * @param string $type
1920 * @param string $name
1921 * @param boolean? $delayed_loading
1923 function _add_plugin($type, $name, $delayed_loading = null)
1925 if (!isset($this->_plugin_info[$type])) {
1926 $this->_plugin_info[$type] = array();
1928 if (!isset($this->_plugin_info[$type][$name])) {
1929 $this->_plugin_info[$type][$name] = array($this->_current_file,
1930 $this->_current_line_no,
1931 $delayed_loading);
1937 * Compiles references of type $smarty.foo
1939 * @param string $indexes
1940 * @return string
1942 function _compile_smarty_ref(&$indexes)
1944 /* Extract the reference name. */
1945 $_ref = substr($indexes[0], 1);
1946 foreach($indexes as $_index_no=>$_index) {
1947 if ($_index{0} != '.' && $_index_no<2 || !preg_match('!^(\.|\[|->)!', $_index)) {
1948 $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
1952 switch ($_ref) {
1953 case 'now':
1954 $compiled_ref = 'time()';
1955 $_max_index = 1;
1956 break;
1958 case 'foreach':
1959 case 'section':
1960 array_shift($indexes);
1961 $_var = $this->_parse_var_props(substr($indexes[0], 1));
1962 if ($_ref == 'foreach')
1963 $compiled_ref = "\$this->_foreach[$_var]";
1964 else
1965 $compiled_ref = "\$this->_sections[$_var]";
1966 break;
1968 case 'get':
1969 $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
1970 break;
1972 case 'post':
1973 $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
1974 break;
1976 case 'cookies':
1977 $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
1978 break;
1980 case 'env':
1981 $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
1982 break;
1984 case 'server':
1985 $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
1986 break;
1988 case 'session':
1989 $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
1990 break;
1993 * These cases are handled either at run-time or elsewhere in the
1994 * compiler.
1996 case 'request':
1997 if ($this->request_use_auto_globals) {
1998 $compiled_ref = '$_REQUEST';
1999 break;
2000 } else {
2001 $this->_init_smarty_vars = true;
2003 return null;
2005 case 'capture':
2006 return null;
2008 case 'template':
2009 $compiled_ref = "'$this->_current_file'";
2010 $_max_index = 1;
2011 break;
2013 case 'version':
2014 $compiled_ref = "'$this->_version'";
2015 $_max_index = 1;
2016 break;
2018 case 'const':
2019 array_shift($indexes);
2020 $_val = $this->_parse_var_props(substr($indexes[0],1));
2021 $compiled_ref = '@constant(' . $_val . ')';
2022 $_max_index = 1;
2023 break;
2025 case 'config':
2026 $compiled_ref = "\$this->_config[0]['vars']";
2027 $_max_index = 3;
2028 break;
2030 default:
2031 $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
2032 break;
2035 if (isset($_max_index) && count($indexes) > $_max_index) {
2036 $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2039 array_shift($indexes);
2040 return $compiled_ref;
2044 * compiles call to plugin of type $type with name $name
2045 * returns a string containing the function-name or method call
2046 * without the paramter-list that would have follow to make the
2047 * call valid php-syntax
2049 * @param string $type
2050 * @param string $name
2051 * @return string
2053 function _compile_plugin_call($type, $name) {
2054 if (isset($this->_plugins[$type][$name])) {
2055 /* plugin loaded */
2056 if (is_array($this->_plugins[$type][$name][0])) {
2057 return ((is_object($this->_plugins[$type][$name][0][0])) ?
2058 "\$this->_plugins['$type']['$name'][0][0]->" /* method callback */
2059 : (string)($this->_plugins[$type][$name][0][0]).'::' /* class callback */
2060 ). $this->_plugins[$type][$name][0][1];
2062 } else {
2063 /* function callback */
2064 return $this->_plugins[$type][$name][0];
2067 } else {
2068 /* plugin not loaded -> auto-loadable-plugin */
2069 return 'smarty_'.$type.'_'.$name;
2075 * load pre- and post-filters
2077 function _load_filters()
2079 if (count($this->_plugins['prefilter']) > 0) {
2080 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
2081 if ($prefilter === false) {
2082 unset($this->_plugins['prefilter'][$filter_name]);
2083 $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
2084 require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');
2085 smarty_core_load_plugins($_params, $this);
2089 if (count($this->_plugins['postfilter']) > 0) {
2090 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
2091 if ($postfilter === false) {
2092 unset($this->_plugins['postfilter'][$filter_name]);
2093 $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
2094 require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');
2095 smarty_core_load_plugins($_params, $this);
2103 * Quote subpattern references
2105 * @param string $string
2106 * @return string
2108 function _quote_replace($string)
2110 return preg_replace('![\\$]\d!', '\\\\\\0', $string);
2114 * display Smarty syntax error
2116 * @param string $error_msg
2117 * @param integer $error_type
2118 * @param string $file
2119 * @param integer $line
2121 function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
2123 $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
2128 * check if the compilation changes from cacheable to
2129 * non-cacheable state with the beginning of the current
2130 * plugin. return php-code to reflect the transition.
2131 * @return string
2133 function _push_cacheable_state($type, $name) {
2134 $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2135 if ($_cacheable
2136 || 0<$this->_cacheable_state++) return '';
2137 if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
2138 $_ret = 'if ($this->caching) { echo \'{nocache:'
2139 . $this->_cache_serial . '#' . $this->_nocache_count
2140 . '}\';}';
2141 return $_ret;
2146 * check if the compilation changes from non-cacheable to
2147 * cacheable state with the end of the current plugin return
2148 * php-code to reflect the transition.
2149 * @return string
2151 function _pop_cacheable_state($type, $name) {
2152 $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2153 if ($_cacheable
2154 || --$this->_cacheable_state>0) return '';
2155 return 'if ($this->caching) { echo \'{/nocache:'
2156 . $this->_cache_serial . '#' . ($this->_nocache_count++)
2157 . '}\';}';
2162 * push opening tag-name, file-name and line-number on the tag-stack
2163 * @param: string the opening tag's name
2165 function _push_tag($open_tag)
2167 array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
2171 * pop closing tag-name
2172 * raise an error if this stack-top doesn't match with the closing tag
2173 * @param: string the closing tag's name
2174 * @return: string the opening tag's name
2176 function _pop_tag($close_tag)
2178 $message = '';
2179 if (count($this->_tag_stack)>0) {
2180 list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
2181 if ($close_tag == $_open_tag) {
2182 return $_open_tag;
2184 if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
2185 return $this->_pop_tag($close_tag);
2187 if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
2188 $this->_pop_tag($close_tag);
2189 return $_open_tag;
2191 if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
2192 $this->_pop_tag($close_tag);
2193 return $_open_tag;
2195 $message = " expected {/$_open_tag} (opened line $_line_no).";
2197 $this->_syntax_error("mismatched tag {/$close_tag}.$message",
2198 E_USER_ERROR, __FILE__, __LINE__);
2204 * compare to values by their string length
2206 * @access private
2207 * @param string $a
2208 * @param string $b
2209 * @return 0|-1|1
2211 function _smarty_sort_length($a, $b)
2213 if($a == $b)
2214 return 0;
2216 if(strlen($a) == strlen($b))
2217 return ($a > $b) ? -1 : 1;
2219 return (strlen($a) > strlen($b)) ? -1 : 1;
2223 /* vim: set et: */