source: trunk/www.guidonia.net/wp/wp-content/plugins/odlinks/includes/Smarty/Smarty_Compiler.class.php@ 44

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