1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: 13: 14: 15: 16: 17:
18: abstract class Smarty_Internal_TemplateCompilerBase
19: {
20: 21: 22: 23: 24:
25: private $nocache_hash = null;
26:
27: 28: 29: 30: 31:
32: public $suppressNocacheProcessing = false;
33:
34: 35: 36: 37: 38:
39: public $suppressMergedTemplates = false;
40:
41: 42: 43: 44: 45:
46: public static $_tag_objects = array();
47:
48: 49: 50: 51: 52:
53: public $_tag_stack = array();
54:
55: 56: 57: 58: 59:
60: public $template = null;
61:
62: 63: 64: 65: 66:
67: public $merged_templates = array();
68:
69: 70: 71: 72: 73:
74: public $sources = array();
75:
76: 77: 78: 79: 80:
81: public $inheritance = false;
82:
83: 84: 85: 86: 87:
88: public $inheritance_child = false;
89:
90: 91: 92: 93: 94:
95: public $extends_uid = array();
96:
97: 98: 99: 100: 101:
102: public $trace_line_offset = 0;
103:
104: 105: 106: 107: 108:
109: public $trace_uid = '';
110:
111: 112: 113: 114: 115:
116: public $trace_filepath = '';
117: 118: 119: 120: 121:
122: public $trace_stack = array();
123:
124: 125: 126: 127: 128:
129: public $default_handler_plugins = array();
130:
131: 132: 133: 134: 135:
136: public $default_modifier_list = null;
137:
138: 139: 140: 141: 142:
143: public $forceNocache = false;
144:
145: 146: 147: 148: 149:
150: public $suppressHeader = false;
151:
152: 153: 154: 155: 156:
157: public $suppressTemplatePropertyHeader = false;
158:
159: 160: 161: 162: 163:
164: public $suppressFilter = false;
165:
166: 167: 168: 169: 170:
171: public $write_compiled_code = true;
172:
173: 174: 175: 176: 177:
178: public $compiles_template_function = false;
179:
180: 181: 182: 183: 184:
185: public $called_functions = array();
186:
187: 188: 189: 190: 191:
192: public $modifier_plugins = array();
193:
194: 195: 196: 197: 198:
199: public $known_modifier_type = array();
200:
201: 202: 203: 204: 205: 206: 207:
208: abstract protected function doCompile($_content);
209:
210: 211: 212:
213: public function __construct()
214: {
215: $this->nocache_hash = str_replace(array('.', ','), '-', uniqid(rand(), true));
216: }
217:
218: 219: 220: 221: 222: 223: 224: 225:
226: public function compileTemplate(Smarty_Internal_Template $template, $nocache = false)
227: {
228: if (empty($template->properties['nocache_hash'])) {
229: $template->properties['nocache_hash'] = $this->nocache_hash;
230: } else {
231: $this->nocache_hash = $template->properties['nocache_hash'];
232: }
233:
234: $this->nocache = $nocache;
235: $this->tag_nocache = false;
236:
237: $this->template = $template;
238:
239: $this->template->has_nocache_code = false;
240: $save_source = $this->template->source;
241:
242: $template_header = '';
243: if (!$this->suppressHeader) {
244: $template_header .= "<?php /* Smarty version " . Smarty::SMARTY_VERSION . ", created on " . strftime("%Y-%m-%d %H:%M:%S") . "\n";
245: $template_header .= " compiled from \"" . $this->template->source->filepath . "\" */ ?>\n";
246: }
247:
248: if (empty($this->template->source->components)) {
249: $this->sources = array($template->source);
250: } else {
251:
252: $this->sources = array_reverse($template->source->components);
253: }
254: $loop = 0;
255:
256: while ($this->template->source = array_shift($this->sources)) {
257: $this->smarty->_current_file = $this->template->source->filepath;
258: if ($this->smarty->debugging) {
259: Smarty_Internal_Debug::start_compile($this->template);
260: }
261: $no_sources = count($this->sources);
262: if ($loop || $no_sources) {
263: $this->template->properties['file_dependency'][$this->template->source->uid] = array($this->template->source->filepath, $this->template->source->timestamp, $this->template->source->type);
264: }
265: $loop ++;
266: if ($no_sources) {
267: $this->inheritance_child = true;
268: } else {
269: $this->inheritance_child = false;
270: }
271: do {
272: $_compiled_code = '';
273:
274: $this->abort_and_recompile = false;
275:
276: $_content = $this->template->source->content;
277: if ($_content != '') {
278:
279: if ((isset($this->smarty->autoload_filters['pre']) || isset($this->smarty->registered_filters['pre'])) && !$this->suppressFilter) {
280: $_content = Smarty_Internal_Filter_Handler::runFilter('pre', $_content, $template);
281: }
282:
283: $_compiled_code = $this->doCompile($_content);
284: }
285: } while ($this->abort_and_recompile);
286: if ($this->smarty->debugging) {
287: Smarty_Internal_Debug::end_compile($this->template);
288: }
289: }
290:
291: $this->template->source = $save_source;
292: unset($save_source);
293: $this->smarty->_current_file = $this->template->source->filepath;
294:
295: unset($this->parser->root_buffer, $this->parser->current_buffer, $this->parser, $this->lex, $this->template);
296: self::$_tag_objects = array();
297:
298: $merged_code = '';
299: if (!$this->suppressMergedTemplates && !empty($this->merged_templates)) {
300: foreach ($this->merged_templates as $code) {
301: $merged_code .= $code;
302: }
303: }
304:
305: if ((isset($this->smarty->autoload_filters['post']) || isset($this->smarty->registered_filters['post'])) && !$this->suppressFilter && $_compiled_code != '') {
306: $_compiled_code = Smarty_Internal_Filter_Handler::runFilter('post', $_compiled_code, $template);
307: }
308: if ($this->suppressTemplatePropertyHeader) {
309: $code = $_compiled_code . $merged_code;
310: } else {
311: $code = $template_header . $template->createTemplateCodeFrame($_compiled_code) . $merged_code;
312: }
313:
314: unset ($template->source->content);
315:
316: return $code;
317: }
318:
319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331:
332: public function compileTag($tag, $args, $parameter = array())
333: {
334:
335:
336: $this->has_code = true;
337: $this->has_output = false;
338:
339: if (isset($this->smarty->get_used_tags) && $this->smarty->get_used_tags) {
340: $this->template->used_tags[] = array($tag, $args);
341: }
342:
343: if (in_array("'nocache'", $args) || in_array(array('nocache' => 'true'), $args)
344: || in_array(array('nocache' => '"true"'), $args) || in_array(array('nocache' => "'true'"), $args)
345: ) {
346: $this->tag_nocache = true;
347: }
348:
349: if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) {
350: if (isset($this->smarty->template_functions[$tag])) {
351:
352: $args['_attr']['name'] = "'" . $tag . "'";
353: $_output = $this->callTagCompiler('call', $args, $parameter);
354: }
355: }
356: if ($_output !== false) {
357: if ($_output !== true) {
358:
359: if ($this->has_code) {
360:
361: if ($this->has_output) {
362: $_output .= "\n";
363: }
364:
365: return $_output;
366: }
367: }
368:
369: return null;
370: } else {
371:
372: if (isset($args['_attr'])) {
373: foreach ($args['_attr'] as $key => $attribute) {
374: if (is_array($attribute)) {
375: $args = array_merge($args, $attribute);
376: }
377: }
378: }
379:
380: if (strlen($tag) < 6 || substr($tag, - 5) != 'close') {
381:
382: if (isset($this->smarty->registered_objects[$tag]) && isset($parameter['object_method'])) {
383: $method = $parameter['object_method'];
384: if (!in_array($method, $this->smarty->registered_objects[$tag][3]) &&
385: (empty($this->smarty->registered_objects[$tag][1]) || in_array($method, $this->smarty->registered_objects[$tag][1]))
386: ) {
387: return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method);
388: } elseif (in_array($method, $this->smarty->registered_objects[$tag][3])) {
389: return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag, $method);
390: } else {
391:
392: $this->trigger_template_error('not allowed method "' . $method . '" in registered object "' . $tag . '"', $this->lex->taglineno);
393: }
394: }
395:
396: foreach (array(Smarty::PLUGIN_COMPILER, Smarty::PLUGIN_FUNCTION, Smarty::PLUGIN_BLOCK) as $plugin_type) {
397: if (isset($this->smarty->registered_plugins[$plugin_type][$tag])) {
398:
399: if ($plugin_type == Smarty::PLUGIN_COMPILER) {
400: $new_args = array();
401: foreach ($args as $key => $mixed) {
402: if (is_array($mixed)) {
403: $new_args = array_merge($new_args, $mixed);
404: } else {
405: $new_args[$key] = $mixed;
406: }
407: }
408: if (!$this->smarty->registered_plugins[$plugin_type][$tag][1]) {
409: $this->tag_nocache = true;
410: }
411: $function = $this->smarty->registered_plugins[$plugin_type][$tag][0];
412: if (!is_array($function)) {
413: return $function($new_args, $this);
414: } elseif (is_object($function[0])) {
415: return $this->smarty->registered_plugins[$plugin_type][$tag][0][0]->$function[1]($new_args, $this);
416: } else {
417: return call_user_func_array($function, array($new_args, $this));
418: }
419: }
420:
421: if ($plugin_type == Smarty::PLUGIN_FUNCTION || $plugin_type == Smarty::PLUGIN_BLOCK) {
422: return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter, $tag);
423: }
424: }
425: }
426:
427: foreach ($this->smarty->plugin_search_order as $plugin_type) {
428: if ($plugin_type == Smarty::PLUGIN_COMPILER && $this->smarty->loadPlugin('smarty_compiler_' . $tag) && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))) {
429: $plugin = 'smarty_compiler_' . $tag;
430: if (is_callable($plugin)) {
431:
432: $new_args = array();
433: foreach ($args as $key => $mixed) {
434: if (is_array($mixed)) {
435: $new_args = array_merge($new_args, $mixed);
436: } else {
437: $new_args[$key] = $mixed;
438: }
439: }
440:
441: return $plugin($new_args, $this->smarty);
442: }
443: if (class_exists($plugin, false)) {
444: $plugin_object = new $plugin;
445: if (method_exists($plugin_object, 'compile')) {
446: return $plugin_object->compile($args, $this);
447: }
448: }
449: throw new SmartyException("Plugin \"{$tag}\" not callable");
450: } else {
451: if ($function = $this->getPlugin($tag, $plugin_type)) {
452: if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) {
453: return $this->callTagCompiler('private_' . $plugin_type . '_plugin', $args, $parameter, $tag, $function);
454: }
455: }
456: }
457: }
458: if (is_callable($this->smarty->default_plugin_handler_func)) {
459: $found = false;
460:
461: foreach ($this->smarty->plugin_search_order as $plugin_type) {
462: if (isset($this->default_handler_plugins[$plugin_type][$tag])) {
463: $found = true;
464: break;
465: }
466: }
467: if (!$found) {
468:
469: foreach ($this->smarty->plugin_search_order as $plugin_type) {
470: if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) {
471: $found = true;
472: break;
473: }
474: }
475: }
476: if ($found) {
477:
478: if ($plugin_type == Smarty::PLUGIN_COMPILER) {
479: $new_args = array();
480: foreach ($args as $mixed) {
481: $new_args = array_merge($new_args, $mixed);
482: }
483: $function = $this->default_handler_plugins[$plugin_type][$tag][0];
484: if (!is_array($function)) {
485: return $function($new_args, $this);
486: } elseif (is_object($function[0])) {
487: return $this->default_handler_plugins[$plugin_type][$tag][0][0]->$function[1]($new_args, $this);
488: } else {
489: return call_user_func_array($function, array($new_args, $this));
490: }
491: } else {
492: return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter, $tag);
493: }
494: }
495: }
496: } else {
497:
498: $base_tag = substr($tag, 0, - 5);
499:
500: if (isset($this->smarty->registered_objects[$base_tag]) && isset($parameter['object_method'])) {
501: $method = $parameter['object_method'];
502: if (in_array($method, $this->smarty->registered_objects[$base_tag][3])) {
503: return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag, $method);
504: } else {
505:
506: $this->trigger_template_error('not allowed closing tag method "' . $method . '" in registered object "' . $base_tag . '"', $this->lex->taglineno);
507: }
508: }
509:
510: if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_BLOCK][$base_tag]) || isset($this->default_handler_plugins[Smarty::PLUGIN_BLOCK][$base_tag])) {
511: return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag);
512: }
513:
514: if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) {
515: return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function);
516: }
517:
518: if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag])) {
519:
520: $args = array();
521: if (!$this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][1]) {
522: $this->tag_nocache = true;
523: }
524: $function = $this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][0];
525: if (!is_array($function)) {
526: return $function($args, $this);
527: } elseif (is_object($function[0])) {
528: return $this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][0][0]->$function[1]($args, $this);
529: } else {
530: return call_user_func_array($function, array($args, $this));
531: }
532: }
533: if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {
534: $plugin = 'smarty_compiler_' . $tag;
535: if (is_callable($plugin)) {
536: return $plugin($args, $this->smarty);
537: }
538: if (class_exists($plugin, false)) {
539: $plugin_object = new $plugin;
540: if (method_exists($plugin_object, 'compile')) {
541: return $plugin_object->compile($args, $this);
542: }
543: }
544: throw new SmartyException("Plugin \"{$tag}\" not callable");
545: }
546: }
547: $this->trigger_template_error("unknown tag \"" . $tag . "\"", $this->lex->taglineno);
548: }
549: }
550:
551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564:
565: public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)
566: {
567:
568: if (isset(self::$_tag_objects[$tag])) {
569:
570: return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3);
571: }
572:
573: $class_name = 'Smarty_Internal_Compile_' . $tag;
574: if ($this->smarty->loadPlugin($class_name)) {
575:
576: if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) {
577:
578: self::$_tag_objects[$tag] = new $class_name;
579:
580: return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3);
581: }
582: }
583:
584: return false;
585: }
586:
587: 588: 589: 590: 591: 592: 593: 594:
595: public function getPlugin($plugin_name, $plugin_type)
596: {
597: $function = null;
598: if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
599: if (isset($this->template->required_plugins['nocache'][$plugin_name][$plugin_type])) {
600: $function = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'];
601: } elseif (isset($this->template->required_plugins['compiled'][$plugin_name][$plugin_type])) {
602: $this->template->required_plugins['nocache'][$plugin_name][$plugin_type] = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type];
603: $function = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'];
604: }
605: } else {
606: if (isset($this->template->required_plugins['compiled'][$plugin_name][$plugin_type])) {
607: $function = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'];
608: } elseif (isset($this->template->required_plugins['nocache'][$plugin_name][$plugin_type])) {
609: $this->template->required_plugins['compiled'][$plugin_name][$plugin_type] = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type];
610: $function = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'];
611: }
612: }
613: if (isset($function)) {
614: if ($plugin_type == 'modifier') {
615: $this->modifier_plugins[$plugin_name] = true;
616: }
617:
618: return $function;
619: }
620:
621: $function = 'smarty_' . $plugin_type . '_' . $plugin_name;
622: $file = $this->smarty->loadPlugin($function, false);
623:
624: if (is_string($file)) {
625: if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
626: $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['file'] = $file;
627: $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'] = $function;
628: } else {
629: $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['file'] = $file;
630: $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'] = $function;
631: }
632: if ($plugin_type == 'modifier') {
633: $this->modifier_plugins[$plugin_name] = true;
634: }
635:
636: return $function;
637: }
638: if (is_callable($function)) {
639:
640: return $function;
641: }
642:
643: return false;
644: }
645:
646: 647: 648: 649: 650: 651: 652: 653:
654: public function getPluginFromDefaultHandler($tag, $plugin_type)
655: {
656: $callback = null;
657: $script = null;
658: $cacheable = true;
659: $result = call_user_func_array(
660: $this->smarty->default_plugin_handler_func, array($tag, $plugin_type, $this->template, &$callback, &$script, &$cacheable)
661: );
662: if ($result) {
663: $this->tag_nocache = $this->tag_nocache || !$cacheable;
664: if ($script !== null) {
665: if (is_file($script)) {
666: if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
667: $this->template->required_plugins['nocache'][$tag][$plugin_type]['file'] = $script;
668: $this->template->required_plugins['nocache'][$tag][$plugin_type]['function'] = $callback;
669: } else {
670: $this->template->required_plugins['compiled'][$tag][$plugin_type]['file'] = $script;
671: $this->template->required_plugins['compiled'][$tag][$plugin_type]['function'] = $callback;
672: }
673: include_once $script;
674: } else {
675: $this->trigger_template_error("Default plugin handler: Returned script file \"{$script}\" for \"{$tag}\" not found");
676: }
677: }
678: if (!is_string($callback) && !(is_array($callback) && is_string($callback[0]) && is_string($callback[1]))) {
679: $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" must be a static function name or array of class and function name");
680: }
681: if (is_callable($callback)) {
682: $this->default_handler_plugins[$plugin_type][$tag] = array($callback, true, array());
683:
684: return true;
685: } else {
686: $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" not callable");
687: }
688: }
689:
690: return false;
691: }
692:
693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703:
704: public function processNocacheCode($content, $is_code)
705: {
706:
707: if ($is_code && !empty($content)) {
708:
709: if ((!($this->template->source->recompiled) || $this->forceNocache) && $this->template->caching && !$this->suppressNocacheProcessing &&
710: ($this->nocache || $this->tag_nocache)
711: ) {
712: $this->template->has_nocache_code = true;
713: $_output = addcslashes($content, '\'\\');
714: $_output = str_replace("^#^", "'", $_output);
715: $_output = "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/" . $_output . "/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n";
716:
717: foreach ($this->modifier_plugins as $plugin_name => $dummy) {
718: if (isset($this->template->required_plugins['compiled'][$plugin_name]['modifier'])) {
719: $this->template->required_plugins['nocache'][$plugin_name]['modifier'] = $this->template->required_plugins['compiled'][$plugin_name]['modifier'];
720: }
721: }
722: } else {
723: $_output = $content;
724: }
725: } else {
726: $_output = $content;
727: }
728: $this->modifier_plugins = array();
729: $this->suppressNocacheProcessing = false;
730: $this->tag_nocache = false;
731:
732: return $_output;
733: }
734:
735: 736: 737: 738: 739: 740: 741: 742:
743: public function pushTrace($file, $uid, $line, $debug = true)
744: {
745: if ($this->smarty->debugging && $debug) {
746: Smarty_Internal_Debug::end_compile($this->template);
747: }
748: array_push($this->trace_stack, array($this->smarty->_current_file, $this->trace_filepath, $this->trace_uid, $this->trace_line_offset));
749: $this->trace_filepath = $this->smarty->_current_file = $file;
750: $this->trace_uid = $uid;
751: $this->trace_line_offset = $line;
752: if ($this->smarty->debugging) {
753: Smarty_Internal_Debug::start_compile($this->template);
754: }
755: }
756:
757: 758: 759: 760:
761: public function popTrace()
762: {
763: if ($this->smarty->debugging) {
764: Smarty_Internal_Debug::end_compile($this->template);
765: }
766: $r = array_pop($this->trace_stack);
767: $this->smarty->_current_file = $r[0];
768: $this->trace_filepath = $r[1];
769: $this->trace_uid = $r[2];
770: $this->trace_line_offset = $r[3];
771: if ($this->smarty->debugging) {
772: Smarty_Internal_Debug::start_compile($this->template);
773: }
774: }
775:
776: 777: 778: 779: 780: 781: 782: 783: 784: 785: 786:
787: public function trigger_template_error($args = null, $line = null)
788: {
789:
790: if (!isset($line)) {
791: $line = $this->lex->line;
792: }
793:
794: $match = preg_split("/\n/", $this->lex->data);
795: $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])) . '" ';
796: if (isset($args)) {
797:
798: $error_text .= $args;
799: } else {
800:
801: $error_text .= ' - Unexpected "' . $this->lex->value . '"';
802: if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) {
803: foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
804: $exp_token = $this->parser->yyTokenName[$token];
805: if (isset($this->lex->smarty_token_names[$exp_token])) {
806:
807: $expect[] = '"' . $this->lex->smarty_token_names[$exp_token] . '"';
808: } else {
809:
810: $expect[] = $this->parser->yyTokenName[$token];
811: }
812: }
813: $error_text .= ', expected one of: ' . implode(' , ', $expect);
814: }
815: }
816: $e = new SmartyCompilerException($error_text);
817: $e->line = $line;
818: $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[$line - 1]));
819: $e->desc = $args;
820: $e->template = $this->template->source->filepath;
821: throw $e;
822: }
823: }
824: