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