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