218428eedb01ebf5908aef10bc59066c8faa593b
[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 /**
22 * Smarty object
23 *
24 * @var Smarty
25 */
26 public $smarty = null;
27
28 /**
29 * hash for nocache sections
30 *
31 * @var mixed
32 */
33 public $nocache_hash = null;
34
35 /**
36 * suppress generation of nocache code
37 *
38 * @var bool
39 */
40 public $suppressNocacheProcessing = false;
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 /**
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
79 *
80 * @var array
81 */
82 public $templateProperties = array();
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 = '';
132
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 /**
197 * called sub functions from template function
198 *
199 * @var array
200 */
201 public $called_functions = array();
202
203 /**
204 * compiled template function code
205 *
206 * @var string
207 */
208 public $templateFunctionCode = '';
209
210 /**
211 * php_handling setting either from Smarty or security
212 *
213 * @var int
214 */
215 public $php_handling = 0;
216
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
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
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 {
315 $this->nocache_hash = str_replace(array('.', ','), '_', uniqid(rand(), true));
316 }
317
318 /**
319 * Method to compile a Smarty template
320 *
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
324 *
325 * @return bool true if compiling succeeded, false if it failed
326 */
327 public function compileTemplate(Smarty_Internal_Template $template, $nocache = null, $parent_compiler = null)
328 {
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 }
336 $this->parent_compiler = $parent_compiler ? $parent_compiler : $this;
337 $nocache = isset($nocache) ? $nocache : false;
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;
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);
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);
371 $loop ++;
372 if ($no_sources) {
373 $this->inheritance_child = true;
374 } else {
375 $this->inheritance_child = false;
376 }
377 do {
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();
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
396 $_compiled_code = $this->doCompile($_content, true);
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
408 unset($this->parser->root_buffer, $this->parser->current_buffer, $this->parser, $this->lex);
409 self::$_tag_objects = array();
410 // return compiled code to template object
411 $merged_code = '';
412 if (!empty($this->mergedSubTemplatesCode)) {
413 foreach ($this->mergedSubTemplatesCode as $code) {
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) {
422 $_compiled_code .= $merged_code;
423 } else {
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 }
433 }
434 // unset content because template inheritance could have replace source with parent code
435 unset ($template->source->content);
436 $this->parent_compiler = null;
437 $this->template = null;
438 return $_compiled_code;
439 }
440
441 /**
442 * Compile Tag
443 * This is a call back from the lexer/parser
444 *
445 * Save current prefix code
446 * Compile tag
447 * Merge tag prefix code with saved one
448 * (required nested tags in attributes)
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 {
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 = '';
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
490 if (in_array("'nocache'", $args) || in_array(array('nocache' => 'true'), $args) || in_array(array('nocache' => '"true"'), $args) || in_array(array('nocache' => "'true'"), $args)
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) {
496 if (isset($this->parent_compiler->templateProperties['tpl_function'][$tag])) {
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'];
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]))
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
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 {
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
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 {
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 }
760 // compile this tag
761 return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3);
762 }
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;
839 $result = call_user_func_array($this->smarty->default_plugin_handler_func, array($tag, $plugin_type, $this->template, &$callback, &$script, &$cacheable));
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 }
851 require_once $script;
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
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
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
906 if ((!($this->template->source->recompiled) || $this->forceNocache) && $this->template->caching && !$this->suppressNocacheProcessing && ($this->nocache || $this->tag_nocache)
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
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
943 /**
944 * push current file and line offset on stack for tracing {block} source lines
945 *
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
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
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 {
1007 $expect = array();
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 }