|
1 <?php |
|
2 |
|
3 /** |
|
4 * Smarty Internal Plugin Smarty Template Compiler Base |
|
5 * This file contains the basic classes and methods for compiling Smarty templates with lexer/parser |
|
6 * |
|
7 * @package Smarty |
|
8 * @subpackage Compiler |
|
9 * @author Uwe Tews |
|
10 */ |
|
11 |
|
12 /** |
|
13 * Main abstract compiler class |
|
14 * |
|
15 * @package Smarty |
|
16 * @subpackage Compiler |
|
17 */ |
|
18 abstract class Smarty_Internal_TemplateCompilerBase { |
|
19 |
|
20 /** |
|
21 * Smarty object |
|
22 * |
|
23 * @var Smarty |
|
24 */ |
|
25 public $smarty = null; |
|
26 |
|
27 /** |
|
28 * hash for nocache sections |
|
29 * |
|
30 * @var mixed |
|
31 */ |
|
32 public $nocache_hash = null; |
|
33 |
|
34 /** |
|
35 * suppress generation of nocache code |
|
36 * |
|
37 * @var bool |
|
38 */ |
|
39 public $suppressNocacheProcessing = false; |
|
40 |
|
41 /** |
|
42 * compile tag objects |
|
43 * |
|
44 * @var array |
|
45 */ |
|
46 public static $_tag_objects = array(); |
|
47 |
|
48 /** |
|
49 * tag stack |
|
50 * |
|
51 * @var array |
|
52 */ |
|
53 public $_tag_stack = array(); |
|
54 |
|
55 /** |
|
56 * current template |
|
57 * |
|
58 * @var Smarty_Internal_Template |
|
59 */ |
|
60 public $template = null; |
|
61 |
|
62 /** |
|
63 * merged included sub template data |
|
64 * |
|
65 * @var array |
|
66 */ |
|
67 public $mergedSubTemplatesData = array(); |
|
68 |
|
69 /** |
|
70 * merged sub template code |
|
71 * |
|
72 * @var array |
|
73 */ |
|
74 public $mergedSubTemplatesCode = array(); |
|
75 |
|
76 /** |
|
77 * collected template properties during compilation |
|
78 * |
|
79 * @var array |
|
80 */ |
|
81 public $templateProperties = array(); |
|
82 |
|
83 /** |
|
84 * sources which must be compiled |
|
85 * |
|
86 * @var array |
|
87 */ |
|
88 public $sources = array(); |
|
89 |
|
90 /** |
|
91 * flag that we are inside {block} |
|
92 * |
|
93 * @var bool |
|
94 */ |
|
95 public $inheritance = false; |
|
96 |
|
97 /** |
|
98 * flag when compiling inheritance child template |
|
99 * |
|
100 * @var bool |
|
101 */ |
|
102 public $inheritance_child = false; |
|
103 |
|
104 /** |
|
105 * uid of templates called by {extends} for recursion check |
|
106 * |
|
107 * @var array |
|
108 */ |
|
109 public $extends_uid = array(); |
|
110 |
|
111 /** |
|
112 * source line offset for error messages |
|
113 * |
|
114 * @var int |
|
115 */ |
|
116 public $trace_line_offset = 0; |
|
117 |
|
118 /** |
|
119 * trace uid |
|
120 * |
|
121 * @var string |
|
122 */ |
|
123 public $trace_uid = ''; |
|
124 |
|
125 /** |
|
126 * trace file path |
|
127 * |
|
128 * @var string |
|
129 */ |
|
130 public $trace_filepath = ''; |
|
131 |
|
132 /** |
|
133 * stack for tracing file and line of nested {block} tags |
|
134 * |
|
135 * @var array |
|
136 */ |
|
137 public $trace_stack = array(); |
|
138 |
|
139 /** |
|
140 * plugins loaded by default plugin handler |
|
141 * |
|
142 * @var array |
|
143 */ |
|
144 public $default_handler_plugins = array(); |
|
145 |
|
146 /** |
|
147 * saved preprocessed modifier list |
|
148 * |
|
149 * @var mixed |
|
150 */ |
|
151 public $default_modifier_list = null; |
|
152 |
|
153 /** |
|
154 * force compilation of complete template as nocache |
|
155 * |
|
156 * @var boolean |
|
157 */ |
|
158 public $forceNocache = false; |
|
159 |
|
160 /** |
|
161 * suppress Smarty header code in compiled template |
|
162 * |
|
163 * @var bool |
|
164 */ |
|
165 public $suppressHeader = false; |
|
166 |
|
167 /** |
|
168 * suppress template property header code in compiled template |
|
169 * |
|
170 * @var bool |
|
171 */ |
|
172 public $suppressTemplatePropertyHeader = false; |
|
173 |
|
174 /** |
|
175 * suppress pre and post filter |
|
176 * |
|
177 * @var bool |
|
178 */ |
|
179 public $suppressFilter = false; |
|
180 |
|
181 /** |
|
182 * flag if compiled template file shall we written |
|
183 * |
|
184 * @var bool |
|
185 */ |
|
186 public $write_compiled_code = true; |
|
187 |
|
188 /** |
|
189 * flag if currently a template function is compiled |
|
190 * |
|
191 * @var bool |
|
192 */ |
|
193 public $compiles_template_function = false; |
|
194 |
|
195 /** |
|
196 * called sub functions from template function |
|
197 * |
|
198 * @var array |
|
199 */ |
|
200 public $called_functions = array(); |
|
201 |
|
202 /** |
|
203 * compiled template function code |
|
204 * |
|
205 * @var string |
|
206 */ |
|
207 public $templateFunctionCode = ''; |
|
208 |
|
209 /** |
|
210 * php_handling setting either from Smarty or security |
|
211 * |
|
212 * @var int |
|
213 */ |
|
214 public $php_handling = 0; |
|
215 |
|
216 /** |
|
217 * flags for used modifier plugins |
|
218 * |
|
219 * @var array |
|
220 */ |
|
221 public $modifier_plugins = array(); |
|
222 |
|
223 /** |
|
224 * type of already compiled modifier |
|
225 * |
|
226 * @var array |
|
227 */ |
|
228 public $known_modifier_type = array(); |
|
229 |
|
230 /** |
|
231 * parent compiler object for merged subtemplates and template functions |
|
232 * |
|
233 * @var Smarty_Internal_TemplateCompilerBase |
|
234 */ |
|
235 public $parent_compiler = null; |
|
236 |
|
237 /** |
|
238 * Flag true when compiling nocache section |
|
239 * |
|
240 * @var bool |
|
241 */ |
|
242 public $nocache = false; |
|
243 |
|
244 /** |
|
245 * Flag true when tag is compiled as nocache |
|
246 * |
|
247 * @var bool |
|
248 */ |
|
249 public $tag_nocache = false; |
|
250 |
|
251 /** |
|
252 * Flag to restart parsing |
|
253 * |
|
254 * @var bool |
|
255 */ |
|
256 public $abort_and_recompile = false; |
|
257 |
|
258 /** |
|
259 * Compiled tag prefix code |
|
260 * |
|
261 * @var array |
|
262 */ |
|
263 public $prefix_code = array(); |
|
264 |
|
265 /** |
|
266 * Prefix code stack |
|
267 * |
|
268 * @var array |
|
269 */ |
|
270 public $prefixCodeStack = array(); |
|
271 |
|
272 /** |
|
273 * Tag has compiled code |
|
274 * |
|
275 * @var bool |
|
276 */ |
|
277 public $has_code = false; |
|
278 |
|
279 /** |
|
280 * A variable string was compiled |
|
281 * |
|
282 * @var bool |
|
283 */ |
|
284 public $has_variable_string = false; |
|
285 |
|
286 /** |
|
287 * Tag creates output |
|
288 * |
|
289 * @var bool |
|
290 */ |
|
291 public $has_output = false; |
|
292 |
|
293 /** |
|
294 * Strip preg pattern |
|
295 * |
|
296 * @var string |
|
297 */ |
|
298 public $stripRegEx = '![\t ]*[\r\n]+[\t ]*!'; |
|
299 |
|
300 /** |
|
301 * method to compile a Smarty template |
|
302 * |
|
303 * @param mixed $_content template source |
|
304 * |
|
305 * @return bool true if compiling succeeded, false if it failed |
|
306 */ |
|
307 abstract protected function doCompile($_content); |
|
308 |
|
309 /** |
|
310 * Initialize compiler |
|
311 */ |
|
312 public function __construct() { |
|
313 $this->nocache_hash = str_replace(array('.', ','), '_', uniqid(rand(), true)); |
|
314 } |
|
315 |
|
316 /** |
|
317 * Method to compile a Smarty template |
|
318 * |
|
319 * @param Smarty_Internal_Template $template template object to compile |
|
320 * @param bool $nocache true is shall be compiled in nocache mode |
|
321 * @param null|Smarty_Internal_TemplateCompilerBase $parent_compiler |
|
322 * |
|
323 * @return bool true if compiling succeeded, false if it failed |
|
324 */ |
|
325 public function compileTemplate(Smarty_Internal_Template $template, $nocache = null, $parent_compiler = null) { |
|
326 // save template object in compiler class |
|
327 $this->template = $template; |
|
328 if (isset($this->template->smarty->security_policy)) { |
|
329 $this->php_handling = $this->template->smarty->security_policy->php_handling; |
|
330 } else { |
|
331 $this->php_handling = $this->template->smarty->php_handling; |
|
332 } |
|
333 $this->parent_compiler = $parent_compiler ? $parent_compiler : $this; |
|
334 $nocache = isset($nocache) ? $nocache : false; |
|
335 if (empty($template->properties['nocache_hash'])) { |
|
336 $template->properties['nocache_hash'] = $this->nocache_hash; |
|
337 } else { |
|
338 $this->nocache_hash = $template->properties['nocache_hash']; |
|
339 } |
|
340 $save_source = $this->template->source; |
|
341 // template header code |
|
342 $template_header = ''; |
|
343 if (!$this->suppressHeader) { |
|
344 $template_header .= "<?php /* Smarty version " . Smarty::SMARTY_VERSION . ", created on " . strftime("%Y-%m-%d %H:%M:%S") . "\n"; |
|
345 $template_header .= " compiled from \"" . $this->template->source->filepath . "\" */ ?>\n"; |
|
346 } |
|
347 |
|
348 if (empty($this->template->source->components)) { |
|
349 $this->sources = array($template->source); |
|
350 } else { |
|
351 // we have array of inheritance templates by extends: resource |
|
352 $this->sources = array_reverse($template->source->components); |
|
353 } |
|
354 $loop = 0; |
|
355 // the $this->sources array can get additional elements while compiling by the {extends} tag |
|
356 while ($this->template->source = array_shift($this->sources)) { |
|
357 $this->smarty->_current_file = $this->template->source->filepath; |
|
358 if ($this->smarty->debugging) { |
|
359 Smarty_Internal_Debug::start_compile($this->template); |
|
360 } |
|
361 $no_sources = count($this->sources); |
|
362 $this->parent_compiler->template->properties['file_dependency'][$this->template->source->uid] = array($this->template->source->filepath, $this->template->source->timestamp, $this->template->source->type); |
|
363 $loop++; |
|
364 if ($no_sources) { |
|
365 $this->inheritance_child = true; |
|
366 } else { |
|
367 $this->inheritance_child = false; |
|
368 } |
|
369 do { |
|
370 // flag for nochache sections |
|
371 $this->nocache = $nocache; |
|
372 $this->tag_nocache = false; |
|
373 // reset has nocache code flag |
|
374 $this->template->has_nocache_code = false; |
|
375 $this->has_variable_string = false; |
|
376 $this->prefix_code = array(); |
|
377 $_compiled_code = ''; |
|
378 // flag for aborting current and start recompile |
|
379 $this->abort_and_recompile = false; |
|
380 // get template source |
|
381 $_content = $this->template->source->content; |
|
382 if ($_content != '') { |
|
383 // run prefilter if required |
|
384 if ((isset($this->smarty->autoload_filters['pre']) || isset($this->smarty->registered_filters['pre'])) && !$this->suppressFilter) { |
|
385 $_content = Smarty_Internal_Filter_Handler::runFilter('pre', $_content, $template); |
|
386 } |
|
387 // call compiler |
|
388 $_compiled_code = $this->doCompile($_content, true); |
|
389 } |
|
390 } while ($this->abort_and_recompile); |
|
391 if ($this->smarty->debugging) { |
|
392 Smarty_Internal_Debug::end_compile($this->template); |
|
393 } |
|
394 } |
|
395 // restore source |
|
396 $this->template->source = $save_source; |
|
397 unset($save_source); |
|
398 $this->smarty->_current_file = $this->template->source->filepath; |
|
399 // free memory |
|
400 unset($this->parser->root_buffer, $this->parser->current_buffer, $this->parser, $this->lex); |
|
401 self::$_tag_objects = array(); |
|
402 // return compiled code to template object |
|
403 $merged_code = ''; |
|
404 if (!empty($this->mergedSubTemplatesCode)) { |
|
405 foreach ($this->mergedSubTemplatesCode as $code) { |
|
406 $merged_code .= $code; |
|
407 } |
|
408 } |
|
409 // run postfilter if required on compiled template code |
|
410 if ((isset($this->smarty->autoload_filters['post']) || isset($this->smarty->registered_filters['post'])) && !$this->suppressFilter && $_compiled_code != '') { |
|
411 $_compiled_code = Smarty_Internal_Filter_Handler::runFilter('post', $_compiled_code, $template); |
|
412 } |
|
413 if ($this->suppressTemplatePropertyHeader) { |
|
414 $_compiled_code .= $merged_code; |
|
415 } else { |
|
416 $_compiled_code = $template_header . Smarty_Internal_Extension_CodeFrame::create($template, $_compiled_code) . $merged_code; |
|
417 } |
|
418 if (!empty($this->templateFunctionCode)) { |
|
419 // run postfilter if required on compiled template code |
|
420 if ((isset($this->smarty->autoload_filters['post']) || isset($this->smarty->registered_filters['post'])) && !$this->suppressFilter) { |
|
421 $_compiled_code .= Smarty_Internal_Filter_Handler::runFilter('post', $this->templateFunctionCode, $template); |
|
422 } else { |
|
423 $_compiled_code .= $this->templateFunctionCode; |
|
424 } |
|
425 } |
|
426 // unset content because template inheritance could have replace source with parent code |
|
427 unset ($template->source->content); |
|
428 $this->parent_compiler = null; |
|
429 $this->template = null; |
|
430 return $_compiled_code; |
|
431 } |
|
432 |
|
433 /** |
|
434 * Compile Tag |
|
435 * This is a call back from the lexer/parser |
|
436 * |
|
437 * Save current prefix code |
|
438 * Compile tag |
|
439 * Merge tag prefix code with saved one |
|
440 * (required nested tags in attributes) |
|
441 * |
|
442 * @param string $tag tag name |
|
443 * @param array $args array with tag attributes |
|
444 * @param array $parameter array with compilation parameter |
|
445 * |
|
446 * @throws SmartyCompilerException |
|
447 * @throws SmartyException |
|
448 * @return string compiled code |
|
449 */ |
|
450 public function compileTag($tag, $args, $parameter = array()) { |
|
451 $this->prefixCodeStack[] = $this->prefix_code; |
|
452 $this->prefix_code = array(); |
|
453 $result = $this->compileTag2($tag, $args, $parameter); |
|
454 $this->prefix_code = array_merge($this->prefix_code, array_pop($this->prefixCodeStack)); |
|
455 return $result; |
|
456 } |
|
457 |
|
458 /** |
|
459 * Compile Tag |
|
460 * |
|
461 * @param string $tag tag name |
|
462 * @param array $args array with tag attributes |
|
463 * @param array $parameter array with compilation parameter |
|
464 * |
|
465 * @throws SmartyCompilerException |
|
466 * @throws SmartyException |
|
467 * @return string compiled code |
|
468 */ |
|
469 private function compileTag2($tag, $args, $parameter) { |
|
470 $plugin_type = ''; |
|
471 // $args contains the attributes parsed and compiled by the lexer/parser |
|
472 // assume that tag does compile into code, but creates no HTML output |
|
473 $this->has_code = true; |
|
474 $this->has_output = false; |
|
475 // log tag/attributes |
|
476 if (isset($this->smarty->get_used_tags) && $this->smarty->get_used_tags) { |
|
477 $this->template->used_tags[] = array($tag, $args); |
|
478 } |
|
479 // check nocache option flag |
|
480 if (in_array("'nocache'", $args) || in_array(array('nocache' => 'true'), $args) || in_array(array('nocache' => '"true"'), $args) || in_array(array('nocache' => "'true'"), $args) |
|
481 ) { |
|
482 $this->tag_nocache = true; |
|
483 } |
|
484 // compile the smarty tag (required compile classes to compile the tag are autoloaded) |
|
485 if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) { |
|
486 if (isset($this->parent_compiler->templateProperties['tpl_function'][$tag])) { |
|
487 // template defined by {template} tag |
|
488 $args['_attr']['name'] = "'" . $tag . "'"; |
|
489 $_output = $this->callTagCompiler('call', $args, $parameter); |
|
490 } |
|
491 } |
|
492 if ($_output !== false) { |
|
493 if ($_output !== true) { |
|
494 // did we get compiled code |
|
495 if ($this->has_code) { |
|
496 // Does it create output? |
|
497 if ($this->has_output) { |
|
498 $_output .= "\n"; |
|
499 } |
|
500 // return compiled code |
|
501 return $_output; |
|
502 } |
|
503 } |
|
504 // tag did not produce compiled code |
|
505 return null; |
|
506 } else { |
|
507 // map_named attributes |
|
508 if (isset($args['_attr'])) { |
|
509 foreach ($args['_attr'] as $key => $attribute) { |
|
510 if (is_array($attribute)) { |
|
511 $args = array_merge($args, $attribute); |
|
512 } |
|
513 } |
|
514 } |
|
515 // not an internal compiler tag |
|
516 if (strlen($tag) < 6 || substr($tag, -5) != 'close') { |
|
517 // check if tag is a registered object |
|
518 if (isset($this->smarty->registered_objects[$tag]) && isset($parameter['object_method'])) { |
|
519 $method = $parameter['object_method']; |
|
520 if (!in_array($method, $this->smarty->registered_objects[$tag][3]) && (empty($this->smarty->registered_objects[$tag][1]) || in_array($method, $this->smarty->registered_objects[$tag][1])) |
|
521 ) { |
|
522 return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method); |
|
523 } elseif (in_array($method, $this->smarty->registered_objects[$tag][3])) { |
|
524 return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag, $method); |
|
525 } else { |
|
526 // throw exception |
|
527 $this->trigger_template_error('not allowed method "' . $method . '" in registered object "' . $tag . '"', $this->lex->taglineno); |
|
528 } |
|
529 } |
|
530 // check if tag is registered |
|
531 foreach (array(Smarty::PLUGIN_COMPILER, Smarty::PLUGIN_FUNCTION, Smarty::PLUGIN_BLOCK) as $plugin_type) { |
|
532 if (isset($this->smarty->registered_plugins[$plugin_type][$tag])) { |
|
533 // if compiler function plugin call it now |
|
534 if ($plugin_type == Smarty::PLUGIN_COMPILER) { |
|
535 $new_args = array(); |
|
536 foreach ($args as $key => $mixed) { |
|
537 if (is_array($mixed)) { |
|
538 $new_args = array_merge($new_args, $mixed); |
|
539 } else { |
|
540 $new_args[$key] = $mixed; |
|
541 } |
|
542 } |
|
543 if (!$this->smarty->registered_plugins[$plugin_type][$tag][1]) { |
|
544 $this->tag_nocache = true; |
|
545 } |
|
546 $function = $this->smarty->registered_plugins[$plugin_type][$tag][0]; |
|
547 if (!is_array($function)) { |
|
548 return $function($new_args, $this); |
|
549 } elseif (is_object($function[0])) { |
|
550 return $this->smarty->registered_plugins[$plugin_type][$tag][0][0]->$function[1]($new_args, $this); |
|
551 } else { |
|
552 return call_user_func_array($function, array($new_args, $this)); |
|
553 } |
|
554 } |
|
555 // compile registered function or block function |
|
556 if ($plugin_type == Smarty::PLUGIN_FUNCTION || $plugin_type == Smarty::PLUGIN_BLOCK) { |
|
557 return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter, $tag); |
|
558 } |
|
559 } |
|
560 } |
|
561 // check plugins from plugins folder |
|
562 foreach ($this->smarty->plugin_search_order as $plugin_type) { |
|
563 if ($plugin_type == Smarty::PLUGIN_COMPILER && $this->smarty->loadPlugin('smarty_compiler_' . $tag) && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))) { |
|
564 $plugin = 'smarty_compiler_' . $tag; |
|
565 if (is_callable($plugin)) { |
|
566 // convert arguments format for old compiler plugins |
|
567 $new_args = array(); |
|
568 foreach ($args as $key => $mixed) { |
|
569 if (is_array($mixed)) { |
|
570 $new_args = array_merge($new_args, $mixed); |
|
571 } else { |
|
572 $new_args[$key] = $mixed; |
|
573 } |
|
574 } |
|
575 |
|
576 return $plugin($new_args, $this->smarty); |
|
577 } |
|
578 if (class_exists($plugin, false)) { |
|
579 $plugin_object = new $plugin; |
|
580 if (method_exists($plugin_object, 'compile')) { |
|
581 return $plugin_object->compile($args, $this); |
|
582 } |
|
583 } |
|
584 throw new SmartyException("Plugin \"{$tag}\" not callable"); |
|
585 } else { |
|
586 if ($function = $this->getPlugin($tag, $plugin_type)) { |
|
587 if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) { |
|
588 return $this->callTagCompiler('private_' . $plugin_type . '_plugin', $args, $parameter, $tag, $function); |
|
589 } |
|
590 } |
|
591 } |
|
592 } |
|
593 if (is_callable($this->smarty->default_plugin_handler_func)) { |
|
594 $found = false; |
|
595 // look for already resolved tags |
|
596 foreach ($this->smarty->plugin_search_order as $plugin_type) { |
|
597 if (isset($this->default_handler_plugins[$plugin_type][$tag])) { |
|
598 $found = true; |
|
599 break; |
|
600 } |
|
601 } |
|
602 if (!$found) { |
|
603 // call default handler |
|
604 foreach ($this->smarty->plugin_search_order as $plugin_type) { |
|
605 if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) { |
|
606 $found = true; |
|
607 break; |
|
608 } |
|
609 } |
|
610 } |
|
611 if ($found) { |
|
612 // if compiler function plugin call it now |
|
613 if ($plugin_type == Smarty::PLUGIN_COMPILER) { |
|
614 $new_args = array(); |
|
615 foreach ($args as $mixed) { |
|
616 $new_args = array_merge($new_args, $mixed); |
|
617 } |
|
618 $function = $this->default_handler_plugins[$plugin_type][$tag][0]; |
|
619 if (!is_array($function)) { |
|
620 return $function($new_args, $this); |
|
621 } elseif (is_object($function[0])) { |
|
622 return $this->default_handler_plugins[$plugin_type][$tag][0][0]->$function[1]($new_args, $this); |
|
623 } else { |
|
624 return call_user_func_array($function, array($new_args, $this)); |
|
625 } |
|
626 } else { |
|
627 return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter, $tag); |
|
628 } |
|
629 } |
|
630 } |
|
631 } else { |
|
632 // compile closing tag of block function |
|
633 $base_tag = substr($tag, 0, -5); |
|
634 // check if closing tag is a registered object |
|
635 if (isset($this->smarty->registered_objects[$base_tag]) && isset($parameter['object_method'])) { |
|
636 $method = $parameter['object_method']; |
|
637 if (in_array($method, $this->smarty->registered_objects[$base_tag][3])) { |
|
638 return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag, $method); |
|
639 } else { |
|
640 // throw exception |
|
641 $this->trigger_template_error('not allowed closing tag method "' . $method . '" in registered object "' . $base_tag . '"', $this->lex->taglineno); |
|
642 } |
|
643 } |
|
644 // registered block tag ? |
|
645 if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_BLOCK][$base_tag]) || isset($this->default_handler_plugins[Smarty::PLUGIN_BLOCK][$base_tag])) { |
|
646 return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag); |
|
647 } |
|
648 // block plugin? |
|
649 if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) { |
|
650 return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function); |
|
651 } |
|
652 // registered compiler plugin ? |
|
653 if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag])) { |
|
654 // if compiler function plugin call it now |
|
655 $args = array(); |
|
656 if (!$this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][1]) { |
|
657 $this->tag_nocache = true; |
|
658 } |
|
659 $function = $this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][0]; |
|
660 if (!is_array($function)) { |
|
661 return $function($args, $this); |
|
662 } elseif (is_object($function[0])) { |
|
663 return $this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][0][0]->$function[1]($args, $this); |
|
664 } else { |
|
665 return call_user_func_array($function, array($args, $this)); |
|
666 } |
|
667 } |
|
668 if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) { |
|
669 $plugin = 'smarty_compiler_' . $tag; |
|
670 if (is_callable($plugin)) { |
|
671 return $plugin($args, $this->smarty); |
|
672 } |
|
673 if (class_exists($plugin, false)) { |
|
674 $plugin_object = new $plugin; |
|
675 if (method_exists($plugin_object, 'compile')) { |
|
676 return $plugin_object->compile($args, $this); |
|
677 } |
|
678 } |
|
679 throw new SmartyException("Plugin \"{$tag}\" not callable"); |
|
680 } |
|
681 } |
|
682 $this->trigger_template_error("unknown tag \"" . $tag . "\"", $this->lex->taglineno); |
|
683 } |
|
684 } |
|
685 |
|
686 /** |
|
687 * compile variable |
|
688 * |
|
689 * @param string $variable |
|
690 * |
|
691 * @return string |
|
692 */ |
|
693 public function compileVariable($variable) { |
|
694 if (strpos($variable, '(') == 0) { |
|
695 // not a variable variable |
|
696 $var = trim($variable, '\''); |
|
697 $this->tag_nocache = $this->tag_nocache | $this->template->getVariable($var, null, true, false)->nocache; |
|
698 $this->template->properties['variables'][$var] = $this->tag_nocache | $this->nocache; |
|
699 } |
|
700 return '$_smarty_tpl->tpl_vars[' . $variable . ']->value'; |
|
701 } |
|
702 |
|
703 /** |
|
704 * This method is called from parser to process a text content section |
|
705 * - remove text from inheritance child templates as they may generate output |
|
706 * - strip text if strip is enabled |
|
707 * |
|
708 * @param string $text |
|
709 * |
|
710 * @return null|\Smarty_Internal_ParseTree_Text |
|
711 */ |
|
712 public function processText($text) { |
|
713 if ($this->parser->strip) { |
|
714 return new Smarty_Internal_ParseTree_Text($this->parser, preg_replace($this->stripRegEx, '', $text)); |
|
715 } else { |
|
716 return new Smarty_Internal_ParseTree_Text($this->parser, $text); |
|
717 } |
|
718 } |
|
719 |
|
720 /** |
|
721 * lazy loads internal compile plugin for tag and calls the compile method |
|
722 * compile objects cached for reuse. |
|
723 * class name format: Smarty_Internal_Compile_TagName |
|
724 * plugin filename format: Smarty_Internal_Tagname.php |
|
725 * |
|
726 * @param string $tag tag name |
|
727 * @param array $args list of tag attributes |
|
728 * @param mixed $param1 optional parameter |
|
729 * @param mixed $param2 optional parameter |
|
730 * @param mixed $param3 optional parameter |
|
731 * |
|
732 * @return string compiled code |
|
733 */ |
|
734 public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null) { |
|
735 // check if tag allowed by security |
|
736 if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) { |
|
737 // re-use object if already exists |
|
738 if (!isset(self::$_tag_objects[$tag])) { |
|
739 // lazy load internal compiler plugin |
|
740 $class_name = 'Smarty_Internal_Compile_' . $tag; |
|
741 if ($this->smarty->loadPlugin($class_name)) { |
|
742 self::$_tag_objects[$tag] = new $class_name; |
|
743 } else { |
|
744 return false; |
|
745 } |
|
746 } |
|
747 // compile this tag |
|
748 return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3); |
|
749 } |
|
750 // no internal compile plugin for this tag |
|
751 return false; |
|
752 } |
|
753 |
|
754 /** |
|
755 * Check for plugins and return function name |
|
756 * |
|
757 * @param $plugin_name |
|
758 * @param string $plugin_type type of plugin |
|
759 * |
|
760 * @return string call name of function |
|
761 */ |
|
762 public function getPlugin($plugin_name, $plugin_type) { |
|
763 $function = null; |
|
764 if ($this->template->caching && ($this->nocache || $this->tag_nocache)) { |
|
765 if (isset($this->template->required_plugins['nocache'][$plugin_name][$plugin_type])) { |
|
766 $function = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function']; |
|
767 } elseif (isset($this->template->required_plugins['compiled'][$plugin_name][$plugin_type])) { |
|
768 $this->template->required_plugins['nocache'][$plugin_name][$plugin_type] = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]; |
|
769 $function = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function']; |
|
770 } |
|
771 } else { |
|
772 if (isset($this->template->required_plugins['compiled'][$plugin_name][$plugin_type])) { |
|
773 $function = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function']; |
|
774 } elseif (isset($this->template->required_plugins['nocache'][$plugin_name][$plugin_type])) { |
|
775 $this->template->required_plugins['compiled'][$plugin_name][$plugin_type] = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]; |
|
776 $function = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function']; |
|
777 } |
|
778 } |
|
779 if (isset($function)) { |
|
780 if ($plugin_type == 'modifier') { |
|
781 $this->modifier_plugins[$plugin_name] = true; |
|
782 } |
|
783 |
|
784 return $function; |
|
785 } |
|
786 // loop through plugin dirs and find the plugin |
|
787 $function = 'smarty_' . $plugin_type . '_' . $plugin_name; |
|
788 $file = $this->smarty->loadPlugin($function, false); |
|
789 |
|
790 if (is_string($file)) { |
|
791 if ($this->template->caching && ($this->nocache || $this->tag_nocache)) { |
|
792 $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['file'] = $file; |
|
793 $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'] = $function; |
|
794 } else { |
|
795 $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['file'] = $file; |
|
796 $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'] = $function; |
|
797 } |
|
798 if ($plugin_type == 'modifier') { |
|
799 $this->modifier_plugins[$plugin_name] = true; |
|
800 } |
|
801 |
|
802 return $function; |
|
803 } |
|
804 if (is_callable($function)) { |
|
805 // plugin function is defined in the script |
|
806 return $function; |
|
807 } |
|
808 |
|
809 return false; |
|
810 } |
|
811 |
|
812 /** |
|
813 * Check for plugins by default plugin handler |
|
814 * |
|
815 * @param string $tag name of tag |
|
816 * @param string $plugin_type type of plugin |
|
817 * |
|
818 * @return boolean true if found |
|
819 */ |
|
820 public function getPluginFromDefaultHandler($tag, $plugin_type) { |
|
821 $callback = null; |
|
822 $script = null; |
|
823 $cacheable = true; |
|
824 $result = call_user_func_array($this->smarty->default_plugin_handler_func, array($tag, $plugin_type, $this->template, &$callback, &$script, &$cacheable)); |
|
825 if ($result) { |
|
826 $this->tag_nocache = $this->tag_nocache || !$cacheable; |
|
827 if ($script !== null) { |
|
828 if (is_file($script)) { |
|
829 if ($this->template->caching && ($this->nocache || $this->tag_nocache)) { |
|
830 $this->template->required_plugins['nocache'][$tag][$plugin_type]['file'] = $script; |
|
831 $this->template->required_plugins['nocache'][$tag][$plugin_type]['function'] = $callback; |
|
832 } else { |
|
833 $this->template->required_plugins['compiled'][$tag][$plugin_type]['file'] = $script; |
|
834 $this->template->required_plugins['compiled'][$tag][$plugin_type]['function'] = $callback; |
|
835 } |
|
836 require_once $script; |
|
837 } else { |
|
838 $this->trigger_template_error("Default plugin handler: Returned script file \"{$script}\" for \"{$tag}\" not found"); |
|
839 } |
|
840 } |
|
841 if (!is_string($callback) && !(is_array($callback) && is_string($callback[0]) && is_string($callback[1]))) { |
|
842 $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" must be a static function name or array of class and function name"); |
|
843 } |
|
844 if (is_callable($callback)) { |
|
845 $this->default_handler_plugins[$plugin_type][$tag] = array($callback, true, array()); |
|
846 |
|
847 return true; |
|
848 } else { |
|
849 $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" not callable"); |
|
850 } |
|
851 } |
|
852 |
|
853 return false; |
|
854 } |
|
855 |
|
856 /** |
|
857 * Append code segments and remove unneeded ?> <?php transitions |
|
858 * |
|
859 * @param string $left |
|
860 * @param string $right |
|
861 * |
|
862 * @return string |
|
863 */ |
|
864 public function appendCode($left, $right) { |
|
865 if (preg_match('/\s*\?>\s*$/', $left) && preg_match('/^\s*<\?php\s+/', $right)) { |
|
866 $left = preg_replace('/\s*\?>\s*$/', "\n", $left); |
|
867 $left .= preg_replace('/^\s*<\?php\s+/', '', $right); |
|
868 } else { |
|
869 $left .= $right; |
|
870 } |
|
871 return $left; |
|
872 } |
|
873 |
|
874 /** |
|
875 * Inject inline code for nocache template sections |
|
876 * This method gets the content of each template element from the parser. |
|
877 * If the content is compiled code and it should be not cached the code is injected |
|
878 * into the rendered output. |
|
879 * |
|
880 * @param string $content content of template element |
|
881 * @param boolean $is_code true if content is compiled code |
|
882 * |
|
883 * @return string content |
|
884 */ |
|
885 public function processNocacheCode($content, $is_code) { |
|
886 // If the template is not evaluated and we have a nocache section and or a nocache tag |
|
887 if ($is_code && !empty($content)) { |
|
888 // generate replacement code |
|
889 if ((!($this->template->source->recompiled) || $this->forceNocache) && $this->template->caching && !$this->suppressNocacheProcessing && ($this->nocache || $this->tag_nocache) |
|
890 ) { |
|
891 $this->template->has_nocache_code = true; |
|
892 $_output = addcslashes($content, '\'\\'); |
|
893 $_output = str_replace("^#^", "'", $_output); |
|
894 $_output = "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/" . $_output . "/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n"; |
|
895 // make sure we include modifier plugins for nocache code |
|
896 foreach ($this->modifier_plugins as $plugin_name => $dummy) { |
|
897 if (isset($this->template->required_plugins['compiled'][$plugin_name]['modifier'])) { |
|
898 $this->template->required_plugins['nocache'][$plugin_name]['modifier'] = $this->template->required_plugins['compiled'][$plugin_name]['modifier']; |
|
899 } |
|
900 } |
|
901 } else { |
|
902 $_output = $content; |
|
903 } |
|
904 } else { |
|
905 $_output = $content; |
|
906 } |
|
907 $this->modifier_plugins = array(); |
|
908 $this->suppressNocacheProcessing = false; |
|
909 $this->tag_nocache = false; |
|
910 |
|
911 return $_output; |
|
912 } |
|
913 |
|
914 /** |
|
915 * Generate nocache code string |
|
916 * |
|
917 * @param string $code PHP code |
|
918 * |
|
919 * @return string |
|
920 */ |
|
921 public function makeNocacheCode($code) { |
|
922 return "echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/<?php " . str_replace("^#^", "'", addcslashes($code, '\'\\')) . "?>/*/%%SmartyNocache:{$this->nocache_hash}%%*/';\n"; |
|
923 } |
|
924 |
|
925 /** |
|
926 * push current file and line offset on stack for tracing {block} source lines |
|
927 * |
|
928 * @param string $file new filename |
|
929 * @param string $uid uid of file |
|
930 * @param int $line line offset to source |
|
931 * @param bool $debug false debug end_compile shall not be called |
|
932 */ |
|
933 public function pushTrace($file, $uid, $line, $debug = true) { |
|
934 if ($this->smarty->debugging && $debug) { |
|
935 Smarty_Internal_Debug::end_compile($this->template); |
|
936 } |
|
937 array_push($this->trace_stack, array($this->smarty->_current_file, $this->trace_filepath, $this->trace_uid, $this->trace_line_offset)); |
|
938 $this->trace_filepath = $this->smarty->_current_file = $file; |
|
939 $this->trace_uid = $uid; |
|
940 $this->trace_line_offset = $line; |
|
941 if ($this->smarty->debugging) { |
|
942 Smarty_Internal_Debug::start_compile($this->template); |
|
943 } |
|
944 } |
|
945 |
|
946 /** |
|
947 * restore file and line offset |
|
948 */ |
|
949 public function popTrace() { |
|
950 if ($this->smarty->debugging) { |
|
951 Smarty_Internal_Debug::end_compile($this->template); |
|
952 } |
|
953 $r = array_pop($this->trace_stack); |
|
954 $this->smarty->_current_file = $r[0]; |
|
955 $this->trace_filepath = $r[1]; |
|
956 $this->trace_uid = $r[2]; |
|
957 $this->trace_line_offset = $r[3]; |
|
958 if ($this->smarty->debugging) { |
|
959 Smarty_Internal_Debug::start_compile($this->template); |
|
960 } |
|
961 } |
|
962 |
|
963 /** |
|
964 * display compiler error messages without dying |
|
965 * If parameter $args is empty it is a parser detected syntax error. |
|
966 * In this case the parser is called to obtain information about expected tokens. |
|
967 * If parameter $args contains a string this is used as error message |
|
968 * |
|
969 * @param string $args individual error message or null |
|
970 * @param string $line line-number |
|
971 * |
|
972 * @throws SmartyCompilerException when an unexpected token is found |
|
973 */ |
|
974 public function trigger_template_error($args = null, $line = null) { |
|
975 // get template source line which has error |
|
976 if (!isset($line)) { |
|
977 $line = $this->lex->line; |
|
978 } |
|
979 // $line += $this->trace_line_offset; |
|
980 $match = preg_split("/\n/", $this->lex->data); |
|
981 $error_text = 'Syntax error in template "' . (empty($this->trace_filepath) ? $this->template->source->filepath : $this->trace_filepath) . '" on line ' . ($line + $this->trace_line_offset) . ' "' . trim(preg_replace('![\t\r\n]+!', ' ', $match[$line - 1])) . '" '; |
|
982 if (isset($args)) { |
|
983 // individual error message |
|
984 $error_text .= $args; |
|
985 } else { |
|
986 $expect = array(); |
|
987 // expected token from parser |
|
988 $error_text .= ' - Unexpected "' . $this->lex->value . '"'; |
|
989 if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) { |
|
990 foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) { |
|
991 $exp_token = $this->parser->yyTokenName[$token]; |
|
992 if (isset($this->lex->smarty_token_names[$exp_token])) { |
|
993 // token type from lexer |
|
994 $expect[] = '"' . $this->lex->smarty_token_names[$exp_token] . '"'; |
|
995 } else { |
|
996 // otherwise internal token name |
|
997 $expect[] = $this->parser->yyTokenName[$token]; |
|
998 } |
|
999 } |
|
1000 $error_text .= ', expected one of: ' . implode(' , ', $expect); |
|
1001 } |
|
1002 } |
|
1003 $e = new SmartyCompilerException($error_text); |
|
1004 $e->line = $line; |
|
1005 $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[$line - 1])); |
|
1006 $e->desc = $args; |
|
1007 $e->template = $this->template->source->filepath; |
|
1008 throw $e; |
|
1009 } |
|
1010 } |