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