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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (hide 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 cvsjoko 1.1 <?php
2    
3 joko 1.3 /**
4 cvsjoko 1.1 * 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 joko 1.3 * @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 cvsjoko 1.1 */
28    
29 joko 1.3 /* $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 cvsjoko 1.1 class Smarty_Compiler extends Smarty {
36    
37     // internal vars
38 joko 1.3 /**#@+
39     * @access private
40     */
41     var $_folded_blocks = array(); // keeps folded template blocks
42 cvsjoko 1.1 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 joko 1.3 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 cvsjoko 1.1
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 joko 1.3 $this->_current_file = $resource_name;
230 cvsjoko 1.1 $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 joko 1.3 if ($prefilter[3] || is_callable($prefilter[0])) {
239     $source_content = call_user_func_array($prefilter[0],
240     array($source_content, &$this));
241 cvsjoko 1.1 $this->_plugins['prefilter'][$filter_name][3] = true;
242     } else {
243 joko 1.3 $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
244 cvsjoko 1.1 }
245     }
246     }
247    
248 joko 1.3 /* 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 cvsjoko 1.1
251 joko 1.3 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 cvsjoko 1.1
263     /* Gather all template tags. */
264 joko 1.3 preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $source_content, $_match);
265     $template_tags = $_match[1];
266 cvsjoko 1.1 /* Split content by template tags to obtain non-template content. */
267 joko 1.3 $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $source_content);
268 cvsjoko 1.1
269     /* loop through text blocks */
270     for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
271 joko 1.3 /* 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 cvsjoko 1.1 }
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 joko 1.3 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 cvsjoko 1.1
312 joko 1.3 $compiled_content = '';
313 cvsjoko 1.1
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 joko 1.3 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 cvsjoko 1.1 }
322 joko 1.3 $compiled_content .= $text_blocks[$i];
323 cvsjoko 1.1
324     /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
325 joko 1.3 if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_content, $_match)) {
326     $strip_tags = $_match[0];
327 cvsjoko 1.1 $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 joko 1.3 $compiled_content = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s",
331     $this->_quote_replace($strip_tags_modified[$i]),
332     $compiled_content, 1);
333 cvsjoko 1.1 }
334    
335     // remove \n from the end of the file, if any
336 joko 1.3 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 cvsjoko 1.1 }
343    
344 joko 1.3 // remove unnecessary close/open tags
345     $compiled_content = preg_replace('!\?>\n?<\?php!', '', $compiled_content);
346    
347 cvsjoko 1.1 // 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 joko 1.3 if ($postfilter[3] || is_callable($postfilter[0])) {
352     $compiled_content = call_user_func_array($postfilter[0],
353     array($compiled_content, &$this));
354 cvsjoko 1.1 $this->_plugins['postfilter'][$filter_name][3] = true;
355     } else {
356 joko 1.3 $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
357 cvsjoko 1.1 }
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 joko 1.3 $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
364 cvsjoko 1.1
365     /* Emit code to load needed plugins. */
366 joko 1.3 $this->_plugins_code = '';
367 cvsjoko 1.1 if (count($this->_plugin_info)) {
368 joko 1.3 $_plugins_params = "array('plugins' => array(";
369 cvsjoko 1.1 foreach ($this->_plugin_info as $plugin_type => $plugins) {
370     foreach ($plugins as $plugin_name => $plugin_info) {
371 joko 1.3 $_plugins_params .= "array('$plugin_type', '$plugin_name', '$plugin_info[0]', $plugin_info[1], ";
372     $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
373 cvsjoko 1.1 }
374     }
375 joko 1.3 $_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 cvsjoko 1.1 $template_header .= $plugins_code;
378     $this->_plugin_info = array();
379 joko 1.3 $this->_plugins_code = $plugins_code;
380 cvsjoko 1.1 }
381    
382     if ($this->_init_smarty_vars) {
383 joko 1.3 $template_header .= "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
384 cvsjoko 1.1 $this->_init_smarty_vars = false;
385     }
386    
387 joko 1.3 $compiled_content = $template_header . $compiled_content;
388 cvsjoko 1.1 return true;
389     }
390    
391 joko 1.3 /**
392     * Compile a template tag
393     *
394     * @param string $template_tag
395     * @return string
396     */
397 cvsjoko 1.1 function _compile_tag($template_tag)
398     {
399     /* Matched comment. */
400     if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
401     return '';
402    
403 joko 1.3 /* 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 cvsjoko 1.1
411     $tag_command = $match[1];
412 joko 1.3 $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 cvsjoko 1.1
425 joko 1.3 /* 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 cvsjoko 1.1 }
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 joko 1.3 $this->_push_tag('if');
439 cvsjoko 1.1 return $this->_compile_if_tag($tag_args);
440    
441     case 'else':
442 joko 1.3 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 cvsjoko 1.1 return '<?php else: ?>';
448    
449     case 'elseif':
450 joko 1.3 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 cvsjoko 1.1 return $this->_compile_if_tag($tag_args, true);
456    
457     case '/if':
458 joko 1.3 $this->_pop_tag('if');
459 cvsjoko 1.1 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 joko 1.3 $this->_push_tag('section');
475 cvsjoko 1.1 return $this->_compile_section_start($tag_args);
476    
477     case 'sectionelse':
478 joko 1.3 $this->_push_tag('sectionelse');
479 cvsjoko 1.1 return "<?php endfor; else: ?>";
480 joko 1.3 break;
481 cvsjoko 1.1
482     case '/section':
483 joko 1.3 $_open_tag = $this->_pop_tag('section');
484     if ($_open_tag == 'sectionelse')
485 cvsjoko 1.1 return "<?php endif; ?>";
486     else
487     return "<?php endfor; endif; ?>";
488    
489     case 'foreach':
490 joko 1.3 $this->_push_tag('foreach');
491 cvsjoko 1.1 return $this->_compile_foreach_start($tag_args);
492     break;
493    
494     case 'foreachelse':
495 joko 1.3 $this->_push_tag('foreachelse');
496     return "<?php endforeach; unset(\$_from); else: ?>";
497 cvsjoko 1.1
498     case '/foreach':
499 joko 1.3 $_open_tag = $this->_pop_tag('foreach');
500     if ($_open_tag == 'foreachelse')
501 cvsjoko 1.1 return "<?php endif; ?>";
502     else
503 joko 1.3 return "<?php endforeach; unset(\$_from); endif; ?>";
504     break;
505 cvsjoko 1.1
506     case 'strip':
507     case '/strip':
508 joko 1.3 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 cvsjoko 1.1
523     case 'php':
524 joko 1.3 /* 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 cvsjoko 1.1
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 joko 1.3 } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
552 cvsjoko 1.1 return $output;
553 joko 1.3 } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
554     return $output;
555 cvsjoko 1.1 } else {
556 joko 1.3 $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
557 cvsjoko 1.1 }
558 joko 1.3
559 cvsjoko 1.1 }
560     }
561    
562    
563 joko 1.3 /**
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 cvsjoko 1.1 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 joko 1.3 if (!is_callable($plugin_func)) {
585 cvsjoko 1.1 $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 joko 1.3 if (!is_callable($plugin_func)) {
600 cvsjoko 1.1 $message = "plugin function $plugin_func() not found in $plugin_file\n";
601     $have_function = false;
602     } else {
603 joko 1.3 $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
604 cvsjoko 1.1 }
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 joko 1.3 $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 cvsjoko 1.1 } else {
622 joko 1.3 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
623 cvsjoko 1.1 }
624     return true;
625     } else {
626     return false;
627     }
628     }
629    
630    
631 joko 1.3 /**
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 cvsjoko 1.1 {
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 joko 1.3 if (!is_callable($plugin_func)) {
660 cvsjoko 1.1 $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 joko 1.3 $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
679    
680 cvsjoko 1.1 }
681     }
682    
683     if (!$found) {
684     return false;
685     } else if (!$have_function) {
686 joko 1.3 $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
687 cvsjoko 1.1 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 joko 1.3 if ($start_tag)
698     $this->_push_tag($tag_command);
699     else
700     $this->_pop_tag($tag_command);
701    
702 cvsjoko 1.1 if ($start_tag) {
703 joko 1.3 $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
704 cvsjoko 1.1 $attrs = $this->_parse_attrs($tag_args);
705 joko 1.3 $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 cvsjoko 1.1 }
715 joko 1.3 $output .= 'echo '.$_out_tag_text.'; } ';
716     $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
717 cvsjoko 1.1 }
718    
719     return true;
720     }
721    
722    
723 joko 1.3 /**
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 cvsjoko 1.1 {
733 joko 1.3 $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 cvsjoko 1.1 $this->_add_plugin('function', $tag_command);
777    
778 joko 1.3 $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
779 cvsjoko 1.1 $attrs = $this->_parse_attrs($tag_args);
780 joko 1.3 $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 cvsjoko 1.1 }
791    
792 joko 1.3 return true;
793 cvsjoko 1.1 }
794    
795 joko 1.3 /**
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 cvsjoko 1.1
876 joko 1.3 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 cvsjoko 1.1 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 joko 1.3 $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
902 cvsjoko 1.1 }
903    
904     if (!empty($attrs['script'])) {
905     $delayed_loading = true;
906 joko 1.3 } else {
907     $delayed_loading = false;
908 cvsjoko 1.1 }
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 joko 1.3 $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
919 cvsjoko 1.1
920 joko 1.3 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 cvsjoko 1.1 }
922    
923 joko 1.3 /**
924     * Compile {include ...} tag
925     *
926     * @param string $tag_args
927     * @return string
928     */
929 cvsjoko 1.1 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 joko 1.3 $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
936 cvsjoko 1.1 }
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 joko 1.3 $output .= "ob_start();\n";
955 cvsjoko 1.1 }
956    
957 joko 1.3 $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 cvsjoko 1.1
966     if (isset($assign_var)) {
967 joko 1.3 $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
968 cvsjoko 1.1 }
969    
970     $output .= ' ?>';
971    
972 joko 1.3 return $output;
973 cvsjoko 1.1
974     }
975    
976 joko 1.3 /**
977     * Compile {include ...} tag
978     *
979     * @param string $tag_args
980     * @return string
981     */
982 cvsjoko 1.1 function _compile_include_php_tag($tag_args)
983     {
984     $attrs = $this->_parse_attrs($tag_args);
985    
986     if (empty($attrs['file'])) {
987 joko 1.3 $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
988 cvsjoko 1.1 }
989    
990 joko 1.3 $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
991     $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
992 cvsjoko 1.1
993 joko 1.3 $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 cvsjoko 1.1 }
1006    
1007 joko 1.3
1008     /**
1009     * Compile {section ...} tag
1010     *
1011     * @param string $tag_args
1012     * @return string
1013     */
1014 cvsjoko 1.1 function _compile_section_start($tag_args)
1015     {
1016     $attrs = $this->_parse_attrs($tag_args);
1017     $arg_list = array();
1018    
1019 joko 1.3 $output = '<?php ';
1020 cvsjoko 1.1 $section_name = $attrs['name'];
1021     if (empty($section_name)) {
1022 joko 1.3 $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
1023 cvsjoko 1.1 }
1024    
1025 joko 1.3 $output .= "unset(\$this->_sections[$section_name]);\n";
1026 cvsjoko 1.1 $section_props = "\$this->_sections[$section_name]";
1027    
1028     foreach ($attrs as $attr_name => $attr_value) {
1029     switch ($attr_name) {
1030     case 'loop':
1031 joko 1.3 $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
1032 cvsjoko 1.1 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 joko 1.3 $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
1057 cvsjoko 1.1 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 joko 1.3
1113     /**
1114     * Compile {foreach ...} tag.
1115     *
1116     * @param string $tag_args
1117     * @return string
1118     */
1119 cvsjoko 1.1 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 joko 1.3 $this->_syntax_error("missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
1126 cvsjoko 1.1 }
1127    
1128     if (empty($attrs['item'])) {
1129 joko 1.3 $this->_syntax_error("missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
1130 cvsjoko 1.1 }
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 joko 1.3 $output .= "{$foreach_props}['total'] = count(\$_from = (array)$from);\n";
1160 cvsjoko 1.1 $output .= "{$foreach_props}['show'] = {$foreach_props}['total'] > 0;\n";
1161     $output .= "if ({$foreach_props}['show']):\n";
1162     $output .= "{$foreach_props}['iteration'] = 0;\n";
1163 joko 1.3 $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1164 cvsjoko 1.1 $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 joko 1.3 $output .= "if (count(\$_from = (array)$from)):\n";
1169     $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
1170 cvsjoko 1.1 }
1171     $output .= '?>';
1172    
1173     return $output;
1174     }
1175    
1176    
1177 joko 1.3 /**
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 cvsjoko 1.1 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 joko 1.3 if (isset($attrs['assign']))
1196     $assign = $attrs['assign'];
1197     else
1198     $assign = null;
1199 cvsjoko 1.1 $output = "<?php ob_start(); ?>";
1200 joko 1.3 $this->_capture_stack[] = array($buffer, $assign);
1201 cvsjoko 1.1 } else {
1202 joko 1.3 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 cvsjoko 1.1 }
1209    
1210     return $output;
1211     }
1212    
1213 joko 1.3 /**
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 cvsjoko 1.1 function _compile_if_tag($tag_args, $elseif = false)
1221     {
1222 joko 1.3
1223 cvsjoko 1.1 /* Tokenize args for 'if' tag. */
1224 joko 1.3 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 cvsjoko 1.1 $tokens = $match[0];
1233    
1234 joko 1.3 // 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 cvsjoko 1.1
1240     $is_arg_stack = array();
1241    
1242 joko 1.3 for ($i = 0; $i < count($tokens); $i++) {
1243 cvsjoko 1.1
1244     $token = &$tokens[$i];
1245 joko 1.3
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 cvsjoko 1.1 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 joko 1.3 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 cvsjoko 1.1 }
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 joko 1.3 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 cvsjoko 1.1 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 joko 1.3 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1429 cvsjoko 1.1 $expr_end++;
1430     $expr_arg = $tokens[$expr_end++];
1431 joko 1.3 $expr = "!(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1432 cvsjoko 1.1 } else
1433 joko 1.3 $expr = "!(1 & $is_arg)";
1434 cvsjoko 1.1 break;
1435    
1436     case 'odd':
1437 joko 1.3 if (isset($tokens[$expr_end]) && $tokens[$expr_end] == 'by') {
1438 cvsjoko 1.1 $expr_end++;
1439     $expr_arg = $tokens[$expr_end++];
1440 joko 1.3 $expr = "(1 & ($is_arg / " . $this->_parse_var_props($expr_arg) . "))";
1441 cvsjoko 1.1 } else
1442 joko 1.3 $expr = "(1 & $is_arg)";
1443 cvsjoko 1.1 break;
1444    
1445     case 'div':
1446     if (@$tokens[$expr_end] == 'by') {
1447     $expr_end++;
1448     $expr_arg = $tokens[$expr_end++];
1449 joko 1.3 $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
1450 cvsjoko 1.1 } else {
1451 joko 1.3 $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
1452 cvsjoko 1.1 }
1453     break;
1454    
1455     default:
1456 joko 1.3 $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
1457 cvsjoko 1.1 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 joko 1.3 /**
1471     * Parse attribute string
1472     *
1473     * @param string $tag_args
1474     * @return array
1475     */
1476     function _parse_attrs($tag_args)
1477 cvsjoko 1.1 {
1478 joko 1.3
1479 cvsjoko 1.1 /* Tokenize tag attributes. */
1480 joko 1.3 preg_match_all('/(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
1481 cvsjoko 1.1 )+ |
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 joko 1.3 $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
1503 cvsjoko 1.1 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 joko 1.3 $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
1511 cvsjoko 1.1 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 joko 1.3 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 cvsjoko 1.1
1532     $attrs[$attr_name] = $token;
1533     $state = 0;
1534     } else
1535 joko 1.3 $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
1536 cvsjoko 1.1 break;
1537     }
1538 joko 1.3 $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 cvsjoko 1.1 }
1548    
1549     $this->_parse_vars_props($attrs);
1550    
1551     return $attrs;
1552     }
1553    
1554 joko 1.3 /**
1555     * compile multiple variables and section properties tokens into
1556     * PHP code
1557     *
1558     * @param array $tokens
1559     */
1560 cvsjoko 1.1 function _parse_vars_props(&$tokens)
1561     {
1562 joko 1.3 foreach($tokens as $key => $val) {
1563     $tokens[$key] = $this->_parse_var_props($val);
1564     }
1565     }
1566 cvsjoko 1.1
1567 joko 1.3 /**
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 cvsjoko 1.1 }
1606 joko 1.3 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 cvsjoko 1.1 }
1614 joko 1.3 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 cvsjoko 1.1 }
1645 joko 1.3 $_return = preg_replace('%\.""|(?<!\\\\)""\.%', '', $var_expr);
1646     } else {
1647     $_return = $var_expr;
1648 cvsjoko 1.1 }
1649 joko 1.3 // replace double quoted literal string with single quotes
1650     $_return = preg_replace('!^"([\s\w]+)"$!',"'\\1'",$_return);
1651     return $_return;
1652 cvsjoko 1.1 }
1653    
1654 joko 1.3 /**
1655     * parse variable expression into PHP code
1656     *
1657     * @param string $var_expr
1658     * @param string $output
1659     * @return string
1660     */
1661 cvsjoko 1.1 function _parse_var($var_expr)
1662     {
1663 joko 1.3 $_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 cvsjoko 1.1
1708 joko 1.3 // get the modifiers working (only the last var from math + modifier is left)
1709     $var_expr = $_complete_var;
1710 cvsjoko 1.1 }
1711     }
1712    
1713 joko 1.3 // 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 cvsjoko 1.1 } else {
1736 joko 1.3 $_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 cvsjoko 1.1 } else {
1748 joko 1.3 $_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 cvsjoko 1.1 }
1794     }
1795    
1796 joko 1.3 return $_output;
1797 cvsjoko 1.1 }
1798    
1799 joko 1.3 /**
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 cvsjoko 1.1 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 joko 1.3 /**
1837     * parse section property expression into PHP code
1838     *
1839     * @param string $section_prop_expr
1840     * @return string
1841     */
1842 cvsjoko 1.1 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 joko 1.3 /**
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 cvsjoko 1.1 function _parse_modifiers(&$output, $modifier_string)
1868     {
1869 joko 1.3 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 cvsjoko 1.1 } else {
1887 joko 1.3 $_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 cvsjoko 1.1 }
1899 joko 1.3 $this->_add_plugin('modifier', $_modifier_name);
1900 cvsjoko 1.1
1901 joko 1.3 $this->_parse_vars_props($_modifier_args);
1902 cvsjoko 1.1
1903 joko 1.3 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 cvsjoko 1.1 else
1915 joko 1.3 $_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 cvsjoko 1.1
1920 joko 1.3 } else {
1921    
1922     $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
1923    
1924     }
1925 cvsjoko 1.1 }
1926     }
1927    
1928    
1929 joko 1.3 /**
1930     * add plugin
1931     *
1932     * @param string $type
1933     * @param string $name
1934     * @param boolean? $delayed_loading
1935     */
1936 cvsjoko 1.1 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 joko 1.3
1949     /**
1950     * Compiles references of type $smarty.foo
1951     *
1952     * @param string $indexes
1953     * @return string
1954     */
1955 cvsjoko 1.1 function _compile_smarty_ref(&$indexes)
1956     {
1957     /* Extract the reference name. */
1958 joko 1.3 $_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 cvsjoko 1.1
1965 joko 1.3 switch ($_ref) {
1966 cvsjoko 1.1 case 'now':
1967     $compiled_ref = 'time()';
1968 joko 1.3 $_max_index = 1;
1969 cvsjoko 1.1 break;
1970    
1971     case 'foreach':
1972     case 'section':
1973     array_shift($indexes);
1974 joko 1.3 $_var = $this->_parse_var_props(substr($indexes[0], 1));
1975     if ($_ref == 'foreach')
1976     $compiled_ref = "\$this->_foreach[$_var]";
1977 cvsjoko 1.1 else
1978 joko 1.3 $compiled_ref = "\$this->_sections[$_var]";
1979 cvsjoko 1.1 break;
1980    
1981     case 'get':
1982 joko 1.3 $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
1983 cvsjoko 1.1 break;
1984    
1985     case 'post':
1986 joko 1.3 $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
1987 cvsjoko 1.1 break;
1988    
1989     case 'cookies':
1990 joko 1.3 $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
1991 cvsjoko 1.1 break;
1992    
1993     case 'env':
1994 joko 1.3 $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
1995 cvsjoko 1.1 break;
1996    
1997     case 'server':
1998 joko 1.3 $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
1999 cvsjoko 1.1 break;
2000    
2001     case 'session':
2002 joko 1.3 $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
2003 cvsjoko 1.1 break;
2004    
2005     /*
2006     * These cases are handled either at run-time or elsewhere in the
2007     * compiler.
2008     */
2009     case 'request':
2010 joko 1.3 if ($this->request_use_auto_globals) {
2011     $compiled_ref = '$_REQUEST';
2012     break;
2013     } else {
2014     $this->_init_smarty_vars = true;
2015     }
2016 cvsjoko 1.1 return null;
2017    
2018     case 'capture':
2019     return null;
2020    
2021     case 'template':
2022     $compiled_ref = "'$this->_current_file'";
2023 joko 1.3 $_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 cvsjoko 1.1 break;
2042    
2043     default:
2044 joko 1.3 $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
2045 cvsjoko 1.1 break;
2046     }
2047    
2048 joko 1.3 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 cvsjoko 1.1 array_shift($indexes);
2053     return $compiled_ref;
2054     }
2055    
2056 joko 1.3 /**
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 cvsjoko 1.1
2075 joko 1.3 } 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 cvsjoko 1.1 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 joko 1.3 $_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 cvsjoko 1.1 }
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 joko 1.3 $_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 cvsjoko 1.1 }
2110     }
2111     }
2112     }
2113    
2114    
2115 joko 1.3 /**
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 cvsjoko 1.1 }
2213 joko 1.3
2214 cvsjoko 1.1 }
2215 joko 1.3
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 cvsjoko 1.1
2236     /* vim: set et: */
2237    
2238     ?>

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