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