big commit
[mirrors/Kyberia-bloodline.git] / smarty / Smarty-2.6.10 / libs / Smarty_Compiler.class.php
CommitLineData
e586807d
H
1<?php
2
3/**
4 * Project: Smarty: the PHP compiling template engine
5 * File: Smarty_Compiler.class.php
6 *
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.
11 *
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.
16 *
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
20 *
e586807d 21 * @link http://smarty.php.net/
b42b2bf9 22 * @author Monte Ohrt <monte at ohrt dot com>
e586807d 23 * @author Andrei Zmievski <andrei@php.net>
b42b2bf9
H
24 * @version 2.6.10
25 * @copyright 2001-2005 New Digital Group, Inc.
e586807d
H
26 * @package Smarty
27 */
28
b42b2bf9 29/* $Id: Smarty_Compiler.class.php,v 1.370 2005/08/04 19:43:21 messju Exp $ */
e586807d
H
30
31/**
32 * Template compiling class
33 * @package Smarty
34 */
35class Smarty_Compiler extends Smarty {
36
37 // internal vars
38 /**#@+
39 * @access private
40 */
b42b2bf9 41 var $_folded_blocks = array(); // keeps folded template blocks
e586807d
H
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;
b42b2bf9
H
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;
e586807d
H
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;
73
74 var $_strip_depth = 0;
b42b2bf9 75 var $_additional_newline = "\n";
e586807d
H
76
77 /**#@-*/
b42b2bf9
H
78 /**
79 * The class constructor.
80 */
e586807d
H
81 function Smarty_Compiler()
82 {
b42b2bf9
H
83 // matches double quoted strings:
84 // "foobar"
85 // "foo\"bar"
86 $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
87
88 // matches single quoted strings:
89 // 'foobar'
90 // 'foo\'bar'
91 $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
92
93 // matches single or double quoted strings
94 $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
95
96 // matches bracket portion of vars
97 // [0]
98 // [foo]
99 // [$bar]
100 $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
101
102 // matches numerical constants
103 // 30
104 // -12
105 // 13.22
106 $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
107
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;
121
122 // matches config vars:
123 // #foo#
124 // #foobar123_foo#
125 $this->_cvar_regexp = '\#\w+\#';
126
127 // matches section vars:
128 // %foo.bar%
129 $this->_svar_regexp = '\%\w+\.\w+\%';
130
131 // matches all valid variables (no quotes, no modifiers)
132 $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
133 . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
134
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 . ')';
143
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 . ')*)?)';
165
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 .'))*)';
176
177 // matches valid function name:
178 // foo123
179 // _foo_bar
180 $this->_func_regexp = '[a-zA-Z_]\w*';
181
182 // matches valid registered object:
183 // foo->bar
184 $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
185
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*)';
197
198 // matches valid parenthesised function parameters:
199 //
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 . ')))*)?\))';
206
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 . '))';
214 }
215
216 /**
217 * compile a resource
218 *
e586807d 219 * sets $compiled_content to the compiled source
b42b2bf9
H
220 * @param string $resource_name
221 * @param string $source_content
222 * @param string $compiled_content
e586807d 223 * @return true
b42b2bf9 224 */
e586807d
H
225 function _compile_file($resource_name, $source_content, &$compiled_content)
226 {
227
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;
233 }
234 }
235
236 $this->_load_filters();
237
238 $this->_current_file = $resource_name;
239 $this->_current_line_no = 1;
b42b2bf9
H
240 $ldq = preg_quote($this->left_delimiter, '~');
241 $rdq = preg_quote($this->right_delimiter, '~');
e586807d
H
242
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");
253 }
254 }
255 }
256
b42b2bf9
H
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";
e586807d 259
b42b2bf9
H
260 preg_match_all($search, $source_content, $match, PREG_SET_ORDER);
261 $this->_folded_blocks = $match;
262 reset($this->_folded_blocks);
e586807d 263
b42b2bf9
H
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);
e586807d
H
271
272 /* Gather all template tags. */
b42b2bf9 273 preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match);
e586807d
H
274 $template_tags = $_match[1];
275 /* Split content by template tags to obtain non-template content. */
b42b2bf9 276 $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);
e586807d
H
277
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 */
b42b2bf9
H
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');
e586807d 285 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
b42b2bf9
H
286 $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
287 }
e586807d 288 /* process each one */
b42b2bf9 289 for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
e586807d
H
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 {
b42b2bf9
H
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]);
303 }
e586807d
H
304 }
305 }
306 }
307
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");
314 }
b42b2bf9
H
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;
319 }
e586807d 320
b42b2bf9
H
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]);
330 }
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]);
339 }
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;
347 }
348 }
349 }
350 }
e586807d
H
351 $compiled_content = '';
352
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++) {
b42b2bf9
H
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]);
358 }
e586807d
H
359 $compiled_content .= $text_blocks[$i].$compiled_tags[$i];
360 }
361 $compiled_content .= $text_blocks[$i];
362
e586807d
H
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);
366 }
367
368 if (!empty($this->_cache_serial)) {
369 $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
370 }
371
372 // remove unnecessary close/open tags
b42b2bf9 373 $compiled_content = preg_replace('~\?>\n?<\?php~', '', $compiled_content);
e586807d
H
374
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");
385 }
386 }
387 }
388
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";
392
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) {
b42b2bf9 399 $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
e586807d
H
400 $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
401 }
402 }
403 $_plugins_params .= '))';
b42b2bf9 404 $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
e586807d
H
405 $template_header .= $plugins_code;
406 $this->_plugin_info = array();
407 $this->_plugins_code = $plugins_code;
408 }
409
410 if ($this->_init_smarty_vars) {
b42b2bf9 411 $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
e586807d
H
412 $this->_init_smarty_vars = false;
413 }
414
415 $compiled_content = $template_header . $compiled_content;
e586807d
H
416 return true;
417 }
418
b42b2bf9
H
419 /**
420 * Compile a template tag
421 *
422 * @param string $template_tag
e586807d 423 * @return string
b42b2bf9 424 */
e586807d
H
425 function _compile_tag($template_tag)
426 {
e586807d
H
427 /* Matched comment. */
428 if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
429 return '';
430
431 /* Split tag into two three parts: command, command modifiers and the arguments. */
b42b2bf9
H
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 . '*))
e586807d 434 (?:\s+(.*))?$
b42b2bf9
H
435 ~xs', $template_tag, $match)) {
436 $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
437 }
e586807d
H
438
439 $tag_command = $match[1];
440 $tag_modifier = isset($match[2]) ? $match[2] : null;
441 $tag_args = isset($match[3]) ? $match[3] : null;
442
b42b2bf9
H
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;
447 }
448
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);
452 }
e586807d
H
453
454 switch ($tag_command) {
455 case 'include':
456 return $this->_compile_include_tag($tag_args);
457
458 case 'include_php':
459 return $this->_compile_include_php_tag($tag_args);
460
461 case 'if':
b42b2bf9 462 $this->_push_tag('if');
e586807d
H
463 return $this->_compile_if_tag($tag_args);
464
465 case 'else':
b42b2bf9
H
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');
e586807d
H
471 return '<?php else: ?>';
472
473 case 'elseif':
b42b2bf9
H
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');
e586807d
H
479 return $this->_compile_if_tag($tag_args, true);
480
481 case '/if':
b42b2bf9 482 $this->_pop_tag('if');
e586807d
H
483 return '<?php endif; ?>';
484
485 case 'capture':
486 return $this->_compile_capture_tag(true, $tag_args);
487
488 case '/capture':
489 return $this->_compile_capture_tag(false);
490
491 case 'ldelim':
492 return $this->left_delimiter;
493
494 case 'rdelim':
495 return $this->right_delimiter;
496
497 case 'section':
b42b2bf9 498 $this->_push_tag('section');
e586807d
H
499 return $this->_compile_section_start($tag_args);
500
501 case 'sectionelse':
b42b2bf9 502 $this->_push_tag('sectionelse');
e586807d 503 return "<?php endfor; else: ?>";
b42b2bf9 504 break;
e586807d
H
505
506 case '/section':
b42b2bf9
H
507 $_open_tag = $this->_pop_tag('section');
508 if ($_open_tag == 'sectionelse')
e586807d
H
509 return "<?php endif; ?>";
510 else
511 return "<?php endfor; endif; ?>";
512
513 case 'foreach':
b42b2bf9 514 $this->_push_tag('foreach');
e586807d
H
515 return $this->_compile_foreach_start($tag_args);
516 break;
517
518 case 'foreachelse':
b42b2bf9
H
519 $this->_push_tag('foreachelse');
520 return "<?php endforeach; else: ?>";
e586807d
H
521
522 case '/foreach':
b42b2bf9
H
523 $_open_tag = $this->_pop_tag('foreach');
524 if ($_open_tag == 'foreachelse')
525 return "<?php endif; unset(\$_from); ?>";
e586807d 526 else
b42b2bf9
H
527 return "<?php endforeach; endif; unset(\$_from); ?>";
528 break;
e586807d
H
529
530 case 'strip':
531 case '/strip':
532 if ($tag_command{0}=='/') {
b42b2bf9 533 $this->_pop_tag('strip');
e586807d
H
534 if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
535 $this->_additional_newline = "\n";
b42b2bf9 536 return '{' . $tag_command . '}';
e586807d
H
537 }
538 } else {
b42b2bf9 539 $this->_push_tag('strip');
e586807d
H
540 if ($this->_strip_depth++==0) { /* outermost opening {strip} */
541 $this->_additional_newline = "";
b42b2bf9 542 return '{' . $tag_command . '}';
e586807d
H
543 }
544 }
545 return '';
546
e586807d 547 case 'php':
b42b2bf9
H
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 '';
556
557 case 3: /* literal */
558 return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
559
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;
564 }
565 return '<?php ' . $block[3] .' ?>';
e586807d 566 }
b42b2bf9 567 break;
e586807d
H
568
569 case 'insert':
570 return $this->_compile_insert_tag($tag_args);
571
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;
b42b2bf9
H
577 } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
578 return $output;
e586807d 579 } else {
b42b2bf9 580 $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
e586807d 581 }
b42b2bf9 582
e586807d
H
583 }
584 }
585
586
b42b2bf9
H
587 /**
588 * compile the custom compiler tag
589 *
e586807d 590 * sets $output to the compiled custom compiler tag
b42b2bf9
H
591 * @param string $tag_command
592 * @param string $tag_args
593 * @param string $output
e586807d 594 * @return boolean
b42b2bf9 595 */
e586807d
H
596 function _compile_compiler_tag($tag_command, $tag_args, &$output)
597 {
598 $found = false;
599 $have_function = true;
600
601 /*
602 * First we check if the compiler function has already been registered
603 * or loaded from a plugin file.
604 */
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;
611 }
612 }
613 /*
614 * Otherwise we need to load plugin file and look for the function
615 * inside it.
616 */
617 else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
618 $found = true;
619
620 include_once $plugin_file;
621
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);
628 }
629 }
630
631 /*
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.
636 */
637 if ($found) {
638 if ($have_function) {
639 $output = call_user_func_array($plugin_func, array($tag_args, &$this));
b42b2bf9 640 if($output != '') {
e586807d
H
641 $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
642 . $output
643 . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
b42b2bf9 644 }
e586807d
H
645 } else {
646 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
647 }
648 return true;
649 } else {
650 return false;
651 }
652 }
653
654
b42b2bf9
H
655 /**
656 * compile block function tag
657 *
e586807d 658 * sets $output to compiled block function tag
b42b2bf9
H
659 * @param string $tag_command
660 * @param string $tag_args
661 * @param string $tag_modifier
662 * @param string $output
e586807d 663 * @return boolean
b42b2bf9 664 */
e586807d
H
665 function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
666 {
667 if ($tag_command{0} == '/') {
668 $start_tag = false;
669 $tag_command = substr($tag_command, 1);
670 } else
671 $start_tag = true;
672
673 $found = false;
674 $have_function = true;
675
676 /*
677 * First we check if the block function has already been registered
678 * or loaded from a plugin file.
679 */
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;
686 }
687 }
688 /*
689 * Otherwise we need to load plugin file and look for the function
690 * inside it.
691 */
692 else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
693 $found = true;
694
695 include_once $plugin_file;
696
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);
703
704 }
705 }
706
707 if (!$found) {
708 return false;
709 } else if (!$have_function) {
710 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
711 return true;
712 }
713
714 /*
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.
718 */
719 $this->_add_plugin('block', $tag_command);
720
b42b2bf9
H
721 if ($start_tag)
722 $this->_push_tag($tag_command);
723 else
724 $this->_pop_tag($tag_command);
725
e586807d
H
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='');
b42b2bf9
H
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);';
e586807d
H
732 $output .= 'while ($_block_repeat) { ob_start(); ?>';
733 } else {
b42b2bf9
H
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)';
e586807d
H
736 if ($tag_modifier != '') {
737 $this->_parse_modifiers($_out_tag_text, $tag_modifier);
738 }
739 $output .= 'echo '.$_out_tag_text.'; } ';
b42b2bf9 740 $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
e586807d
H
741 }
742
743 return true;
744 }
745
746
b42b2bf9
H
747 /**
748 * compile custom function tag
749 *
750 * @param string $tag_command
751 * @param string $tag_args
752 * @param string $tag_modifier
e586807d 753 * @return string
b42b2bf9
H
754 */
755 function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
e586807d 756 {
b42b2bf9
H
757 $found = false;
758 $have_function = true;
759
760 /*
761 * First we check if the custom function has already been registered
762 * or loaded from a plugin file.
763 */
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;
770 }
771 }
772 /*
773 * Otherwise we need to load plugin file and look for the function
774 * inside it.
775 */
776 else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
777 $found = true;
778
779 include_once $plugin_file;
780
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);
787
788 }
789 }
790
791 if (!$found) {
792 return false;
793 } else if (!$have_function) {
794 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
795 return true;
796 }
797
798 /* declare plugin to be loaded on display of the template that
799 we compile right now */
e586807d
H
800 $this->_add_plugin('function', $tag_command);
801
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='');
805
b42b2bf9
H
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);
809 }
e586807d 810
b42b2bf9
H
811 if($output != '') {
812 $output = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
e586807d 813 . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
b42b2bf9 814 }
e586807d 815
b42b2bf9 816 return true;
e586807d
H
817 }
818
b42b2bf9
H
819 /**
820 * compile a registered object tag
821 *
822 * @param string $tag_command
823 * @param array $attrs
824 * @param string $tag_modifier
e586807d 825 * @return string
b42b2bf9 826 */
e586807d
H
827 function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
828 {
829 if ($tag_command{0} == '/') {
830 $start_tag = false;
831 $tag_command = substr($tag_command, 1);
832 } else {
833 $start_tag = true;
834 }
835
b42b2bf9 836 list($object, $obj_comp) = explode('->', $tag_command);
e586807d
H
837
838 $arg_list = array();
b42b2bf9
H
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;
846 }
847 if (is_bool($arg_value))
848 $arg_value = $arg_value ? 'true' : 'false';
849 $arg_list[] = "'$arg_name' => $arg_value";
850 }
851 }
e586807d
H
852
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';
861 }
862 }
863
864 $prefix = '';
865 $postfix = '';
866 $newline = '';
b42b2bf9
H
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
e586807d
H
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 = '';
b42b2bf9
H
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)";
e586807d
H
884 $postfix = "} array_pop(\$this->_tag_stack);";
885 }
886 } else {
887 // non-block method
888 $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
b42b2bf9
H
889 }
890 } else {
891 // property
892 $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
893 }
e586807d
H
894
895 if($return != null) {
896 if($tag_modifier != '') {
897 $this->_parse_modifiers($return, $tag_modifier);
898 }
899
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;
905 }
906 } else {
907 $output = '';
908 }
909
910 return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
911 }
912
b42b2bf9
H
913 /**
914 * Compile {insert ...} tag
915 *
916 * @param string $tag_args
e586807d 917 * @return string
b42b2bf9 918 */
e586807d
H
919 function _compile_insert_tag($tag_args)
920 {
921 $attrs = $this->_parse_attrs($tag_args);
922 $name = $this->_dequote($attrs['name']);
923
924 if (empty($name)) {
925 $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
926 }
927
928 if (!empty($attrs['script'])) {
929 $delayed_loading = true;
930 } else {
931 $delayed_loading = false;
b42b2bf9 932 }
e586807d
H
933
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";
938 }
939
940 $this->_add_plugin('insert', $name, $delayed_loading);
941
b42b2bf9 942 $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
e586807d 943
b42b2bf9 944 return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
e586807d
H
945 }
946
b42b2bf9
H
947 /**
948 * Compile {include ...} tag
949 *
950 * @param string $tag_args
e586807d 951 * @return string
b42b2bf9 952 */
e586807d
H
953 function _compile_include_tag($tag_args)
954 {
955 $attrs = $this->_parse_attrs($tag_args);
956 $arg_list = array();
957
958 if (empty($attrs['file'])) {
959 $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
960 }
961
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;
969 }
970 if (is_bool($arg_value))
971 $arg_value = $arg_value ? 'true' : 'false';
972 $arg_list[] = "'$arg_name' => $arg_value";
973 }
974
975 $output = '<?php ';
976
977 if (isset($assign_var)) {
b42b2bf9 978 $output .= "ob_start();\n";
e586807d
H
979 }
980
981 $output .=
982 "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
983
984
b42b2bf9
H
985 $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
986 $output .= "\$this->_smarty_include($_params);\n" .
e586807d
H
987 "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
988 "unset(\$_smarty_tpl_vars);\n";
989
990 if (isset($assign_var)) {
b42b2bf9 991 $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
e586807d
H
992 }
993
994 $output .= ' ?>';
995
b42b2bf9 996 return $output;
e586807d
H
997
998 }
999
b42b2bf9
H
1000 /**
1001 * Compile {include ...} tag
1002 *
1003 * @param string $tag_args
e586807d 1004 * @return string
b42b2bf9 1005 */
e586807d
H
1006 function _compile_include_php_tag($tag_args)
1007 {
1008 $attrs = $this->_parse_attrs($tag_args);
1009
1010 if (empty($attrs['file'])) {
1011 $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
1012 }
1013
b42b2bf9
H
1014 $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
1015 $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
e586807d 1016
b42b2bf9
H
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";
1023 }
1024 }
e586807d 1025
b42b2bf9 1026 $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
e586807d 1027
b42b2bf9 1028 return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
e586807d
H
1029 }
1030
1031
b42b2bf9
H
1032 /**
1033 * Compile {section ...} tag
1034 *
1035 * @param string $tag_args
e586807d 1036 * @return string
b42b2bf9 1037 */
e586807d
H
1038 function _compile_section_start($tag_args)
1039 {
1040 $attrs = $this->_parse_attrs($tag_args);
1041 $arg_list = array();
1042
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__);
1047 }
1048
b42b2bf9 1049 $output .= "unset(\$this->_sections[$section_name]);\n";
e586807d
H
1050 $section_props = "\$this->_sections[$section_name]";
1051
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;
1057
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;
1065
1066 case 'name':
1067 $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
1068 break;
1069
1070 case 'max':
1071 case 'start':
1072 $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
1073 break;
1074
1075 case 'step':
1076 $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
1077 break;
1078
1079 default:
1080 $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
1081 break;
1082 }
1083 }
1084
1085 if (!isset($attrs['show']))
1086 $output .= "{$section_props}['show'] = true;\n";
1087
1088 if (!isset($attrs['loop']))
1089 $output .= "{$section_props}['loop'] = 1;\n";
1090
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";
1096
1097 if (!isset($attrs['step']))
1098 $output .= "{$section_props}['step'] = 1;\n";
1099
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";
1107 }
1108
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";
1114 }
1115 $output .= " if ({$section_props}['total'] == 0)\n" .
1116 " {$section_props}['show'] = false;\n" .
1117 "} else\n" .
1118 " {$section_props}['total'] = 0;\n";
1119
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";
1130
1131 $output .= "?>";
1132
1133 return $output;
1134 }
1135
1136
b42b2bf9
H
1137 /**
1138 * Compile {foreach ...} tag.
1139 *
1140 * @param string $tag_args
e586807d 1141 * @return string
b42b2bf9 1142 */
e586807d
H
1143 function _compile_foreach_start($tag_args)
1144 {
1145 $attrs = $this->_parse_attrs($tag_args);
1146 $arg_list = array();
1147
1148 if (empty($attrs['from'])) {
b42b2bf9 1149 return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
e586807d 1150 }
b42b2bf9 1151 $from = $attrs['from'];
e586807d
H
1152
1153 if (empty($attrs['item'])) {
b42b2bf9 1154 return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
e586807d 1155 }
e586807d 1156 $item = $this->_dequote($attrs['item']);
b42b2bf9
H
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__);
e586807d
H
1159 }
1160
b42b2bf9
H
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__);
e586807d 1165 }
b42b2bf9
H
1166 $key_part = "\$this->_tpl_vars['$key'] => ";
1167 } else {
1168 $key = null;
1169 $key_part = '';
e586807d
H
1170 }
1171
b42b2bf9
H
1172 if (isset($attrs['name'])) {
1173 $name = $attrs['name'];
1174 } else {
1175 $name = null;
1176 }
1177
1178 $output = '<?php ';
1179 $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
e586807d 1180 if (isset($name)) {
b42b2bf9
H
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";
e586807d
H
1184 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1185 $output .= " {$foreach_props}['iteration']++;\n";
e586807d 1186 } else {
b42b2bf9 1187 $output .= "if (count(\$_from)):\n";
e586807d
H
1188 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1189 }
1190 $output .= '?>';
1191
1192 return $output;
1193 }
1194
1195
b42b2bf9
H
1196 /**
1197 * Compile {capture} .. {/capture} tags
1198 *
1199 * @param boolean $start true if this is the {capture} tag
1200 * @param string $tag_args
e586807d 1201 * @return string
b42b2bf9 1202 */
e586807d
H
1203
1204 function _compile_capture_tag($start, $tag_args = '')
1205 {
1206 $attrs = $this->_parse_attrs($tag_args);
1207
1208 if ($start) {
1209 if (isset($attrs['name']))
1210 $buffer = $attrs['name'];
1211 else
1212 $buffer = "'default'";
1213
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());";
1225 }
1226 $output .= "ob_end_clean(); ?>";
1227 }
1228
1229 return $output;
1230 }
1231
b42b2bf9
H
1232 /**
1233 * Compile {if ...} tag
1234 *
1235 * @param string $tag_args
1236 * @param boolean $elseif if true, uses elseif instead of if
e586807d 1237 * @return string
b42b2bf9 1238 */
e586807d
H
1239 function _compile_if_tag($tag_args, $elseif = false)
1240 {
1241
1242 /* Tokenize args for 'if' tag. */
b42b2bf9
H
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);
e586807d
H
1250
1251 $tokens = $match[0];
1252
b42b2bf9
H
1253 if(empty($tokens)) {
1254 $_error_msg .= $elseif ? "'elseif'" : "'if'";
1255 $_error_msg .= ' statement requires arguments';
1256 $this->_syntax_error($_error_msg, E_USER_ERROR, __FILE__, __LINE__);
1257 }
1258
1259
1260 // make sure we have balanced parenthesis
1261 $token_count = array_count_values($tokens);
1262 if(isset($token_count['(']) && $token_count['('] != $token_count[')']) {
1263 $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
1264 }
e586807d
H
1265
1266 $is_arg_stack = array();
1267
1268 for ($i = 0; $i < count($tokens); $i++) {
1269
1270 $token = &$tokens[$i];
1271
1272 switch (strtolower($token)) {
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 '||':
b42b2bf9
H
1288 case '|':
1289 case '^':
1290 case '&':
1291 case '~':
1292 case ')':
1293 case ',':
1294 case '+':
1295 case '-':
1296 case '*':
1297 case '/':
1298 case '@':
1299 break;
e586807d
H
1300
1301 case 'eq':
1302 $token = '==';
1303 break;
1304
1305 case 'ne':
1306 case 'neq':
1307 $token = '!=';
1308 break;
1309
1310 case 'lt':
1311 $token = '<';
1312 break;
1313
1314 case 'le':
1315 case 'lte':
1316 $token = '<=';
1317 break;
1318
1319 case 'gt':
1320 $token = '>';
1321 break;
1322
1323 case 'ge':
1324 case 'gte':
1325 $token = '>=';
1326 break;
1327
1328 case 'and':
1329 $token = '&&';
1330 break;
1331
1332 case 'or':
1333 $token = '||';
1334 break;
1335
1336 case 'not':
1337 $token = '!';
1338 break;
1339
1340 case 'mod':
1341 $token = '%';
1342 break;
1343
1344 case '(':
1345 array_push($is_arg_stack, $i);
1346 break;
1347
1348 case 'is':
1349 /* If last token was a ')', we operate on the parenthesized
1350 expression. The start of the expression is on the stack.
1351 Otherwise, we operate on the last encountered token. */
1352 if ($tokens[$i-1] == ')')
1353 $is_arg_start = array_pop($is_arg_stack);
1354 else
1355 $is_arg_start = $i-1;
1356 /* Construct the argument for 'is' expression, so it knows
1357 what to operate on. */
1358 $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
1359
1360 /* Pass all tokens from next one until the end to the
1361 'is' expression parsing function. The function will
1362 return modified tokens, where the first one is the result
1363 of the 'is' expression and the rest are the tokens it
1364 didn't touch. */
1365 $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
1366
1367 /* Replace the old tokens with the new ones. */
1368 array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
1369
1370 /* Adjust argument start so that it won't change from the
1371 current position for the next iteration. */
1372 $i = $is_arg_start;
1373 break;
1374
1375 default:
b42b2bf9
H
1376 if(preg_match('~^' . $this->_func_regexp . '$~', $token) ) {
1377 // function call
1378 if($this->security &&
1379 !in_array($token, $this->security_settings['IF_FUNCS'])) {
1380 $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1381 }
1382 } elseif(preg_match('~^' . $this->_var_regexp . '$~', $token) && isset($tokens[$i+1]) && $tokens[$i+1] == '(') {
1383 // variable function call
1384 $this->_syntax_error("variable function call '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
1385 } elseif(preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$~', $token)) {
1386 // object or variable
1387 $token = $this->_parse_var_props($token);
1388 } elseif(is_numeric($token)) {
1389 // number, skip it
1390 } else {
1391 $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
1392 }
e586807d
H
1393 break;
1394 }
1395 }
1396
1397 if ($elseif)
1398 return '<?php elseif ('.implode(' ', $tokens).'): ?>';
1399 else
1400 return '<?php if ('.implode(' ', $tokens).'): ?>';
1401 }
1402
1403
1404 function _compile_arg_list($type, $name, $attrs, &$cache_code) {
1405 $arg_list = array();
1406
1407 if (isset($type) && isset($name)
1408 && isset($this->_plugins[$type])
1409 && isset($this->_plugins[$type][$name])
1410 && empty($this->_plugins[$type][$name][4])
1411 && is_array($this->_plugins[$type][$name][5])
1412 ) {
1413 /* we have a list of parameters that should be cached */
1414 $_cache_attrs = $this->_plugins[$type][$name][5];
1415 $_count = $this->_cache_attrs_count++;
1416 $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
1417
1418 } else {
1419 /* no parameters are cached */
1420 $_cache_attrs = null;
1421 }
1422
1423 foreach ($attrs as $arg_name => $arg_value) {
1424 if (is_bool($arg_value))
1425 $arg_value = $arg_value ? 'true' : 'false';
1426 if (is_null($arg_value))
1427 $arg_value = 'null';
1428 if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
1429 $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
1430 } else {
1431 $arg_list[] = "'$arg_name' => $arg_value";
1432 }
1433 }
1434 return $arg_list;
1435 }
1436
b42b2bf9
H
1437 /**
1438 * Parse is expression
1439 *
1440 * @param string $is_arg
1441 * @param array $tokens
e586807d 1442 * @return array
b42b2bf9 1443 */
e586807d
H
1444 function _parse_is_expr($is_arg, $tokens)
1445 {
1446 $expr_end = 0;
1447 $negate_expr = false;
1448
1449 if (($first_token = array_shift($tokens)) == 'not') {
1450 $negate_expr = true;
1451 $expr_type = array_shift($tokens);
1452 } else
1453 $expr_type = $first_token;
1454
1455 switch ($expr_type) {
1456 case 'even':
b42b2bf9 1457 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
e586807d
H
1458 $expr_end++;
1459 $expr_arg = $tokens[$expr_end++];
b42b2bf9 1460 $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
e586807d 1461 } else
b42b2bf9 1462 $expr = "!(1 & $is_arg)";
e586807d
H
1463 break;
1464
1465 case 'odd':
b42b2bf9 1466 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
e586807d
H
1467 $expr_end++;
1468 $expr_arg = $tokens[$expr_end++];
b42b2bf9 1469 $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
e586807d 1470 } else
b42b2bf9 1471 $expr = "(1 & $is_arg)";
e586807d
H
1472 break;
1473
1474 case 'div':
1475 if (@$tokens[$expr_end] == 'by') {
1476 $expr_end++;
1477 $expr_arg = $tokens[$expr_end++];
1478 $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1479 } else {
1480 $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1481 }
1482 break;
1483
1484 default:
1485 $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1486 break;
1487 }
1488
1489 if ($negate_expr) {
1490 $expr = "!($expr)";
1491 }
1492
1493 array_splice($tokens, 0, $expr_end, $expr);
1494
1495 return $tokens;
1496 }
1497
1498
b42b2bf9
H
1499 /**
1500 * Parse attribute string
1501 *
1502 * @param string $tag_args
e586807d 1503 * @return array
b42b2bf9 1504 */
e586807d
H
1505 function _parse_attrs($tag_args)
1506 {
1507
1508 /* Tokenize tag attributes. */
b42b2bf9 1509 preg_match_all('~(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
e586807d
H
1510 )+ |
1511 [=]
b42b2bf9 1512 ~x', $tag_args, $match);
e586807d
H
1513 $tokens = $match[0];
1514
1515 $attrs = array();
1516 /* Parse state:
1517 0 - expecting attribute name
1518 1 - expecting '='
1519 2 - expecting attribute value (not '=') */
1520 $state = 0;
1521
1522 foreach ($tokens as $token) {
1523 switch ($state) {
1524 case 0:
1525 /* If the token is a valid identifier, we set attribute name
1526 and go to state 1. */
b42b2bf9 1527 if (preg_match('~^\w+$~', $token)) {
e586807d
H
1528 $attr_name = $token;
1529 $state = 1;
1530 } else
1531 $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1532 break;
1533
1534 case 1:
1535 /* If the token is '=', then we go to state 2. */
1536 if ($token == '=') {
1537 $state = 2;
1538 } else
1539 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1540 break;
1541
1542 case 2:
1543 /* If token is not '=', we set the attribute value and go to
1544 state 0. */
1545 if ($token != '=') {
1546 /* We booleanize the token if it's a non-quoted possible
1547 boolean value. */
b42b2bf9 1548 if (preg_match('~^(on|yes|true)$~', $token)) {
e586807d 1549 $token = 'true';
b42b2bf9 1550 } else if (preg_match('~^(off|no|false)$~', $token)) {
e586807d 1551 $token = 'false';
b42b2bf9 1552 } else if ($token == 'null') {
e586807d 1553 $token = 'null';
b42b2bf9 1554 } else if (preg_match('~^' . $this->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
e586807d 1555 /* treat integer literally */
b42b2bf9
H
1556 } else if (!preg_match('~^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$~', $token)) {
1557 /* treat as a string, double-quote it escaping quotes */
e586807d 1558 $token = '"'.addslashes($token).'"';
b42b2bf9 1559 }
e586807d
H
1560
1561 $attrs[$attr_name] = $token;
1562 $state = 0;
1563 } else
1564 $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1565 break;
1566 }
b42b2bf9 1567 $last_token = $token;
e586807d
H
1568 }
1569
b42b2bf9
H
1570 if($state != 0) {
1571 if($state == 1) {
1572 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1573 } else {
1574 $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
1575 }
1576 }
e586807d
H
1577
1578 $this->_parse_vars_props($attrs);
1579
1580 return $attrs;
1581 }
1582
b42b2bf9
H
1583 /**
1584 * compile multiple variables and section properties tokens into
1585 * PHP code
1586 *
1587 * @param array $tokens
1588 */
e586807d
H
1589 function _parse_vars_props(&$tokens)
1590 {
b42b2bf9
H
1591 foreach($tokens as $key => $val) {
1592 $tokens[$key] = $this->_parse_var_props($val);
1593 }
1594 }
1595
1596 /**
1597 * compile single variable and section properties token into
1598 * PHP code
1599 *
1600 * @param string $val
1601 * @param string $tag_attrs
e586807d 1602 * @return string
b42b2bf9
H
1603 */
1604 function _parse_var_props($val)
e586807d 1605 {
b42b2bf9
H
1606 $val = trim($val);
1607
1608 if(preg_match('~^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$~', $val, $match)) {
1609 // $ variable or object
1610 $return = $this->_parse_var($match[1]);
1611 $modifiers = $match[2];
1612 if (!empty($this->default_modifiers) && !preg_match('~(^|\|)smarty:nodefaults($|\|)~',$modifiers)) {
1613 $_default_mod_string = implode('|',(array)$this->default_modifiers);
1614 $modifiers = empty($modifiers) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers;
1615 }
1616 $this->_parse_modifiers($return, $modifiers);
1617 return $return;
1618 } elseif (preg_match('~^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1619 // double quoted text
1620 preg_match('~^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
e586807d 1621 $return = $this->_expand_quoted_text($match[1]);
b42b2bf9
H
1622 if($match[2] != '') {
1623 $this->_parse_modifiers($return, $match[2]);
1624 }
1625 return $return;
1626 }
1627 elseif(preg_match('~^' . $this->_num_const_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1628 // numerical constant
1629 preg_match('~^(' . $this->_num_const_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1630 if($match[2] != '') {
1631 $this->_parse_modifiers($match[1], $match[2]);
1632 return $match[1];
1633 }
1634 }
1635 elseif(preg_match('~^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1636 // single quoted text
1637 preg_match('~^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$~', $val, $match);
1638 if($match[2] != '') {
1639 $this->_parse_modifiers($match[1], $match[2]);
1640 return $match[1];
1641 }
1642 }
1643 elseif(preg_match('~^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1644 // config var
e586807d 1645 return $this->_parse_conf_var($val);
b42b2bf9
H
1646 }
1647 elseif(preg_match('~^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$~', $val)) {
1648 // section var
e586807d 1649 return $this->_parse_section_prop($val);
b42b2bf9
H
1650 }
1651 elseif(!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
1652 // literal string
1653 return $this->_expand_quoted_text('"' . strtr($val, array('\\' => '\\\\', '"' => '\\"')) .'"');
1654 }
1655 return $val;
e586807d
H
1656 }
1657
b42b2bf9
H
1658 /**
1659 * expand quoted text with embedded variables
1660 *
1661 * @param string $var_expr
e586807d 1662 * @return string
b42b2bf9 1663 */
e586807d
H
1664 function _expand_quoted_text($var_expr)
1665 {
b42b2bf9
H
1666 // if contains unescaped $, expand it
1667 if(preg_match_all('~(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '(?:' . $this->_obj_ext_regexp . ')*\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)~', $var_expr, $_match)) {
1668 $_match = $_match[0];
1669 rsort($_match);
1670 reset($_match);
1671 foreach($_match as $_var) {
1672 $var_expr = str_replace ($_var, '".(' . $this->_parse_var(str_replace('`','',$_var)) . ')."', $var_expr);
1673 }
1674 $_return = preg_replace('~\.""|(?<!\\\\)""\.~', '', $var_expr);
1675 } else {
1676 $_return = $var_expr;
1677 }
1678 // replace double quoted literal string with single quotes
1679 $_return = preg_replace('~^"([\s\w]+)"$~',"'\\1'",$_return);
1680 return $_return;
1681 }
1682
1683 /**
1684 * parse variable expression into PHP code
1685 *
1686 * @param string $var_expr
1687 * @param string $output
e586807d 1688 * @return string
b42b2bf9 1689 */
e586807d
H
1690 function _parse_var($var_expr)
1691 {
b42b2bf9
H
1692 $_has_math = false;
1693 $_math_vars = preg_split('~('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')~', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
1694
1695 if(count($_math_vars) > 1) {
1696 $_first_var = "";
1697 $_complete_var = "";
1698 $_output = "";
1699 // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
1700 foreach($_math_vars as $_k => $_math_var) {
1701 $_math_var = $_math_vars[$_k];
1702
1703 if(!empty($_math_var) || is_numeric($_math_var)) {
1704 // hit a math operator, so process the stuff which came before it
1705 if(preg_match('~^' . $this->_dvar_math_regexp . '$~', $_math_var)) {
1706 $_has_math = true;
1707 if(!empty($_complete_var) || is_numeric($_complete_var)) {
1708 $_output .= $this->_parse_var($_complete_var);
1709 }
1710
1711 // just output the math operator to php
1712 $_output .= $_math_var;
1713
1714 if(empty($_first_var))
1715 $_first_var = $_complete_var;
1716
1717 $_complete_var = "";
1718 } else {
1719 $_complete_var .= $_math_var;
1720 }
1721 }
1722 }
1723 if($_has_math) {
1724 if(!empty($_complete_var) || is_numeric($_complete_var))
1725 $_output .= $this->_parse_var($_complete_var);
1726
1727 // get the modifiers working (only the last var from math + modifier is left)
1728 $var_expr = $_complete_var;
e586807d 1729 }
b42b2bf9
H
1730 }
1731
1732 // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
1733 if(is_numeric($var_expr{0}))
1734 $_var_ref = $var_expr;
1735 else
1736 $_var_ref = substr($var_expr, 1);
1737
1738 if(!$_has_math) {
1739
1740 // get [foo] and .foo and ->foo and (...) pieces
1741 preg_match_all('~(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+~', $_var_ref, $match);
1742
1743 $_indexes = $match[0];
1744 $_var_name = array_shift($_indexes);
1745
1746 /* Handle $smarty.* variable references as a special case. */
1747 if ($_var_name == 'smarty') {
1748 /*
1749 * If the reference could be compiled, use the compiled output;
1750 * otherwise, fall back on the $smarty variable generated at
1751 * run-time.
1752 */
1753 if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
1754 $_output = $smarty_ref;
e586807d 1755 } else {
b42b2bf9
H
1756 $_var_name = substr(array_shift($_indexes), 1);
1757 $_output = "\$this->_smarty_vars['$_var_name']";
e586807d 1758 }
b42b2bf9
H
1759 } elseif(is_numeric($_var_name) && is_numeric($var_expr{0})) {
1760 // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
1761 if(count($_indexes) > 0)
1762 {
1763 $_var_name .= implode("", $_indexes);
1764 $_indexes = array();
1765 }
1766 $_output = $_var_name;
1767 } else {
1768 $_output = "\$this->_tpl_vars['$_var_name']";
1769 }
1770
1771 foreach ($_indexes as $_index) {
1772 if ($_index{0} == '[') {
1773 $_index = substr($_index, 1, -1);
1774 if (is_numeric($_index)) {
1775 $_output .= "[$_index]";
1776 } elseif ($_index{0} == '$') {
1777 if (strpos($_index, '.') !== false) {
1778 $_output .= '[' . $this->_parse_var($_index) . ']';
1779 } else {
1780 $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
1781 }
e586807d 1782 } else {
b42b2bf9
H
1783 $_var_parts = explode('.', $_index);
1784 $_var_section = $_var_parts[0];
1785 $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
1786 $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
e586807d 1787 }
b42b2bf9
H
1788 } else if ($_index{0} == '.') {
1789 if ($_index{1} == '$')
1790 $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
1791 else
1792 $_output .= "['" . substr($_index, 1) . "']";
1793 } else if (substr($_index,0,2) == '->') {
1794 if(substr($_index,2,2) == '__') {
1795 $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1796 } elseif($this->security && substr($_index, 2, 1) == '_') {
1797 $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1798 } elseif ($_index{2} == '$') {
1799 if ($this->security) {
1800 $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
1801 } else {
1802 $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index,3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
1803 }
1804 } else {
1805 $_output .= $_index;
1806 }
1807 } elseif ($_index{0} == '(') {
1808 $_index = $this->_parse_parenth_args($_index);
1809 $_output .= $_index;
e586807d
H
1810 } else {
1811 $_output .= $_index;
1812 }
e586807d
H
1813 }
1814 }
1815
e586807d
H
1816 return $_output;
1817 }
1818
b42b2bf9
H
1819 /**
1820 * parse arguments in function call parenthesis
1821 *
1822 * @param string $parenth_args
e586807d 1823 * @return string
b42b2bf9 1824 */
e586807d
H
1825 function _parse_parenth_args($parenth_args)
1826 {
b42b2bf9
H
1827 preg_match_all('~' . $this->_param_regexp . '~',$parenth_args, $match);
1828 $orig_vals = $match = $match[0];
1829 $this->_parse_vars_props($match);
1830 $replace = array();
1831 for ($i = 0, $count = count($match); $i < $count; $i++) {
1832 $replace[$orig_vals[$i]] = $match[$i];
1833 }
1834 return strtr($parenth_args, $replace);
1835 }
1836
1837 /**
1838 * parse configuration variable expression into PHP code
1839 *
1840 * @param string $conf_var_expr
1841 */
e586807d
H
1842 function _parse_conf_var($conf_var_expr)
1843 {
1844 $parts = explode('|', $conf_var_expr, 2);
1845 $var_ref = $parts[0];
1846 $modifiers = isset($parts[1]) ? $parts[1] : '';
1847
1848 $var_name = substr($var_ref, 1, -1);
1849
1850 $output = "\$this->_config[0]['vars']['$var_name']";
1851
1852 $this->_parse_modifiers($output, $modifiers);
1853
1854 return $output;
1855 }
1856
b42b2bf9
H
1857 /**
1858 * parse section property expression into PHP code
1859 *
1860 * @param string $section_prop_expr
e586807d 1861 * @return string
b42b2bf9 1862 */
e586807d
H
1863 function _parse_section_prop($section_prop_expr)
1864 {
1865 $parts = explode('|', $section_prop_expr, 2);
1866 $var_ref = $parts[0];
1867 $modifiers = isset($parts[1]) ? $parts[1] : '';
1868
1869 preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
1870 $section_name = $match[1];
1871 $prop_name = $match[2];
1872
1873 $output = "\$this->_sections['$section_name']['$prop_name']";
1874
1875 $this->_parse_modifiers($output, $modifiers);
1876
1877 return $output;
1878 }
1879
1880
b42b2bf9
H
1881 /**
1882 * parse modifier chain into PHP code
1883 *
e586807d 1884 * sets $output to parsed modified chain
b42b2bf9
H
1885 * @param string $output
1886 * @param string $modifier_string
1887 */
e586807d
H
1888 function _parse_modifiers(&$output, $modifier_string)
1889 {
b42b2bf9 1890 preg_match_all('~\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)~', '|' . $modifier_string, $_match);
e586807d
H
1891 list(, $_modifiers, $modifier_arg_strings) = $_match;
1892
1893 for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
1894 $_modifier_name = $_modifiers[$_i];
1895
b42b2bf9
H
1896 if($_modifier_name == 'smarty') {
1897 // skip smarty modifier
1898 continue;
1899 }
e586807d 1900
b42b2bf9 1901 preg_match_all('~:(' . $this->_qstr_regexp . '|[^:]+)~', $modifier_arg_strings[$_i], $_match);
e586807d
H
1902 $_modifier_args = $_match[1];
1903
1904 if ($_modifier_name{0} == '@') {
1905 $_map_array = false;
1906 $_modifier_name = substr($_modifier_name, 1);
1907 } else {
1908 $_map_array = true;
1909 }
1910
e586807d
H
1911 if (empty($this->_plugins['modifier'][$_modifier_name])
1912 && !$this->_get_plugin_filepath('modifier', $_modifier_name)
1913 && function_exists($_modifier_name)) {
1914 if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
b42b2bf9 1915 $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
e586807d
H
1916 } else {
1917 $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false);
1918 }
1919 }
b42b2bf9 1920 $this->_add_plugin('modifier', $_modifier_name);
e586807d
H
1921
1922 $this->_parse_vars_props($_modifier_args);
1923
b42b2bf9
H
1924 if($_modifier_name == 'default') {
1925 // supress notifications of default modifier vars and args
1926 if($output{0} == '$') {
1927 $output = '@' . $output;
1928 }
1929 if(isset($_modifier_args[0]) && $_modifier_args[0]{0} == '$') {
1930 $_modifier_args[0] = '@' . $_modifier_args[0];
1931 }
1932 }
e586807d
H
1933 if (count($_modifier_args) > 0)
1934 $_modifier_args = ', '.implode(', ', $_modifier_args);
1935 else
1936 $_modifier_args = '';
1937
1938 if ($_map_array) {
1939 $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))";
1940
1941 } else {
1942
1943 $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
1944
1945 }
1946 }
1947 }
1948
1949
b42b2bf9
H
1950 /**
1951 * add plugin
1952 *
1953 * @param string $type
1954 * @param string $name
1955 * @param boolean? $delayed_loading
1956 */
e586807d
H
1957 function _add_plugin($type, $name, $delayed_loading = null)
1958 {
1959 if (!isset($this->_plugin_info[$type])) {
1960 $this->_plugin_info[$type] = array();
1961 }
1962 if (!isset($this->_plugin_info[$type][$name])) {
1963 $this->_plugin_info[$type][$name] = array($this->_current_file,
1964 $this->_current_line_no,
1965 $delayed_loading);
1966 }
1967 }
1968
1969
b42b2bf9
H
1970 /**
1971 * Compiles references of type $smarty.foo
1972 *
1973 * @param string $indexes
e586807d 1974 * @return string
b42b2bf9 1975 */
e586807d
H
1976 function _compile_smarty_ref(&$indexes)
1977 {
1978 /* Extract the reference name. */
1979 $_ref = substr($indexes[0], 1);
1980 foreach($indexes as $_index_no=>$_index) {
b42b2bf9
H
1981 if ($_index{0} != '.' && $_index_no<2 || !preg_match('~^(\.|\[|->)~', $_index)) {
1982 $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
1983 }
1984 }
e586807d
H
1985
1986 switch ($_ref) {
1987 case 'now':
1988 $compiled_ref = 'time()';
b42b2bf9 1989 $_max_index = 1;
e586807d
H
1990 break;
1991
1992 case 'foreach':
b42b2bf9
H
1993 array_shift($indexes);
1994 $_var = $this->_parse_var_props(substr($indexes[0], 1));
1995 $_propname = substr($indexes[1], 1);
1996 $_max_index = 1;
1997 switch ($_propname) {
1998 case 'index':
1999 array_shift($indexes);
2000 $compiled_ref = "(\$this->_foreach[$_var]['iteration']-1)";
2001 break;
2002
2003 case 'first':
2004 array_shift($indexes);
2005 $compiled_ref = "(\$this->_foreach[$_var]['iteration'] <= 1)";
2006 break;
2007
2008 case 'last':
2009 array_shift($indexes);
2010 $compiled_ref = "(\$this->_foreach[$_var]['iteration'] == \$this->_foreach[$_var]['total'])";
2011 break;
2012
2013 case 'show':
2014 array_shift($indexes);
2015 $compiled_ref = "(\$this->_foreach[$_var]['total'] > 0)";
2016 break;
2017
2018 default:
2019 unset($_max_index);
2020 $compiled_ref = "\$this->_foreach[$_var]";
2021 }
2022 break;
2023
e586807d
H
2024 case 'section':
2025 array_shift($indexes);
2026 $_var = $this->_parse_var_props(substr($indexes[0], 1));
b42b2bf9 2027 $compiled_ref = "\$this->_sections[$_var]";
e586807d
H
2028 break;
2029
2030 case 'get':
2031 $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
2032 break;
2033
2034 case 'post':
2035 $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
2036 break;
2037
2038 case 'cookies':
2039 $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
2040 break;
2041
2042 case 'env':
2043 $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
2044 break;
2045
2046 case 'server':
2047 $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
2048 break;
2049
2050 case 'session':
2051 $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
2052 break;
2053
2054 /*
2055 * These cases are handled either at run-time or elsewhere in the
2056 * compiler.
2057 */
2058 case 'request':
2059 if ($this->request_use_auto_globals) {
2060 $compiled_ref = '$_REQUEST';
2061 break;
2062 } else {
2063 $this->_init_smarty_vars = true;
2064 }
2065 return null;
2066
2067 case 'capture':
2068 return null;
2069
2070 case 'template':
2071 $compiled_ref = "'$this->_current_file'";
b42b2bf9 2072 $_max_index = 1;
e586807d
H
2073 break;
2074
b42b2bf9
H
2075 case 'version':
2076 $compiled_ref = "'$this->_version'";
2077 $_max_index = 1;
2078 break;
e586807d 2079
b42b2bf9
H
2080 case 'const':
2081 if ($this->security && !$this->security_settings['ALLOW_CONSTANTS']) {
2082 $this->_syntax_error("(secure mode) constants not permitted",
2083 E_USER_WARNING, __FILE__, __LINE__);
2084 return;
2085 }
e586807d 2086 array_shift($indexes);
b42b2bf9
H
2087 if (preg_match('!^\.\w+$!', $indexes[0])) {
2088 $compiled_ref = '@' . substr($indexes[0], 1);
2089 } else {
2090 $_val = $this->_parse_var_props(substr($indexes[0], 1));
2091 $compiled_ref = '@constant(' . $_val . ')';
2092 }
2093 $_max_index = 1;
e586807d
H
2094 break;
2095
2096 case 'config':
2097 $compiled_ref = "\$this->_config[0]['vars']";
b42b2bf9
H
2098 $_max_index = 3;
2099 break;
2100
2101 case 'ldelim':
2102 $compiled_ref = "'$this->left_delimiter'";
2103 break;
2104
2105 case 'rdelim':
2106 $compiled_ref = "'$this->right_delimiter'";
e586807d
H
2107 break;
2108
2109 default:
2110 $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
2111 break;
2112 }
2113
2114 if (isset($_max_index) && count($indexes) > $_max_index) {
2115 $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
2116 }
2117
2118 array_shift($indexes);
2119 return $compiled_ref;
2120 }
2121
2122 /**
2123 * compiles call to plugin of type $type with name $name
2124 * returns a string containing the function-name or method call
2125 * without the paramter-list that would have follow to make the
2126 * call valid php-syntax
2127 *
2128 * @param string $type
2129 * @param string $name
2130 * @return string
2131 */
2132 function _compile_plugin_call($type, $name) {
2133 if (isset($this->_plugins[$type][$name])) {
2134 /* plugin loaded */
2135 if (is_array($this->_plugins[$type][$name][0])) {
2136 return ((is_object($this->_plugins[$type][$name][0][0])) ?
2137 "\$this->_plugins['$type']['$name'][0][0]->" /* method callback */
2138 : (string)($this->_plugins[$type][$name][0][0]).'::' /* class callback */
2139 ). $this->_plugins[$type][$name][0][1];
2140
2141 } else {
2142 /* function callback */
2143 return $this->_plugins[$type][$name][0];
2144
2145 }
2146 } else {
2147 /* plugin not loaded -> auto-loadable-plugin */
2148 return 'smarty_'.$type.'_'.$name;
2149
2150 }
2151 }
2152
b42b2bf9
H
2153 /**
2154 * load pre- and post-filters
2155 */
e586807d
H
2156 function _load_filters()
2157 {
2158 if (count($this->_plugins['prefilter']) > 0) {
2159 foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
2160 if ($prefilter === false) {
2161 unset($this->_plugins['prefilter'][$filter_name]);
b42b2bf9
H
2162 $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
2163 require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
e586807d
H
2164 smarty_core_load_plugins($_params, $this);
2165 }
2166 }
2167 }
2168 if (count($this->_plugins['postfilter']) > 0) {
2169 foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
2170 if ($postfilter === false) {
2171 unset($this->_plugins['postfilter'][$filter_name]);
b42b2bf9
H
2172 $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
2173 require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
e586807d
H
2174 smarty_core_load_plugins($_params, $this);
2175 }
2176 }
2177 }
2178 }
2179
2180
b42b2bf9
H
2181 /**
2182 * Quote subpattern references
2183 *
2184 * @param string $string
2185 * @return string
2186 */
2187 function _quote_replace($string)
2188 {
2189 return strtr($string, array('\\' => '\\\\', '$' => '\\$'));
2190 }
2191
2192 /**
2193 * display Smarty syntax error
2194 *
2195 * @param string $error_msg
2196 * @param integer $error_type
2197 * @param string $file
2198 * @param integer $line
2199 */
e586807d
H
2200 function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null)
2201 {
b42b2bf9 2202 $this->_trigger_fatal_error("syntax error: $error_msg", $this->_current_file, $this->_current_line_no, $file, $line, $error_type);
e586807d
H
2203 }
2204
2205
2206 /**
2207 * check if the compilation changes from cacheable to
2208 * non-cacheable state with the beginning of the current
2209 * plugin. return php-code to reflect the transition.
2210 * @return string
2211 */
2212 function _push_cacheable_state($type, $name) {
2213 $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2214 if ($_cacheable
2215 || 0<$this->_cacheable_state++) return '';
2216 if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
b42b2bf9 2217 $_ret = 'if ($this->caching && !$this->_cache_including) { echo \'{nocache:'
e586807d
H
2218 . $this->_cache_serial . '#' . $this->_nocache_count
2219 . '}\';}';
2220 return $_ret;
2221 }
2222
2223
2224 /**
2225 * check if the compilation changes from non-cacheable to
2226 * cacheable state with the end of the current plugin return
2227 * php-code to reflect the transition.
2228 * @return string
2229 */
2230 function _pop_cacheable_state($type, $name) {
2231 $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
2232 if ($_cacheable
2233 || --$this->_cacheable_state>0) return '';
b42b2bf9 2234 return 'if ($this->caching && !$this->_cache_including) { echo \'{/nocache:'
e586807d
H
2235 . $this->_cache_serial . '#' . ($this->_nocache_count++)
2236 . '}\';}';
2237 }
2238
b42b2bf9
H
2239
2240 /**
2241 * push opening tag-name, file-name and line-number on the tag-stack
2242 * @param string the opening tag's name
2243 */
2244 function _push_tag($open_tag)
2245 {
2246 array_push($this->_tag_stack, array($open_tag, $this->_current_line_no));
2247 }
2248
2249 /**
2250 * pop closing tag-name
2251 * raise an error if this stack-top doesn't match with the closing tag
2252 * @param string the closing tag's name
2253 * @return string the opening tag's name
2254 */
2255 function _pop_tag($close_tag)
2256 {
2257 $message = '';
2258 if (count($this->_tag_stack)>0) {
2259 list($_open_tag, $_line_no) = array_pop($this->_tag_stack);
2260 if ($close_tag == $_open_tag) {
2261 return $_open_tag;
2262 }
2263 if ($close_tag == 'if' && ($_open_tag == 'else' || $_open_tag == 'elseif' )) {
2264 return $this->_pop_tag($close_tag);
2265 }
2266 if ($close_tag == 'section' && $_open_tag == 'sectionelse') {
2267 $this->_pop_tag($close_tag);
2268 return $_open_tag;
2269 }
2270 if ($close_tag == 'foreach' && $_open_tag == 'foreachelse') {
2271 $this->_pop_tag($close_tag);
2272 return $_open_tag;
2273 }
2274 if ($_open_tag == 'else' || $_open_tag == 'elseif') {
2275 $_open_tag = 'if';
2276 } elseif ($_open_tag == 'sectionelse') {
2277 $_open_tag = 'section';
2278 } elseif ($_open_tag == 'foreachelse') {
2279 $_open_tag = 'foreach';
2280 }
2281 $message = " expected {/$_open_tag} (opened line $_line_no).";
2282 }
2283 $this->_syntax_error("mismatched tag {/$close_tag}.$message",
2284 E_USER_ERROR, __FILE__, __LINE__);
2285 }
2286
e586807d
H
2287}
2288
2289/**
2290 * compare to values by their string length
2291 *
2292 * @access private
2293 * @param string $a
2294 * @param string $b
2295 * @return 0|-1|1
2296 */
2297function _smarty_sort_length($a, $b)
2298{
b42b2bf9
H
2299 if($a == $b)
2300 return 0;
e586807d 2301
b42b2bf9
H
2302 if(strlen($a) == strlen($b))
2303 return ($a > $b) ? -1 : 1;
e586807d 2304
b42b2bf9 2305 return (strlen($a) > strlen($b)) ? -1 : 1;
e586807d
H
2306}
2307
2308
2309/* vim: set et: */
2310
2311?>
This page took 1.811174 seconds and 4 git commands to generate.