/[cvs]/nfo/php/libs/net.php.smarty/Smarty_Compiler.class.php
ViewVC logotype

Contents of /nfo/php/libs/net.php.smarty/Smarty_Compiler.class.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (show annotations)
Wed Jun 16 21:58:11 2004 UTC (20 years ago) by joko
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +1339 -515 lines
updated to smarty-2.6.3

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

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed