1: <?php
2: /**
3: * Smarty Internal Plugin Smarty Template Compiler Base
4: * This file contains the basic classes and methods for compiling Smarty templates with lexer/parser
5: *
6: * @package Smarty
7: * @subpackage Compiler
8: * @author Uwe Tews
9: */
10:
11: /**
12: * Main abstract compiler class
13: *
14: * @package Smarty
15: * @subpackage Compiler
16: *
17: * @property Smarty_Internal_SmartyTemplateCompiler $prefixCompiledCode = ''
18: * @property Smarty_Internal_SmartyTemplateCompiler $postfixCompiledCode = ''
19: * @method registerPostCompileCallback($callback, $parameter = array(), $key = null, $replace = false)
20: * @method unregisterPostCompileCallback($key)
21: */
22: abstract class Smarty_Internal_TemplateCompilerBase
23: {
24: /**
25: * compile tag objects cache
26: *
27: * @var array
28: */
29: public static $_tag_objects = array();
30:
31: /**
32: * counter for prefix variable number
33: *
34: * @var int
35: */
36: public static $prefixVariableNumber = 0;
37:
38: /**
39: * Smarty object
40: *
41: * @var Smarty
42: */
43: public $smarty = null;
44:
45: /**
46: * Parser object
47: *
48: * @var Smarty_Internal_Templateparser
49: */
50: public $parser = null;
51:
52: /**
53: * hash for nocache sections
54: *
55: * @var mixed
56: */
57: public $nocache_hash = null;
58:
59: /**
60: * suppress generation of nocache code
61: *
62: * @var bool
63: */
64: public $suppressNocacheProcessing = false;
65:
66: /**
67: * caching enabled (copied from template object)
68: *
69: * @var int
70: */
71: public $caching = 0;
72:
73: /**
74: * tag stack
75: *
76: * @var array
77: */
78: public $_tag_stack = array();
79:
80: /**
81: * tag stack count
82: *
83: * @var array
84: */
85: public $_tag_stack_count = array();
86:
87: /**
88: * Plugins used by template
89: *
90: * @var array
91: */
92: public $required_plugins = array('compiled' => array(), 'nocache' => array());
93:
94: /**
95: * Required plugins stack
96: *
97: * @var array
98: */
99: public $required_plugins_stack = array();
100:
101: /**
102: * current template
103: *
104: * @var Smarty_Internal_Template
105: */
106: public $template = null;
107:
108: /**
109: * merged included sub template data
110: *
111: * @var array
112: */
113: public $mergedSubTemplatesData = array();
114:
115: /**
116: * merged sub template code
117: *
118: * @var array
119: */
120: public $mergedSubTemplatesCode = array();
121:
122: /**
123: * collected template properties during compilation
124: *
125: * @var array
126: */
127: public $templateProperties = array();
128:
129: /**
130: * source line offset for error messages
131: *
132: * @var int
133: */
134: public $trace_line_offset = 0;
135:
136: /**
137: * trace uid
138: *
139: * @var string
140: */
141: public $trace_uid = '';
142:
143: /**
144: * trace file path
145: *
146: * @var string
147: */
148: public $trace_filepath = '';
149:
150: /**
151: * stack for tracing file and line of nested {block} tags
152: *
153: * @var array
154: */
155: public $trace_stack = array();
156:
157: /**
158: * plugins loaded by default plugin handler
159: *
160: * @var array
161: */
162: public $default_handler_plugins = array();
163:
164: /**
165: * saved preprocessed modifier list
166: *
167: * @var mixed
168: */
169: public $default_modifier_list = null;
170:
171: /**
172: * force compilation of complete template as nocache
173: *
174: * @var boolean
175: */
176: public $forceNocache = false;
177:
178: /**
179: * flag if compiled template file shall we written
180: *
181: * @var bool
182: */
183: public $write_compiled_code = true;
184:
185: /**
186: * Template functions
187: *
188: * @var array
189: */
190: public $tpl_function = array();
191:
192: /**
193: * called sub functions from template function
194: *
195: * @var array
196: */
197: public $called_functions = array();
198:
199: /**
200: * compiled template or block function code
201: *
202: * @var string
203: */
204: public $blockOrFunctionCode = '';
205:
206: /**
207: * php_handling setting either from Smarty or security
208: *
209: * @var int
210: */
211: public $php_handling = 0;
212:
213: /**
214: * flags for used modifier plugins
215: *
216: * @var array
217: */
218: public $modifier_plugins = array();
219:
220: /**
221: * type of already compiled modifier
222: *
223: * @var array
224: */
225: public $known_modifier_type = array();
226:
227: /**
228: * parent compiler object for merged subtemplates and template functions
229: *
230: * @var Smarty_Internal_TemplateCompilerBase
231: */
232: public $parent_compiler = null;
233:
234: /**
235: * Flag true when compiling nocache section
236: *
237: * @var bool
238: */
239: public $nocache = false;
240:
241: /**
242: * Flag true when tag is compiled as nocache
243: *
244: * @var bool
245: */
246: public $tag_nocache = false;
247:
248: /**
249: * Compiled tag prefix code
250: *
251: * @var array
252: */
253: public $prefix_code = array();
254:
255: /**
256: * used prefix variables by current compiled tag
257: *
258: * @var array
259: */
260: public $usedPrefixVariables = array();
261:
262: /**
263: * Prefix code stack
264: *
265: * @var array
266: */
267: public $prefixCodeStack = array();
268:
269: /**
270: * Tag has compiled code
271: *
272: * @var bool
273: */
274: public $has_code = false;
275:
276: /**
277: * A variable string was compiled
278: *
279: * @var bool
280: */
281: public $has_variable_string = false;
282:
283: /**
284: * Stack for {setfilter} {/setfilter}
285: *
286: * @var array
287: */
288: public $variable_filter_stack = array();
289:
290: /**
291: * variable filters for {setfilter} {/setfilter}
292: *
293: * @var array
294: */
295: public $variable_filters = array();
296:
297: /**
298: * Nesting count of looping tags like {foreach}, {for}, {section}, {while}
299: *
300: * @var int
301: */
302: public $loopNesting = 0;
303:
304: /**
305: * Strip preg pattern
306: *
307: * @var string
308: */
309: public $stripRegEx = '![\t ]*[\r\n]+[\t ]*!';
310:
311: /**
312: * plugin search order
313: *
314: * @var array
315: */
316: public $plugin_search_order = array(
317: 'function',
318: 'block',
319: 'compiler',
320: 'class'
321: );
322:
323: /**
324: * General storage area for tag compiler plugins
325: *
326: * @var array
327: */
328: public $_cache = array();
329:
330: /**
331: * Lexer preg pattern for left delimiter
332: *
333: * @var string
334: */
335: private $ldelPreg = '[{]';
336:
337: /**
338: * Lexer preg pattern for right delimiter
339: *
340: * @var string
341: */
342: private $rdelPreg = '[}]';
343:
344: /**
345: * Length of right delimiter
346: *
347: * @var int
348: */
349: private $rdelLength = 0;
350:
351: /**
352: * Length of left delimiter
353: *
354: * @var int
355: */
356: private $ldelLength = 0;
357:
358: /**
359: * Lexer preg pattern for user literals
360: *
361: * @var string
362: */
363: private $literalPreg = '';
364:
365: /**
366: * Initialize compiler
367: *
368: * @param Smarty $smarty global instance
369: */
370: public function __construct(Smarty $smarty)
371: {
372: $this->smarty = $smarty;
373: $this->nocache_hash = str_replace(
374: array(
375: '.',
376: ','
377: ),
378: '_',
379: uniqid(mt_rand(), true)
380: );
381: }
382:
383: /**
384: * Method to compile a Smarty template
385: *
386: * @param Smarty_Internal_Template $template template object to compile
387: * @param bool $nocache true is shall be compiled in nocache mode
388: * @param null|Smarty_Internal_TemplateCompilerBase $parent_compiler
389: *
390: * @return bool true if compiling succeeded, false if it failed
391: * @throws \Exception
392: */
393: public function compileTemplate(
394: Smarty_Internal_Template $template,
395: $nocache = null,
396: Smarty_Internal_TemplateCompilerBase $parent_compiler = null
397: ) {
398: // get code frame of compiled template
399: $_compiled_code = $template->smarty->ext->_codeFrame->create(
400: $template,
401: $this->compileTemplateSource(
402: $template,
403: $nocache,
404: $parent_compiler
405: ),
406: $this->postFilter($this->blockOrFunctionCode) .
407: join('', $this->mergedSubTemplatesCode),
408: false,
409: $this
410: );
411: return $_compiled_code;
412: }
413:
414: /**
415: * Compile template source and run optional post filter
416: *
417: * @param \Smarty_Internal_Template $template
418: * @param null|bool $nocache flag if template must be compiled in nocache mode
419: * @param \Smarty_Internal_TemplateCompilerBase $parent_compiler
420: *
421: * @return string
422: * @throws \Exception
423: */
424: public function compileTemplateSource(
425: Smarty_Internal_Template $template,
426: $nocache = null,
427: Smarty_Internal_TemplateCompilerBase $parent_compiler = null
428: ) {
429: try {
430: // save template object in compiler class
431: $this->template = $template;
432: if (property_exists($this->template->smarty, 'plugin_search_order')) {
433: $this->plugin_search_order = $this->template->smarty->plugin_search_order;
434: }
435: if ($this->smarty->debugging) {
436: if (!isset($this->smarty->_debug)) {
437: $this->smarty->_debug = new Smarty_Internal_Debug();
438: }
439: $this->smarty->_debug->start_compile($this->template);
440: }
441: if (isset($this->template->smarty->security_policy)) {
442: $this->php_handling = $this->template->smarty->security_policy->php_handling;
443: } else {
444: $this->php_handling = $this->template->smarty->php_handling;
445: }
446: $this->parent_compiler = $parent_compiler ? $parent_compiler : $this;
447: $nocache = isset($nocache) ? $nocache : false;
448: if (empty($template->compiled->nocache_hash)) {
449: $template->compiled->nocache_hash = $this->nocache_hash;
450: } else {
451: $this->nocache_hash = $template->compiled->nocache_hash;
452: }
453: $this->caching = $template->caching;
454: // flag for nocache sections
455: $this->nocache = $nocache;
456: $this->tag_nocache = false;
457: // reset has nocache code flag
458: $this->template->compiled->has_nocache_code = false;
459: $this->has_variable_string = false;
460: $this->prefix_code = array();
461: // add file dependency
462: if ($this->smarty->merge_compiled_includes || $this->template->source->handler->checkTimestamps()) {
463: $this->parent_compiler->template->compiled->file_dependency[ $this->template->source->uid ] =
464: array(
465: $this->template->source->filepath,
466: $this->template->source->getTimeStamp(),
467: $this->template->source->type,
468: );
469: }
470: $this->smarty->_current_file = $this->template->source->filepath;
471: // get template source
472: if (!empty($this->template->source->components)) {
473: // we have array of inheritance templates by extends: resource
474: // generate corresponding source code sequence
475: $_content =
476: Smarty_Internal_Compile_Extends::extendsSourceArrayCode($this->template);
477: } else {
478: // get template source
479: $_content = $this->template->source->getContent();
480: }
481: $_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true));
482: if (!empty($this->required_plugins[ 'compiled' ]) || !empty($this->required_plugins[ 'nocache' ])) {
483: $_compiled_code = '<?php ' . $this->compileRequiredPlugins() . "?>\n" . $_compiled_code;
484: }
485: } catch (Exception $e) {
486: if ($this->smarty->debugging) {
487: $this->smarty->_debug->end_compile($this->template);
488: }
489: $this->_tag_stack = array();
490: // free memory
491: $this->parent_compiler = null;
492: $this->template = null;
493: $this->parser = null;
494: throw $e;
495: }
496: if ($this->smarty->debugging) {
497: $this->smarty->_debug->end_compile($this->template);
498: }
499: $this->parent_compiler = null;
500: $this->parser = null;
501: return $_compiled_code;
502: }
503:
504: /**
505: * Optionally process compiled code by post filter
506: *
507: * @param string $code compiled code
508: *
509: * @return string
510: * @throws \SmartyException
511: */
512: public function postFilter($code)
513: {
514: // run post filter if on code
515: if (!empty($code)
516: && (isset($this->smarty->autoload_filters[ 'post' ]) || isset($this->smarty->registered_filters[ 'post' ]))
517: ) {
518: return $this->smarty->ext->_filterHandler->runFilter('post', $code, $this->template);
519: } else {
520: return $code;
521: }
522: }
523:
524: /**
525: * Run optional prefilter
526: *
527: * @param string $_content template source
528: *
529: * @return string
530: * @throws \SmartyException
531: */
532: public function preFilter($_content)
533: {
534: // run pre filter if required
535: if ($_content !== ''
536: && ((isset($this->smarty->autoload_filters[ 'pre' ]) || isset($this->smarty->registered_filters[ 'pre' ])))
537: ) {
538: return $this->smarty->ext->_filterHandler->runFilter('pre', $_content, $this->template);
539: } else {
540: return $_content;
541: }
542: }
543:
544: /**
545: * Compile Tag
546: * This is a call back from the lexer/parser
547: *
548: * Save current prefix code
549: * Compile tag
550: * Merge tag prefix code with saved one
551: * (required nested tags in attributes)
552: *
553: * @param string $tag tag name
554: * @param array $args array with tag attributes
555: * @param array $parameter array with compilation parameter
556: *
557: * @throws SmartyCompilerException
558: * @throws SmartyException
559: * @return string compiled code
560: */
561: public function compileTag($tag, $args, $parameter = array())
562: {
563: $this->prefixCodeStack[] = $this->prefix_code;
564: $this->prefix_code = array();
565: $result = $this->compileTag2($tag, $args, $parameter);
566: $this->prefix_code = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
567: return $result;
568: }
569:
570: /**
571: * compile variable
572: *
573: * @param string $variable
574: *
575: * @return string
576: */
577: public function compileVariable($variable)
578: {
579: if (!strpos($variable, '(')) {
580: // not a variable variable
581: $var = trim($variable, '\'');
582: $this->tag_nocache = $this->tag_nocache |
583: $this->template->ext->getTemplateVars->_getVariable(
584: $this->template,
585: $var,
586: null,
587: true,
588: false
589: )->nocache;
590: // todo $this->template->compiled->properties['variables'][$var] = $this->tag_nocache | $this->nocache;
591: }
592: return '$_smarty_tpl->tpl_vars[' . $variable . ']->value';
593: }
594:
595: /**
596: * compile config variable
597: *
598: * @param string $variable
599: *
600: * @return string
601: */
602: public function compileConfigVariable($variable)
603: {
604: // return '$_smarty_tpl->config_vars[' . $variable . ']';
605: return '$_smarty_tpl->smarty->ext->configLoad->_getConfigVariable($_smarty_tpl, ' . $variable . ')';
606: }
607:
608: /**
609: * compile PHP function call
610: *
611: * @param string $name
612: * @param array $parameter
613: *
614: * @return string
615: * @throws \SmartyCompilerException
616: */
617: public function compilePHPFunctionCall($name, $parameter)
618: {
619: if (!$this->smarty->security_policy || $this->smarty->security_policy->isTrustedPhpFunction($name, $this)) {
620: if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0
621: || strcasecmp($name, 'array') === 0 || is_callable($name)
622: ) {
623: $func_name = strtolower($name);
624:
625: if ($func_name === 'isset') {
626: if (count($parameter) === 0) {
627: $this->trigger_template_error('Illegal number of parameter in "isset()"');
628: }
629:
630: $pa = array();
631: foreach ($parameter as $p) {
632: $pa[] = $this->syntaxMatchesVariable($p) ? 'isset(' . $p . ')' : '(' . $p . ' !== null )';
633: }
634: return '(' . implode(' && ', $pa) . ')';
635:
636: } elseif (in_array(
637: $func_name,
638: array(
639: 'empty',
640: 'reset',
641: 'current',
642: 'end',
643: 'prev',
644: 'next'
645: )
646: )
647: ) {
648: if (count($parameter) !== 1) {
649: $this->trigger_template_error("Illegal number of parameter in '{$func_name()}'");
650: }
651: if ($func_name === 'empty') {
652: if (!$this->syntaxMatchesVariable($parameter[0]) && version_compare(PHP_VERSION, '5.5.0', '<')) {
653: return '(' . $parameter[ 0 ] . ' === false )';
654: } else {
655: return $func_name . '(' .
656: str_replace("')->value", "',null,true,false)->value", $parameter[ 0 ]) . ')';
657: }
658: } else {
659: return $func_name . '(' . $parameter[ 0 ] . ')';
660: }
661: } else {
662: return $name . '(' . implode(',', $parameter) . ')';
663: }
664: } else {
665: $this->trigger_template_error("unknown function '{$name}'");
666: }
667: }
668: }
669:
670: /**
671: * Determines whether the passed string represents a valid (PHP) variable.
672: * This is important, because `isset()` only works on variables and `empty()` can only be passed
673: * a variable prior to php5.5
674: * @param $string
675: * @return bool
676: */
677: private function syntaxMatchesVariable($string) {
678: static $regex_pattern = '/^\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*((->)[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*|\[.*]*\])*$/';
679: return 1 === preg_match($regex_pattern, trim($string));
680: }
681:
682: /**
683: * This method is called from parser to process a text content section if strip is enabled
684: * - remove text from inheritance child templates as they may generate output
685: *
686: * @param string $text
687: *
688: * @return string
689: */
690: public function processText($text)
691: {
692:
693: if (strpos($text, '<') === false) {
694: return preg_replace($this->stripRegEx, '', $text);
695: }
696:
697: $store = array();
698: $_store = 0;
699:
700: // capture html elements not to be messed with
701: $_offset = 0;
702: if (preg_match_all(
703: '#(<script[^>]*>.*?</script[^>]*>)|(<textarea[^>]*>.*?</textarea[^>]*>)|(<pre[^>]*>.*?</pre[^>]*>)#is',
704: $text,
705: $matches,
706: PREG_OFFSET_CAPTURE | PREG_SET_ORDER
707: )
708: ) {
709: foreach ($matches as $match) {
710: $store[] = $match[ 0 ][ 0 ];
711: $_length = strlen($match[ 0 ][ 0 ]);
712: $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@';
713: $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length);
714: $_offset += $_length - strlen($replace);
715: $_store++;
716: }
717: }
718: $expressions = array(// replace multiple spaces between tags by a single space
719: '#(:SMARTY@!@|>)[\040\011]+(?=@!@SMARTY:|<)#s' => '\1 \2',
720: // remove newline between tags
721: '#(:SMARTY@!@|>)[\040\011]*[\n]\s*(?=@!@SMARTY:|<)#s' => '\1\2',
722: // remove multiple spaces between attributes (but not in attribute values!)
723: '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5',
724: '#>[\040\011]+$#Ss' => '> ',
725: '#>[\040\011]*[\n]\s*$#Ss' => '>',
726: $this->stripRegEx => '',
727: );
728: $text = preg_replace(array_keys($expressions), array_values($expressions), $text);
729: $_offset = 0;
730: if (preg_match_all(
731: '#@!@SMARTY:([0-9]+):SMARTY@!@#is',
732: $text,
733: $matches,
734: PREG_OFFSET_CAPTURE | PREG_SET_ORDER
735: )
736: ) {
737: foreach ($matches as $match) {
738: $_length = strlen($match[ 0 ][ 0 ]);
739: $replace = $store[ $match[ 1 ][ 0 ] ];
740: $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length);
741: $_offset += strlen($replace) - $_length;
742: $_store++;
743: }
744: }
745: return $text;
746: }
747:
748: /**
749: * lazy loads internal compile plugin for tag and calls the compile method
750: * compile objects cached for reuse.
751: * class name format: Smarty_Internal_Compile_TagName
752: * plugin filename format: Smarty_Internal_TagName.php
753: *
754: * @param string $tag tag name
755: * @param array $args list of tag attributes
756: * @param mixed $param1 optional parameter
757: * @param mixed $param2 optional parameter
758: * @param mixed $param3 optional parameter
759: *
760: * @return bool|string compiled code or false
761: * @throws \SmartyCompilerException
762: */
763: public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)
764: {
765: /* @var Smarty_Internal_CompileBase $tagCompiler */
766: $tagCompiler = $this->getTagCompiler($tag);
767: // compile this tag
768: return $tagCompiler === false ? false : $tagCompiler->compile($args, $this, $param1, $param2, $param3);
769: }
770:
771: /**
772: * lazy loads internal compile plugin for tag compile objects cached for reuse.
773: *
774: * class name format: Smarty_Internal_Compile_TagName
775: * plugin filename format: Smarty_Internal_TagName.php
776: *
777: * @param string $tag tag name
778: *
779: * @return bool|\Smarty_Internal_CompileBase tag compiler object or false if not found
780: */
781: public function getTagCompiler($tag)
782: {
783: // re-use object if already exists
784: if (!isset(self::$_tag_objects[ $tag ])) {
785: // lazy load internal compiler plugin
786: $_tag = explode('_', $tag);
787: $_tag = array_map('ucfirst', $_tag);
788: $class_name = 'Smarty_Internal_Compile_' . implode('_', $_tag);
789: if (class_exists($class_name)
790: && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))
791: ) {
792: self::$_tag_objects[ $tag ] = new $class_name;
793: } else {
794: self::$_tag_objects[ $tag ] = false;
795: }
796: }
797: return self::$_tag_objects[ $tag ];
798: }
799:
800: /**
801: * Check for plugins and return function name
802: *
803: * @param $plugin_name
804: * @param string $plugin_type type of plugin
805: *
806: * @return string call name of function
807: * @throws \SmartyException
808: */
809: public function getPlugin($plugin_name, $plugin_type)
810: {
811: $function = null;
812: if ($this->caching && ($this->nocache || $this->tag_nocache)) {
813: if (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
814: $function =
815: $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
816: } elseif (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
817: $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ] =
818: $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ];
819: $function =
820: $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
821: }
822: } else {
823: if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
824: $function =
825: $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
826: } elseif (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
827: $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ] =
828: $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ];
829: $function =
830: $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
831: }
832: }
833: if (isset($function)) {
834: if ($plugin_type === 'modifier') {
835: $this->modifier_plugins[ $plugin_name ] = true;
836: }
837: return $function;
838: }
839: // loop through plugin dirs and find the plugin
840: $function = 'smarty_' . $plugin_type . '_' . $plugin_name;
841: $file = $this->smarty->loadPlugin($function, false);
842: if (is_string($file)) {
843: if ($this->caching && ($this->nocache || $this->tag_nocache)) {
844: $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
845: $file;
846: $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
847: $function;
848: } else {
849: $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
850: $file;
851: $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
852: $function;
853: }
854: if ($plugin_type === 'modifier') {
855: $this->modifier_plugins[ $plugin_name ] = true;
856: }
857: return $function;
858: }
859: if (is_callable($function)) {
860: // plugin function is defined in the script
861: return $function;
862: }
863: return false;
864: }
865:
866: /**
867: * Check for plugins by default plugin handler
868: *
869: * @param string $tag name of tag
870: * @param string $plugin_type type of plugin
871: *
872: * @return bool true if found
873: * @throws \SmartyCompilerException
874: */
875: public function getPluginFromDefaultHandler($tag, $plugin_type)
876: {
877: $callback = null;
878: $script = null;
879: $cacheable = true;
880: $result = call_user_func_array(
881: $this->smarty->default_plugin_handler_func,
882: array(
883: $tag,
884: $plugin_type,
885: $this->template,
886: &$callback,
887: &$script,
888: &$cacheable,
889: )
890: );
891: if ($result) {
892: $this->tag_nocache = $this->tag_nocache || !$cacheable;
893: if ($script !== null) {
894: if (is_file($script)) {
895: if ($this->caching && ($this->nocache || $this->tag_nocache)) {
896: $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'file' ] =
897: $script;
898: $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'function' ] =
899: $callback;
900: } else {
901: $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'file' ] =
902: $script;
903: $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'function' ] =
904: $callback;
905: }
906: include_once $script;
907: } else {
908: $this->trigger_template_error("Default plugin handler: Returned script file '{$script}' for '{$tag}' not found");
909: }
910: }
911: if (is_callable($callback)) {
912: $this->default_handler_plugins[ $plugin_type ][ $tag ] = array(
913: $callback,
914: true,
915: array()
916: );
917: return true;
918: } else {
919: $this->trigger_template_error("Default plugin handler: Returned callback for '{$tag}' not callable");
920: }
921: }
922: return false;
923: }
924:
925: /**
926: * Append code segments and remove unneeded ?> <?php transitions
927: *
928: * @param string $left
929: * @param string $right
930: *
931: * @return string
932: */
933: public function appendCode($left, $right)
934: {
935: if (preg_match('/\s*\?>\s?$/D', $left) && preg_match('/^<\?php\s+/', $right)) {
936: $left = preg_replace('/\s*\?>\s?$/D', "\n", $left);
937: $left .= preg_replace('/^<\?php\s+/', '', $right);
938: } else {
939: $left .= $right;
940: }
941: return $left;
942: }
943:
944: /**
945: * Inject inline code for nocache template sections
946: * This method gets the content of each template element from the parser.
947: * If the content is compiled code and it should be not cached the code is injected
948: * into the rendered output.
949: *
950: * @param string $content content of template element
951: * @param boolean $is_code true if content is compiled code
952: *
953: * @return string content
954: */
955: public function processNocacheCode($content, $is_code)
956: {
957: // If the template is not evaluated and we have a nocache section and or a nocache tag
958: if ($is_code && !empty($content)) {
959: // generate replacement code
960: if ((!($this->template->source->handler->recompiled) || $this->forceNocache) && $this->caching
961: && !$this->suppressNocacheProcessing && ($this->nocache || $this->tag_nocache)
962: ) {
963: $this->template->compiled->has_nocache_code = true;
964: $_output = addcslashes($content, '\'\\');
965: $_output = str_replace('^#^', '\'', $_output);
966: $_output =
967: "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/{$_output}/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n";
968: // make sure we include modifier plugins for nocache code
969: foreach ($this->modifier_plugins as $plugin_name => $dummy) {
970: if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ])) {
971: $this->required_plugins[ 'nocache' ][ $plugin_name ][ 'modifier' ] =
972: $this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ];
973: }
974: }
975: } else {
976: $_output = $content;
977: }
978: } else {
979: $_output = $content;
980: }
981: $this->modifier_plugins = array();
982: $this->suppressNocacheProcessing = false;
983: $this->tag_nocache = false;
984: return $_output;
985: }
986:
987: /**
988: * Get Id
989: *
990: * @param string $input
991: *
992: * @return bool|string
993: */
994: public function getId($input)
995: {
996: if (preg_match('~^([\'"]*)([0-9]*[a-zA-Z_]\w*)\1$~', $input, $match)) {
997: return $match[ 2 ];
998: }
999: return false;
1000: }
1001:
1002: /**
1003: * Get variable name from string
1004: *
1005: * @param string $input
1006: *
1007: * @return bool|string
1008: */
1009: public function getVariableName($input)
1010: {
1011: if (preg_match('~^[$]_smarty_tpl->tpl_vars\[[\'"]*([0-9]*[a-zA-Z_]\w*)[\'"]*\]->value$~', $input, $match)) {
1012: return $match[ 1 ];
1013: }
1014: return false;
1015: }
1016:
1017: /**
1018: * Set nocache flag in variable or create new variable
1019: *
1020: * @param string $varName
1021: */
1022: public function setNocacheInVariable($varName)
1023: {
1024: // create nocache var to make it know for further compiling
1025: if ($_var = $this->getId($varName)) {
1026: if (isset($this->template->tpl_vars[ $_var ])) {
1027: $this->template->tpl_vars[ $_var ] = clone $this->template->tpl_vars[ $_var ];
1028: $this->template->tpl_vars[ $_var ]->nocache = true;
1029: } else {
1030: $this->template->tpl_vars[ $_var ] = new Smarty_Variable(null, true);
1031: }
1032: }
1033: }
1034:
1035: /**
1036: * @param array $_attr tag attributes
1037: * @param array $validScopes
1038: *
1039: * @return int|string
1040: * @throws \SmartyCompilerException
1041: */
1042: public function convertScope($_attr, $validScopes)
1043: {
1044: $_scope = 0;
1045: if (isset($_attr[ 'scope' ])) {
1046: $_scopeName = trim($_attr[ 'scope' ], '\'"');
1047: if (is_numeric($_scopeName) && in_array($_scopeName, $validScopes)) {
1048: $_scope = $_scopeName;
1049: } elseif (is_string($_scopeName)) {
1050: $_scopeName = trim($_scopeName, '\'"');
1051: $_scope = isset($validScopes[ $_scopeName ]) ? $validScopes[ $_scopeName ] : false;
1052: } else {
1053: $_scope = false;
1054: }
1055: if ($_scope === false) {
1056: $err = var_export($_scopeName, true);
1057: $this->trigger_template_error("illegal value '{$err}' for \"scope\" attribute", null, true);
1058: }
1059: }
1060: return $_scope;
1061: }
1062:
1063: /**
1064: * Generate nocache code string
1065: *
1066: * @param string $code PHP code
1067: *
1068: * @return string
1069: */
1070: public function makeNocacheCode($code)
1071: {
1072: return "echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/<?php " .
1073: str_replace('^#^', '\'', addcslashes($code, '\'\\')) .
1074: "?>/*/%%SmartyNocache:{$this->nocache_hash}%%*/';\n";
1075: }
1076:
1077: /**
1078: * display compiler error messages without dying
1079: * If parameter $args is empty it is a parser detected syntax error.
1080: * In this case the parser is called to obtain information about expected tokens.
1081: * If parameter $args contains a string this is used as error message
1082: *
1083: * @param string $args individual error message or null
1084: * @param string $line line-number
1085: * @param null|bool $tagline if true the line number of last tag
1086: *
1087: * @throws \SmartyCompilerException when an unexpected token is found
1088: */
1089: public function trigger_template_error($args = null, $line = null, $tagline = null)
1090: {
1091: $lex = $this->parser->lex;
1092: if ($tagline === true) {
1093: // get line number of Tag
1094: $line = $lex->taglineno;
1095: } elseif (!isset($line)) {
1096: // get template source line which has error
1097: $line = $lex->line;
1098: } else {
1099: $line = (int)$line;
1100: }
1101: if (in_array(
1102: $this->template->source->type,
1103: array(
1104: 'eval',
1105: 'string'
1106: )
1107: )
1108: ) {
1109: $templateName = $this->template->source->type . ':' . trim(
1110: preg_replace(
1111: '![\t\r\n]+!',
1112: ' ',
1113: strlen($lex->data) > 40 ?
1114: substr($lex->data, 0, 40) .
1115: '...' : $lex->data
1116: )
1117: );
1118: } else {
1119: $templateName = $this->template->source->type . ':' . $this->template->source->filepath;
1120: }
1121: // $line += $this->trace_line_offset;
1122: $match = preg_split("/\n/", $lex->data);
1123: $error_text =
1124: 'Syntax error in template "' . (empty($this->trace_filepath) ? $templateName : $this->trace_filepath) .
1125: '" on line ' . ($line + $this->trace_line_offset) . ' "' .
1126: trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ])) . '" ';
1127: if (isset($args)) {
1128: // individual error message
1129: $error_text .= $args;
1130: } else {
1131: $expect = array();
1132: // expected token from parser
1133: $error_text .= ' - Unexpected "' . $lex->value . '"';
1134: if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) {
1135: foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
1136: $exp_token = $this->parser->yyTokenName[ $token ];
1137: if (isset($lex->smarty_token_names[ $exp_token ])) {
1138: // token type from lexer
1139: $expect[] = '"' . $lex->smarty_token_names[ $exp_token ] . '"';
1140: } else {
1141: // otherwise internal token name
1142: $expect[] = $this->parser->yyTokenName[ $token ];
1143: }
1144: }
1145: $error_text .= ', expected one of: ' . implode(' , ', $expect);
1146: }
1147: }
1148: if ($this->smarty->_parserdebug) {
1149: $this->parser->errorRunDown();
1150: echo ob_get_clean();
1151: flush();
1152: }
1153: $e = new SmartyCompilerException($error_text);
1154: $e->line = $line;
1155: $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ]));
1156: $e->desc = $args;
1157: $e->template = $this->template->source->filepath;
1158: throw $e;
1159: }
1160:
1161: /**
1162: * Return var_export() value with all white spaces removed
1163: *
1164: * @param mixed $value
1165: *
1166: * @return string
1167: */
1168: public function getVarExport($value)
1169: {
1170: return preg_replace('/\s/', '', var_export($value, true));
1171: }
1172:
1173: /**
1174: * enter double quoted string
1175: * - save tag stack count
1176: */
1177: public function enterDoubleQuote()
1178: {
1179: array_push($this->_tag_stack_count, $this->getTagStackCount());
1180: }
1181:
1182: /**
1183: * Return tag stack count
1184: *
1185: * @return int
1186: */
1187: public function getTagStackCount()
1188: {
1189: return count($this->_tag_stack);
1190: }
1191:
1192: /**
1193: * @param $lexerPreg
1194: *
1195: * @return mixed
1196: */
1197: public function replaceDelimiter($lexerPreg)
1198: {
1199: return str_replace(
1200: array('SMARTYldel', 'SMARTYliteral', 'SMARTYrdel', 'SMARTYautoliteral', 'SMARTYal'),
1201: array(
1202: $this->ldelPreg, $this->literalPreg, $this->rdelPreg,
1203: $this->smarty->getAutoLiteral() ? '{1,}' : '{9}',
1204: $this->smarty->getAutoLiteral() ? '' : '\\s*'
1205: ),
1206: $lexerPreg
1207: );
1208: }
1209:
1210: /**
1211: * Build lexer regular expressions for left and right delimiter and user defined literals
1212: */
1213: public function initDelimiterPreg()
1214: {
1215: $ldel = $this->smarty->getLeftDelimiter();
1216: $this->ldelLength = strlen($ldel);
1217: $this->ldelPreg = '';
1218: foreach (str_split($ldel, 1) as $chr) {
1219: $this->ldelPreg .= '[' . preg_quote($chr,'/') . ']';
1220: }
1221: $rdel = $this->smarty->getRightDelimiter();
1222: $this->rdelLength = strlen($rdel);
1223: $this->rdelPreg = '';
1224: foreach (str_split($rdel, 1) as $chr) {
1225: $this->rdelPreg .= '[' . preg_quote($chr,'/') . ']';
1226: }
1227: $literals = $this->smarty->getLiterals();
1228: if (!empty($literals)) {
1229: foreach ($literals as $key => $literal) {
1230: $literalPreg = '';
1231: foreach (str_split($literal, 1) as $chr) {
1232: $literalPreg .= '[' . preg_quote($chr,'/') . ']';
1233: }
1234: $literals[ $key ] = $literalPreg;
1235: }
1236: $this->literalPreg = '|' . implode('|', $literals);
1237: } else {
1238: $this->literalPreg = '';
1239: }
1240: }
1241:
1242: /**
1243: * leave double quoted string
1244: * - throw exception if block in string was not closed
1245: *
1246: * @throws \SmartyCompilerException
1247: */
1248: public function leaveDoubleQuote()
1249: {
1250: if (array_pop($this->_tag_stack_count) !== $this->getTagStackCount()) {
1251: $tag = $this->getOpenBlockTag();
1252: $this->trigger_template_error(
1253: "unclosed '{{$tag}}' in doubled quoted string",
1254: null,
1255: true
1256: );
1257: }
1258: }
1259:
1260: /**
1261: * Get left delimiter preg
1262: *
1263: * @return string
1264: */
1265: public function getLdelPreg()
1266: {
1267: return $this->ldelPreg;
1268: }
1269:
1270: /**
1271: * Get right delimiter preg
1272: *
1273: * @return string
1274: */
1275: public function getRdelPreg()
1276: {
1277: return $this->rdelPreg;
1278: }
1279:
1280: /**
1281: * Get length of left delimiter
1282: *
1283: * @return int
1284: */
1285: public function getLdelLength()
1286: {
1287: return $this->ldelLength;
1288: }
1289:
1290: /**
1291: * Get length of right delimiter
1292: *
1293: * @return int
1294: */
1295: public function getRdelLength()
1296: {
1297: return $this->rdelLength;
1298: }
1299:
1300: /**
1301: * Get name of current open block tag
1302: *
1303: * @return string|boolean
1304: */
1305: public function getOpenBlockTag()
1306: {
1307: $tagCount = $this->getTagStackCount();
1308: if ($tagCount) {
1309: return $this->_tag_stack[ $tagCount - 1 ][ 0 ];
1310: } else {
1311: return false;
1312: }
1313: }
1314:
1315: /**
1316: * Check if $value contains variable elements
1317: *
1318: * @param mixed $value
1319: *
1320: * @return bool|int
1321: */
1322: public function isVariable($value)
1323: {
1324: if (is_string($value)) {
1325: return preg_match('/[$(]/', $value);
1326: }
1327: if (is_bool($value) || is_numeric($value)) {
1328: return false;
1329: }
1330: if (is_array($value)) {
1331: foreach ($value as $k => $v) {
1332: if ($this->isVariable($k) || $this->isVariable($v)) {
1333: return true;
1334: }
1335: }
1336: return false;
1337: }
1338: return false;
1339: }
1340:
1341: /**
1342: * Get new prefix variable name
1343: *
1344: * @return string
1345: */
1346: public function getNewPrefixVariable()
1347: {
1348: ++self::$prefixVariableNumber;
1349: return $this->getPrefixVariable();
1350: }
1351:
1352: /**
1353: * Get current prefix variable name
1354: *
1355: * @return string
1356: */
1357: public function getPrefixVariable()
1358: {
1359: return '$_prefixVariable' . self::$prefixVariableNumber;
1360: }
1361:
1362: /**
1363: * append code to prefix buffer
1364: *
1365: * @param string $code
1366: */
1367: public function appendPrefixCode($code)
1368: {
1369: $this->prefix_code[] = $code;
1370: }
1371:
1372: /**
1373: * get prefix code string
1374: *
1375: * @return string
1376: */
1377: public function getPrefixCode()
1378: {
1379: $code = '';
1380: $prefixArray = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
1381: $this->prefixCodeStack[] = array();
1382: foreach ($prefixArray as $c) {
1383: $code = $this->appendCode($code, $c);
1384: }
1385: $this->prefix_code = array();
1386: return $code;
1387: }
1388:
1389: /**
1390: * Save current required plugins
1391: *
1392: * @param bool $init if true init required plugins
1393: */
1394: public function saveRequiredPlugins($init = false)
1395: {
1396: $this->required_plugins_stack[] = $this->required_plugins;
1397: if ($init) {
1398: $this->required_plugins = array('compiled' => array(), 'nocache' => array());
1399: }
1400: }
1401:
1402: /**
1403: * Restore required plugins
1404: */
1405: public function restoreRequiredPlugins()
1406: {
1407: $this->required_plugins = array_pop($this->required_plugins_stack);
1408: }
1409:
1410: /**
1411: * Compile code to call Smarty_Internal_Template::_checkPlugins()
1412: * for required plugins
1413: *
1414: * @return string
1415: */
1416: public function compileRequiredPlugins()
1417: {
1418: $code = $this->compileCheckPlugins($this->required_plugins[ 'compiled' ]);
1419: if ($this->caching && !empty($this->required_plugins[ 'nocache' ])) {
1420: $code .= $this->makeNocacheCode($this->compileCheckPlugins($this->required_plugins[ 'nocache' ]));
1421: }
1422: return $code;
1423: }
1424:
1425: /**
1426: * Compile code to call Smarty_Internal_Template::_checkPlugins
1427: * - checks if plugin is callable require otherwise
1428: *
1429: * @param $requiredPlugins
1430: *
1431: * @return string
1432: */
1433: public function compileCheckPlugins($requiredPlugins)
1434: {
1435: if (!empty($requiredPlugins)) {
1436: $plugins = array();
1437: foreach ($requiredPlugins as $plugin) {
1438: foreach ($plugin as $data) {
1439: $plugins[] = $data;
1440: }
1441: }
1442: return '$_smarty_tpl->_checkPlugins(' . $this->getVarExport($plugins) . ');' . "\n";
1443: } else {
1444: return '';
1445: }
1446: }
1447:
1448: /**
1449: * method to compile a Smarty template
1450: *
1451: * @param mixed $_content template source
1452: * @param bool $isTemplateSource
1453: *
1454: * @return bool true if compiling succeeded, false if it failed
1455: */
1456: abstract protected function doCompile($_content, $isTemplateSource = false);
1457:
1458: public function cStyleComment($string) {
1459: return '/*' . str_replace('*/', '* /' , $string) . '*/';
1460: }
1461:
1462: /**
1463: * Compile Tag
1464: *
1465: * @param string $tag tag name
1466: * @param array $args array with tag attributes
1467: * @param array $parameter array with compilation parameter
1468: *
1469: * @throws SmartyCompilerException
1470: * @throws SmartyException
1471: * @return string compiled code
1472: */
1473: private function compileTag2($tag, $args, $parameter)
1474: {
1475: $plugin_type = '';
1476: // $args contains the attributes parsed and compiled by the lexer/parser
1477: // assume that tag does compile into code, but creates no HTML output
1478: $this->has_code = true;
1479: // log tag/attributes
1480: if (isset($this->smarty->_cache[ 'get_used_tags' ])) {
1481: $this->template->_cache[ 'used_tags' ][] = array(
1482: $tag,
1483: $args
1484: );
1485: }
1486: // check nocache option flag
1487: foreach ($args as $arg) {
1488: if (!is_array($arg)) {
1489: if ($arg === "'nocache'" || $arg === 'nocache') {
1490: $this->tag_nocache = true;
1491: }
1492: } else {
1493: foreach ($arg as $k => $v) {
1494: if (($k === "'nocache'" || $k === 'nocache') && (trim($v, "'\" ") === 'true')) {
1495: $this->tag_nocache = true;
1496: }
1497: }
1498: }
1499: }
1500: // compile the smarty tag (required compile classes to compile the tag are auto loaded)
1501: if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) {
1502: if (isset($this->parent_compiler->tpl_function[ $tag ])
1503: || (isset($this->template->smarty->ext->_tplFunction)
1504: && $this->template->smarty->ext->_tplFunction->getTplFunction($this->template, $tag) !== false)
1505: ) {
1506: // template defined by {template} tag
1507: $args[ '_attr' ][ 'name' ] = "'{$tag}'";
1508: $_output = $this->callTagCompiler('call', $args, $parameter);
1509: }
1510: }
1511: if ($_output !== false) {
1512: if ($_output !== true) {
1513: // did we get compiled code
1514: if ($this->has_code) {
1515: // return compiled code
1516: return $_output;
1517: }
1518: }
1519: // tag did not produce compiled code
1520: return null;
1521: } else {
1522: // map_named attributes
1523: if (isset($args[ '_attr' ])) {
1524: foreach ($args[ '_attr' ] as $key => $attribute) {
1525: if (is_array($attribute)) {
1526: $args = array_merge($args, $attribute);
1527: }
1528: }
1529: }
1530: // not an internal compiler tag
1531: if (strlen($tag) < 6 || substr($tag, -5) !== 'close') {
1532: // check if tag is a registered object
1533: if (isset($this->smarty->registered_objects[ $tag ]) && isset($parameter[ 'object_method' ])) {
1534: $method = $parameter[ 'object_method' ];
1535: if (!in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])
1536: && (empty($this->smarty->registered_objects[ $tag ][ 1 ])
1537: || in_array($method, $this->smarty->registered_objects[ $tag ][ 1 ]))
1538: ) {
1539: return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method);
1540: } elseif (in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])) {
1541: return $this->callTagCompiler(
1542: 'private_object_block_function',
1543: $args,
1544: $parameter,
1545: $tag,
1546: $method
1547: );
1548: } else {
1549: // throw exception
1550: $this->trigger_template_error(
1551: 'not allowed method "' . $method . '" in registered object "' .
1552: $tag . '"',
1553: null,
1554: true
1555: );
1556: }
1557: }
1558: // check if tag is registered
1559: foreach (array(
1560: Smarty::PLUGIN_COMPILER,
1561: Smarty::PLUGIN_FUNCTION,
1562: Smarty::PLUGIN_BLOCK,
1563: ) as $plugin_type) {
1564: if (isset($this->smarty->registered_plugins[ $plugin_type ][ $tag ])) {
1565: // if compiler function plugin call it now
1566: if ($plugin_type === Smarty::PLUGIN_COMPILER) {
1567: $new_args = array();
1568: foreach ($args as $key => $mixed) {
1569: if (is_array($mixed)) {
1570: $new_args = array_merge($new_args, $mixed);
1571: } else {
1572: $new_args[ $key ] = $mixed;
1573: }
1574: }
1575: if (!$this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 1 ]) {
1576: $this->tag_nocache = true;
1577: }
1578: return call_user_func_array(
1579: $this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 0 ],
1580: array(
1581: $new_args,
1582: $this
1583: )
1584: );
1585: }
1586: // compile registered function or block function
1587: if ($plugin_type === Smarty::PLUGIN_FUNCTION || $plugin_type === Smarty::PLUGIN_BLOCK) {
1588: return $this->callTagCompiler(
1589: 'private_registered_' . $plugin_type,
1590: $args,
1591: $parameter,
1592: $tag
1593: );
1594: }
1595: }
1596: }
1597: // check plugins from plugins folder
1598: foreach ($this->plugin_search_order as $plugin_type) {
1599: if ($plugin_type === Smarty::PLUGIN_COMPILER
1600: && $this->smarty->loadPlugin('smarty_compiler_' . $tag)
1601: && (!isset($this->smarty->security_policy)
1602: || $this->smarty->security_policy->isTrustedTag($tag, $this))
1603: ) {
1604: $plugin = 'smarty_compiler_' . $tag;
1605: if (is_callable($plugin)) {
1606: // convert arguments format for old compiler plugins
1607: $new_args = array();
1608: foreach ($args as $key => $mixed) {
1609: if (is_array($mixed)) {
1610: $new_args = array_merge($new_args, $mixed);
1611: } else {
1612: $new_args[ $key ] = $mixed;
1613: }
1614: }
1615: return $plugin($new_args, $this->smarty);
1616: }
1617: if (class_exists($plugin, false)) {
1618: $plugin_object = new $plugin;
1619: if (method_exists($plugin_object, 'compile')) {
1620: return $plugin_object->compile($args, $this);
1621: }
1622: }
1623: throw new SmartyException("Plugin '{$tag}' not callable");
1624: } else {
1625: if ($function = $this->getPlugin($tag, $plugin_type)) {
1626: if (!isset($this->smarty->security_policy)
1627: || $this->smarty->security_policy->isTrustedTag($tag, $this)
1628: ) {
1629: return $this->callTagCompiler(
1630: 'private_' . $plugin_type . '_plugin',
1631: $args,
1632: $parameter,
1633: $tag,
1634: $function
1635: );
1636: }
1637: }
1638: }
1639: }
1640: if (is_callable($this->smarty->default_plugin_handler_func)) {
1641: $found = false;
1642: // look for already resolved tags
1643: foreach ($this->plugin_search_order as $plugin_type) {
1644: if (isset($this->default_handler_plugins[ $plugin_type ][ $tag ])) {
1645: $found = true;
1646: break;
1647: }
1648: }
1649: if (!$found) {
1650: // call default handler
1651: foreach ($this->plugin_search_order as $plugin_type) {
1652: if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) {
1653: $found = true;
1654: break;
1655: }
1656: }
1657: }
1658: if ($found) {
1659: // if compiler function plugin call it now
1660: if ($plugin_type === Smarty::PLUGIN_COMPILER) {
1661: $new_args = array();
1662: foreach ($args as $key => $mixed) {
1663: if (is_array($mixed)) {
1664: $new_args = array_merge($new_args, $mixed);
1665: } else {
1666: $new_args[ $key ] = $mixed;
1667: }
1668: }
1669: return call_user_func_array(
1670: $this->default_handler_plugins[ $plugin_type ][ $tag ][ 0 ],
1671: array(
1672: $new_args,
1673: $this
1674: )
1675: );
1676: } else {
1677: return $this->callTagCompiler(
1678: 'private_registered_' . $plugin_type,
1679: $args,
1680: $parameter,
1681: $tag
1682: );
1683: }
1684: }
1685: }
1686: } else {
1687: // compile closing tag of block function
1688: $base_tag = substr($tag, 0, -5);
1689: // check if closing tag is a registered object
1690: if (isset($this->smarty->registered_objects[ $base_tag ]) && isset($parameter[ 'object_method' ])) {
1691: $method = $parameter[ 'object_method' ];
1692: if (in_array($method, $this->smarty->registered_objects[ $base_tag ][ 3 ])) {
1693: return $this->callTagCompiler(
1694: 'private_object_block_function',
1695: $args,
1696: $parameter,
1697: $tag,
1698: $method
1699: );
1700: } else {
1701: // throw exception
1702: $this->trigger_template_error(
1703: 'not allowed closing tag method "' . $method .
1704: '" in registered object "' . $base_tag . '"',
1705: null,
1706: true
1707: );
1708: }
1709: }
1710: // registered block tag ?
1711: if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ])
1712: || isset($this->default_handler_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ])
1713: ) {
1714: return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag);
1715: }
1716: // registered function tag ?
1717: if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_FUNCTION ][ $tag ])) {
1718: return $this->callTagCompiler('private_registered_function', $args, $parameter, $tag);
1719: }
1720: // block plugin?
1721: if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) {
1722: return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function);
1723: }
1724: // function plugin?
1725: if ($function = $this->getPlugin($tag, Smarty::PLUGIN_FUNCTION)) {
1726: if (!isset($this->smarty->security_policy)
1727: || $this->smarty->security_policy->isTrustedTag($tag, $this)
1728: ) {
1729: return $this->callTagCompiler('private_function_plugin', $args, $parameter, $tag, $function);
1730: }
1731: }
1732: // registered compiler plugin ?
1733: if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ])) {
1734: // if compiler function plugin call it now
1735: $args = array();
1736: if (!$this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 1 ]) {
1737: $this->tag_nocache = true;
1738: }
1739: return call_user_func_array(
1740: $this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 0 ],
1741: array(
1742: $args,
1743: $this
1744: )
1745: );
1746: }
1747: if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {
1748: $plugin = 'smarty_compiler_' . $tag;
1749: if (is_callable($plugin)) {
1750: return $plugin($args, $this->smarty);
1751: }
1752: if (class_exists($plugin, false)) {
1753: $plugin_object = new $plugin;
1754: if (method_exists($plugin_object, 'compile')) {
1755: return $plugin_object->compile($args, $this);
1756: }
1757: }
1758: throw new SmartyException("Plugin '{$tag}' not callable");
1759: }
1760: }
1761: $this->trigger_template_error("unknown tag '{$tag}'", null, true);
1762: }
1763: }
1764: }
1765: