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