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 */ 22abstract 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 $par = implode(',', $parameter); 625 $parHasFuction = strpos($par, '(') !== false; 626 if ($func_name === 'isset') { 627 if (count($parameter) === 0) { 628 $this->trigger_template_error('Illegal number of parameter in "isset()"'); 629 } 630 if ($parHasFuction) { 631 $pa = array(); 632 foreach ($parameter as $p) { 633 $pa[] = (strpos($p, '(') === false) ? ('isset(' . $p . ')') : ('(' . $p . ' !== null )'); 634 } 635 return '(' . implode(' && ', $pa) . ')'; 636 } else { 637 $isset_par = str_replace("')->value", "',null,true,false)->value", $par); 638 } 639 return $name . '(' . $isset_par . ')'; 640 } elseif (in_array( 641 $func_name, 642 array( 643 'empty', 644 'reset', 645 'current', 646 'end', 647 'prev', 648 'next' 649 ) 650 ) 651 ) { 652 if (count($parameter) !== 1) { 653 $this->trigger_template_error("Illegal number of parameter in '{$func_name()}'"); 654 } 655 if ($func_name === 'empty') { 656 if ($parHasFuction && version_compare(PHP_VERSION, '5.5.0', '<')) { 657 return '(' . $parameter[ 0 ] . ' === false )'; 658 } else { 659 return $func_name . '(' . 660 str_replace("')->value", "',null,true,false)->value", $parameter[ 0 ]) . ')'; 661 } 662 } else { 663 return $func_name . '(' . $parameter[ 0 ] . ')'; 664 } 665 } else { 666 return $name . '(' . implode(',', $parameter) . ')'; 667 } 668 } else { 669 $this->trigger_template_error("unknown function '{$name}'"); 670 } 671 } 672 } 673 674 /** 675 * This method is called from parser to process a text content section 676 * - remove text from inheritance child templates as they may generate output 677 * - strip text if strip is enabled 678 * 679 * @param string $text 680 * 681 * @return null|\Smarty_Internal_ParseTree_Text 682 */ 683 public function processText($text) 684 { 685 if ((string)$text != '') { 686 $store = array(); 687 $_store = 0; 688 if ($this->parser->strip) { 689 if (strpos($text, '<') !== false) { 690 // capture html elements not to be messed with 691 $_offset = 0; 692 if (preg_match_all( 693 '#(<script[^>]*>.*?</script[^>]*>)|(<textarea[^>]*>.*?</textarea[^>]*>)|(<pre[^>]*>.*?</pre[^>]*>)#is', 694 $text, 695 $matches, 696 PREG_OFFSET_CAPTURE | PREG_SET_ORDER 697 ) 698 ) { 699 foreach ($matches as $match) { 700 $store[] = $match[ 0 ][ 0 ]; 701 $_length = strlen($match[ 0 ][ 0 ]); 702 $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@'; 703 $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length); 704 $_offset += $_length - strlen($replace); 705 $_store++; 706 } 707 } 708 $expressions = array(// replace multiple spaces between tags by a single space 709 '#(:SMARTY@!@|>)[\040\011]+(?=@!@SMARTY:|<)#s' => '\1 \2', 710 // remove newline between tags 711 '#(:SMARTY@!@|>)[\040\011]*[\n]\s*(?=@!@SMARTY:|<)#s' => '\1\2', 712 // remove multiple spaces between attributes (but not in attribute values!) 713 '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5', 714 '#>[\040\011]+$#Ss' => '> ', 715 '#>[\040\011]*[\n]\s*$#Ss' => '>', 716 $this->stripRegEx => '', 717 ); 718 $text = preg_replace(array_keys($expressions), array_values($expressions), $text); 719 $_offset = 0; 720 if (preg_match_all( 721 '#@!@SMARTY:([0-9]+):SMARTY@!@#is', 722 $text, 723 $matches, 724 PREG_OFFSET_CAPTURE | PREG_SET_ORDER 725 ) 726 ) { 727 foreach ($matches as $match) { 728 $_length = strlen($match[ 0 ][ 0 ]); 729 $replace = $store[ $match[ 1 ][ 0 ] ]; 730 $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length); 731 $_offset += strlen($replace) - $_length; 732 $_store++; 733 } 734 } 735 } else { 736 $text = preg_replace($this->stripRegEx, '', $text); 737 } 738 } 739 return new Smarty_Internal_ParseTree_Text($text); 740 } 741 return null; 742 } 743 744 /** 745 * lazy loads internal compile plugin for tag and calls the compile method 746 * compile objects cached for reuse. 747 * class name format: Smarty_Internal_Compile_TagName 748 * plugin filename format: Smarty_Internal_TagName.php 749 * 750 * @param string $tag tag name 751 * @param array $args list of tag attributes 752 * @param mixed $param1 optional parameter 753 * @param mixed $param2 optional parameter 754 * @param mixed $param3 optional parameter 755 * 756 * @return bool|string compiled code or false 757 * @throws \SmartyCompilerException 758 */ 759 public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null) 760 { 761 /* @var Smarty_Internal_CompileBase $tagCompiler */ 762 $tagCompiler = $this->getTagCompiler($tag); 763 // compile this tag 764 return $tagCompiler === false ? false : $tagCompiler->compile($args, $this, $param1, $param2, $param3); 765 } 766 767 /** 768 * lazy loads internal compile plugin for tag compile objects cached for reuse. 769 * 770 * class name format: Smarty_Internal_Compile_TagName 771 * plugin filename format: Smarty_Internal_TagName.php 772 * 773 * @param string $tag tag name 774 * 775 * @return bool|\Smarty_Internal_CompileBase tag compiler object or false if not found 776 */ 777 public function getTagCompiler($tag) 778 { 779 // re-use object if already exists 780 if (!isset(self::$_tag_objects[ $tag ])) { 781 // lazy load internal compiler plugin 782 $_tag = explode('_', $tag); 783 $_tag = array_map('ucfirst', $_tag); 784 $class_name = 'Smarty_Internal_Compile_' . implode('_', $_tag); 785 if (class_exists($class_name) 786 && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) 787 ) { 788 self::$_tag_objects[ $tag ] = new $class_name; 789 } else { 790 self::$_tag_objects[ $tag ] = false; 791 } 792 } 793 return self::$_tag_objects[ $tag ]; 794 } 795 796 /** 797 * Check for plugins and return function name 798 * 799 * @param $plugin_name 800 * @param string $plugin_type type of plugin 801 * 802 * @return string call name of function 803 * @throws \SmartyException 804 */ 805 public function getPlugin($plugin_name, $plugin_type) 806 { 807 $function = null; 808 if ($this->caching && ($this->nocache || $this->tag_nocache)) { 809 if (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) { 810 $function = 811 $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ]; 812 } elseif (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) { 813 $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ] = 814 $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ]; 815 $function = 816 $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ]; 817 } 818 } else { 819 if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) { 820 $function = 821 $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ]; 822 } elseif (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) { 823 $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ] = 824 $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ]; 825 $function = 826 $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ]; 827 } 828 } 829 if (isset($function)) { 830 if ($plugin_type === 'modifier') { 831 $this->modifier_plugins[ $plugin_name ] = true; 832 } 833 return $function; 834 } 835 // loop through plugin dirs and find the plugin 836 $function = 'smarty_' . $plugin_type . '_' . $plugin_name; 837 $file = $this->smarty->loadPlugin($function, false); 838 if (is_string($file)) { 839 if ($this->caching && ($this->nocache || $this->tag_nocache)) { 840 $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'file' ] = 841 $file; 842 $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ] = 843 $function; 844 } else { 845 $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'file' ] = 846 $file; 847 $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ] = 848 $function; 849 } 850 if ($plugin_type === 'modifier') { 851 $this->modifier_plugins[ $plugin_name ] = true; 852 } 853 return $function; 854 } 855 if (is_callable($function)) { 856 // plugin function is defined in the script 857 return $function; 858 } 859 return false; 860 } 861 862 /** 863 * Check for plugins by default plugin handler 864 * 865 * @param string $tag name of tag 866 * @param string $plugin_type type of plugin 867 * 868 * @return bool true if found 869 * @throws \SmartyCompilerException 870 */ 871 public function getPluginFromDefaultHandler($tag, $plugin_type) 872 { 873 $callback = null; 874 $script = null; 875 $cacheable = true; 876 $result = call_user_func_array( 877 $this->smarty->default_plugin_handler_func, 878 array( 879 $tag, 880 $plugin_type, 881 $this->template, 882 &$callback, 883 &$script, 884 &$cacheable, 885 ) 886 ); 887 if ($result) { 888 $this->tag_nocache = $this->tag_nocache || !$cacheable; 889 if ($script !== null) { 890 if (is_file($script)) { 891 if ($this->caching && ($this->nocache || $this->tag_nocache)) { 892 $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'file' ] = 893 $script; 894 $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'function' ] = 895 $callback; 896 } else { 897 $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'file' ] = 898 $script; 899 $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'function' ] = 900 $callback; 901 } 902 include_once $script; 903 } else { 904 $this->trigger_template_error("Default plugin handler: Returned script file '{$script}' for '{$tag}' not found"); 905 } 906 } 907 if (is_callable($callback)) { 908 $this->default_handler_plugins[ $plugin_type ][ $tag ] = array( 909 $callback, 910 true, 911 array() 912 ); 913 return true; 914 } else { 915 $this->trigger_template_error("Default plugin handler: Returned callback for '{$tag}' not callable"); 916 } 917 } 918 return false; 919 } 920 921 /** 922 * Append code segments and remove unneeded ?> <?php transitions 923 * 924 * @param string $left 925 * @param string $right 926 * 927 * @return string 928 */ 929 public function appendCode($left, $right) 930 { 931 if (preg_match('/\s*\?>\s?$/D', $left) && preg_match('/^<\?php\s+/', $right)) { 932 $left = preg_replace('/\s*\?>\s?$/D', "\n", $left); 933 $left .= preg_replace('/^<\?php\s+/', '', $right); 934 } else { 935 $left .= $right; 936 } 937 return $left; 938 } 939 940 /** 941 * Inject inline code for nocache template sections 942 * This method gets the content of each template element from the parser. 943 * If the content is compiled code and it should be not cached the code is injected 944 * into the rendered output. 945 * 946 * @param string $content content of template element 947 * @param boolean $is_code true if content is compiled code 948 * 949 * @return string content 950 */ 951 public function processNocacheCode($content, $is_code) 952 { 953 // If the template is not evaluated and we have a nocache section and or a nocache tag 954 if ($is_code && !empty($content)) { 955 // generate replacement code 956 if ((!($this->template->source->handler->recompiled) || $this->forceNocache) && $this->caching 957 && !$this->suppressNocacheProcessing && ($this->nocache || $this->tag_nocache) 958 ) { 959 $this->template->compiled->has_nocache_code = true; 960 $_output = addcslashes($content, '\'\\'); 961 $_output = str_replace('^#^', '\'', $_output); 962 $_output = 963 "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/{$_output}/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n"; 964 // make sure we include modifier plugins for nocache code 965 foreach ($this->modifier_plugins as $plugin_name => $dummy) { 966 if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ])) { 967 $this->required_plugins[ 'nocache' ][ $plugin_name ][ 'modifier' ] = 968 $this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ]; 969 } 970 } 971 } else { 972 $_output = $content; 973 } 974 } else { 975 $_output = $content; 976 } 977 $this->modifier_plugins = array(); 978 $this->suppressNocacheProcessing = false; 979 $this->tag_nocache = false; 980 return $_output; 981 } 982 983 /** 984 * Get Id 985 * 986 * @param string $input 987 * 988 * @return bool|string 989 */ 990 public function getId($input) 991 { 992 if (preg_match('~^([\'"]*)([0-9]*[a-zA-Z_]\w*)\1$~', $input, $match)) { 993 return $match[ 2 ]; 994 } 995 return false; 996 } 997 998 /** 999 * Get variable name from string 1000 * 1001 * @param string $input 1002 * 1003 * @return bool|string 1004 */ 1005 public function getVariableName($input) 1006 { 1007 if (preg_match('~^[$]_smarty_tpl->tpl_vars\[[\'"]*([0-9]*[a-zA-Z_]\w*)[\'"]*\]->value$~', $input, $match)) { 1008 return $match[ 1 ]; 1009 } 1010 return false; 1011 } 1012 1013 /** 1014 * Set nocache flag in variable or create new variable 1015 * 1016 * @param string $varName 1017 */ 1018 public function setNocacheInVariable($varName) 1019 { 1020 // create nocache var to make it know for further compiling 1021 if ($_var = $this->getId($varName)) { 1022 if (isset($this->template->tpl_vars[ $_var ])) { 1023 $this->template->tpl_vars[ $_var ] = clone $this->template->tpl_vars[ $_var ]; 1024 $this->template->tpl_vars[ $_var ]->nocache = true; 1025 } else { 1026 $this->template->tpl_vars[ $_var ] = new Smarty_Variable(null, true); 1027 } 1028 } 1029 } 1030 1031 /** 1032 * @param array $_attr tag attributes 1033 * @param array $validScopes 1034 * 1035 * @return int|string 1036 * @throws \SmartyCompilerException 1037 */ 1038 public function convertScope($_attr, $validScopes) 1039 { 1040 $_scope = 0; 1041 if (isset($_attr[ 'scope' ])) { 1042 $_scopeName = trim($_attr[ 'scope' ], '\'"'); 1043 if (is_numeric($_scopeName) && in_array($_scopeName, $validScopes)) { 1044 $_scope = $_scopeName; 1045 } elseif (is_string($_scopeName)) { 1046 $_scopeName = trim($_scopeName, '\'"'); 1047 $_scope = isset($validScopes[ $_scopeName ]) ? $validScopes[ $_scopeName ] : false; 1048 } else { 1049 $_scope = false; 1050 } 1051 if ($_scope === false) { 1052 $err = var_export($_scopeName, true); 1053 $this->trigger_template_error("illegal value '{$err}' for \"scope\" attribute", null, true); 1054 } 1055 } 1056 return $_scope; 1057 } 1058 1059 /** 1060 * Generate nocache code string 1061 * 1062 * @param string $code PHP code 1063 * 1064 * @return string 1065 */ 1066 public function makeNocacheCode($code) 1067 { 1068 return "echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/<?php " . 1069 str_replace('^#^', '\'', addcslashes($code, '\'\\')) . 1070 "?>/*/%%SmartyNocache:{$this->nocache_hash}%%*/';\n"; 1071 } 1072 1073 /** 1074 * display compiler error messages without dying 1075 * If parameter $args is empty it is a parser detected syntax error. 1076 * In this case the parser is called to obtain information about expected tokens. 1077 * If parameter $args contains a string this is used as error message 1078 * 1079 * @param string $args individual error message or null 1080 * @param string $line line-number 1081 * @param null|bool $tagline if true the line number of last tag 1082 * 1083 * @throws \SmartyCompilerException when an unexpected token is found 1084 */ 1085 public function trigger_template_error($args = null, $line = null, $tagline = null) 1086 { 1087 $lex = $this->parser->lex; 1088 if ($tagline === true) { 1089 // get line number of Tag 1090 $line = $lex->taglineno; 1091 } elseif (!isset($line)) { 1092 // get template source line which has error 1093 $line = $lex->line; 1094 } else { 1095 $line = (int)$line; 1096 } 1097 if (in_array( 1098 $this->template->source->type, 1099 array( 1100 'eval', 1101 'string' 1102 ) 1103 ) 1104 ) { 1105 $templateName = $this->template->source->type . ':' . trim( 1106 preg_replace( 1107 '![\t\r\n]+!', 1108 ' ', 1109 strlen($lex->data) > 40 ? 1110 substr($lex->data, 0, 40) . 1111 '...' : $lex->data 1112 ) 1113 ); 1114 } else { 1115 $templateName = $this->template->source->type . ':' . $this->template->source->filepath; 1116 } 1117 // $line += $this->trace_line_offset; 1118 $match = preg_split("/\n/", $lex->data); 1119 $error_text = 1120 'Syntax error in template "' . (empty($this->trace_filepath) ? $templateName : $this->trace_filepath) . 1121 '" on line ' . ($line + $this->trace_line_offset) . ' "' . 1122 trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ])) . '" '; 1123 if (isset($args)) { 1124 // individual error message 1125 $error_text .= $args; 1126 } else { 1127 $expect = array(); 1128 // expected token from parser 1129 $error_text .= ' - Unexpected "' . $lex->value . '"'; 1130 if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) { 1131 foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) { 1132 $exp_token = $this->parser->yyTokenName[ $token ]; 1133 if (isset($lex->smarty_token_names[ $exp_token ])) { 1134 // token type from lexer 1135 $expect[] = '"' . $lex->smarty_token_names[ $exp_token ] . '"'; 1136 } else { 1137 // otherwise internal token name 1138 $expect[] = $this->parser->yyTokenName[ $token ]; 1139 } 1140 } 1141 $error_text .= ', expected one of: ' . implode(' , ', $expect); 1142 } 1143 } 1144 if ($this->smarty->_parserdebug) { 1145 $this->parser->errorRunDown(); 1146 echo ob_get_clean(); 1147 flush(); 1148 } 1149 $e = new SmartyCompilerException($error_text); 1150 $e->line = $line; 1151 $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ])); 1152 $e->desc = $args; 1153 $e->template = $this->template->source->filepath; 1154 throw $e; 1155 } 1156 1157 /** 1158 * Return var_export() value with all white spaces removed 1159 * 1160 * @param mixed $value 1161 * 1162 * @return string 1163 */ 1164 public function getVarExport($value) 1165 { 1166 return preg_replace('/\s/', '', var_export($value, true)); 1167 } 1168 1169 /** 1170 * enter double quoted string 1171 * - save tag stack count 1172 */ 1173 public function enterDoubleQuote() 1174 { 1175 array_push($this->_tag_stack_count, $this->getTagStackCount()); 1176 } 1177 1178 /** 1179 * Return tag stack count 1180 * 1181 * @return int 1182 */ 1183 public function getTagStackCount() 1184 { 1185 return count($this->_tag_stack); 1186 } 1187 1188 /** 1189 * @param $lexerPreg 1190 * 1191 * @return mixed 1192 */ 1193 public function replaceDelimiter($lexerPreg) 1194 { 1195 return str_replace( 1196 array('SMARTYldel', 'SMARTYliteral', 'SMARTYrdel', 'SMARTYautoliteral', 'SMARTYal'), 1197 array( 1198 $this->ldelPreg, $this->literalPreg, $this->rdelPreg, 1199 $this->smarty->getAutoLiteral() ? '{1,}' : '{9}', 1200 $this->smarty->getAutoLiteral() ? '' : '\\s*' 1201 ), 1202 $lexerPreg 1203 ); 1204 } 1205 1206 /** 1207 * Build lexer regular expressions for left and right delimiter and user defined literals 1208 */ 1209 public function initDelimiterPreg() 1210 { 1211 $ldel = $this->smarty->getLeftDelimiter(); 1212 $this->ldelLength = strlen($ldel); 1213 $this->ldelPreg = ''; 1214 foreach (str_split($ldel, 1) as $chr) { 1215 $this->ldelPreg .= '[' . preg_quote($chr,'/') . ']'; 1216 } 1217 $rdel = $this->smarty->getRightDelimiter(); 1218 $this->rdelLength = strlen($rdel); 1219 $this->rdelPreg = ''; 1220 foreach (str_split($rdel, 1) as $chr) { 1221 $this->rdelPreg .= '[' . preg_quote($chr,'/') . ']'; 1222 } 1223 $literals = $this->smarty->getLiterals(); 1224 if (!empty($literals)) { 1225 foreach ($literals as $key => $literal) { 1226 $literalPreg = ''; 1227 foreach (str_split($literal, 1) as $chr) { 1228 $literalPreg .= '[' . preg_quote($chr,'/') . ']'; 1229 } 1230 $literals[ $key ] = $literalPreg; 1231 } 1232 $this->literalPreg = '|' . implode('|', $literals); 1233 } else { 1234 $this->literalPreg = ''; 1235 } 1236 } 1237 1238 /** 1239 * leave double quoted string 1240 * - throw exception if block in string was not closed 1241 * 1242 * @throws \SmartyCompilerException 1243 */ 1244 public function leaveDoubleQuote() 1245 { 1246 if (array_pop($this->_tag_stack_count) !== $this->getTagStackCount()) { 1247 $tag = $this->getOpenBlockTag(); 1248 $this->trigger_template_error( 1249 "unclosed '{{$tag}}' in doubled quoted string", 1250 null, 1251 true 1252 ); 1253 } 1254 } 1255 1256 /** 1257 * Get left delimiter preg 1258 * 1259 * @return string 1260 */ 1261 public function getLdelPreg() 1262 { 1263 return $this->ldelPreg; 1264 } 1265 1266 /** 1267 * Get right delimiter preg 1268 * 1269 * @return string 1270 */ 1271 public function getRdelPreg() 1272 { 1273 return $this->rdelPreg; 1274 } 1275 1276 /** 1277 * Get length of left delimiter 1278 * 1279 * @return int 1280 */ 1281 public function getLdelLength() 1282 { 1283 return $this->ldelLength; 1284 } 1285 1286 /** 1287 * Get length of right delimiter 1288 * 1289 * @return int 1290 */ 1291 public function getRdelLength() 1292 { 1293 return $this->rdelLength; 1294 } 1295 1296 /** 1297 * Get name of current open block tag 1298 * 1299 * @return string|boolean 1300 */ 1301 public function getOpenBlockTag() 1302 { 1303 $tagCount = $this->getTagStackCount(); 1304 if ($tagCount) { 1305 return $this->_tag_stack[ $tagCount - 1 ][ 0 ]; 1306 } else { 1307 return false; 1308 } 1309 } 1310 1311 /** 1312 * Check if $value contains variable elements 1313 * 1314 * @param mixed $value 1315 * 1316 * @return bool|int 1317 */ 1318 public function isVariable($value) 1319 { 1320 if (is_string($value)) { 1321 return preg_match('/[$(]/', $value); 1322 } 1323 if (is_bool($value) || is_numeric($value)) { 1324 return false; 1325 } 1326 if (is_array($value)) { 1327 foreach ($value as $k => $v) { 1328 if ($this->isVariable($k) || $this->isVariable($v)) { 1329 return true; 1330 } 1331 } 1332 return false; 1333 } 1334 return false; 1335 } 1336 1337 /** 1338 * Get new prefix variable name 1339 * 1340 * @return string 1341 */ 1342 public function getNewPrefixVariable() 1343 { 1344 ++self::$prefixVariableNumber; 1345 return $this->getPrefixVariable(); 1346 } 1347 1348 /** 1349 * Get current prefix variable name 1350 * 1351 * @return string 1352 */ 1353 public function getPrefixVariable() 1354 { 1355 return '$_prefixVariable' . self::$prefixVariableNumber; 1356 } 1357 1358 /** 1359 * append code to prefix buffer 1360 * 1361 * @param string $code 1362 */ 1363 public function appendPrefixCode($code) 1364 { 1365 $this->prefix_code[] = $code; 1366 } 1367 1368 /** 1369 * get prefix code string 1370 * 1371 * @return string 1372 */ 1373 public function getPrefixCode() 1374 { 1375 $code = ''; 1376 $prefixArray = array_merge($this->prefix_code, array_pop($this->prefixCodeStack)); 1377 $this->prefixCodeStack[] = array(); 1378 foreach ($prefixArray as $c) { 1379 $code = $this->appendCode($code, $c); 1380 } 1381 $this->prefix_code = array(); 1382 return $code; 1383 } 1384 1385 /** 1386 * Save current required plugins 1387 * 1388 * @param bool $init if true init required plugins 1389 */ 1390 public function saveRequiredPlugins($init = false) 1391 { 1392 $this->required_plugins_stack[] = $this->required_plugins; 1393 if ($init) { 1394 $this->required_plugins = array('compiled' => array(), 'nocache' => array()); 1395 } 1396 } 1397 1398 /** 1399 * Restore required plugins 1400 */ 1401 public function restoreRequiredPlugins() 1402 { 1403 $this->required_plugins = array_pop($this->required_plugins_stack); 1404 } 1405 1406 /** 1407 * Compile code to call Smarty_Internal_Template::_checkPlugins() 1408 * for required plugins 1409 * 1410 * @return string 1411 */ 1412 public function compileRequiredPlugins() 1413 { 1414 $code = $this->compileCheckPlugins($this->required_plugins[ 'compiled' ]); 1415 if ($this->caching && !empty($this->required_plugins[ 'nocache' ])) { 1416 $code .= $this->makeNocacheCode($this->compileCheckPlugins($this->required_plugins[ 'nocache' ])); 1417 } 1418 return $code; 1419 } 1420 1421 /** 1422 * Compile code to call Smarty_Internal_Template::_checkPlugins 1423 * - checks if plugin is callable require otherwise 1424 * 1425 * @param $requiredPlugins 1426 * 1427 * @return string 1428 */ 1429 public function compileCheckPlugins($requiredPlugins) 1430 { 1431 if (!empty($requiredPlugins)) { 1432 $plugins = array(); 1433 foreach ($requiredPlugins as $plugin) { 1434 foreach ($plugin as $data) { 1435 $plugins[] = $data; 1436 } 1437 } 1438 return '$_smarty_tpl->_checkPlugins(' . $this->getVarExport($plugins) . ');' . "\n"; 1439 } else { 1440 return ''; 1441 } 1442 } 1443 1444 /** 1445 * method to compile a Smarty template 1446 * 1447 * @param mixed $_content template source 1448 * @param bool $isTemplateSource 1449 * 1450 * @return bool true if compiling succeeded, false if it failed 1451 */ 1452 abstract protected function doCompile($_content, $isTemplateSource = false); 1453 1454 /** 1455 * Compile Tag 1456 * 1457 * @param string $tag tag name 1458 * @param array $args array with tag attributes 1459 * @param array $parameter array with compilation parameter 1460 * 1461 * @throws SmartyCompilerException 1462 * @throws SmartyException 1463 * @return string compiled code 1464 */ 1465 private function compileTag2($tag, $args, $parameter) 1466 { 1467 $plugin_type = ''; 1468 // $args contains the attributes parsed and compiled by the lexer/parser 1469 // assume that tag does compile into code, but creates no HTML output 1470 $this->has_code = true; 1471 // log tag/attributes 1472 if (isset($this->smarty->_cache[ 'get_used_tags' ])) { 1473 $this->template->_cache[ 'used_tags' ][] = array( 1474 $tag, 1475 $args 1476 ); 1477 } 1478 // check nocache option flag 1479 foreach ($args as $arg) { 1480 if (!is_array($arg)) { 1481 if ($arg === "'nocache'" || $arg === 'nocache') { 1482 $this->tag_nocache = true; 1483 } 1484 } else { 1485 foreach ($arg as $k => $v) { 1486 if (($k === "'nocache'" || $k === 'nocache') && (trim($v, "'\" ") === 'true')) { 1487 $this->tag_nocache = true; 1488 } 1489 } 1490 } 1491 } 1492 // compile the smarty tag (required compile classes to compile the tag are auto loaded) 1493 if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) { 1494 if (isset($this->parent_compiler->tpl_function[ $tag ]) 1495 || (isset($this->template->smarty->ext->_tplFunction) 1496 && $this->template->smarty->ext->_tplFunction->getTplFunction($this->template, $tag) !== false) 1497 ) { 1498 // template defined by {template} tag 1499 $args[ '_attr' ][ 'name' ] = "'{$tag}'"; 1500 $_output = $this->callTagCompiler('call', $args, $parameter); 1501 } 1502 } 1503 if ($_output !== false) { 1504 if ($_output !== true) { 1505 // did we get compiled code 1506 if ($this->has_code) { 1507 // return compiled code 1508 return $_output; 1509 } 1510 } 1511 // tag did not produce compiled code 1512 return null; 1513 } else { 1514 // map_named attributes 1515 if (isset($args[ '_attr' ])) { 1516 foreach ($args[ '_attr' ] as $key => $attribute) { 1517 if (is_array($attribute)) { 1518 $args = array_merge($args, $attribute); 1519 } 1520 } 1521 } 1522 // not an internal compiler tag 1523 if (strlen($tag) < 6 || substr($tag, -5) !== 'close') { 1524 // check if tag is a registered object 1525 if (isset($this->smarty->registered_objects[ $tag ]) && isset($parameter[ 'object_method' ])) { 1526 $method = $parameter[ 'object_method' ]; 1527 if (!in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ]) 1528 && (empty($this->smarty->registered_objects[ $tag ][ 1 ]) 1529 || in_array($method, $this->smarty->registered_objects[ $tag ][ 1 ])) 1530 ) { 1531 return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method); 1532 } elseif (in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])) { 1533 return $this->callTagCompiler( 1534 'private_object_block_function', 1535 $args, 1536 $parameter, 1537 $tag, 1538 $method 1539 ); 1540 } else { 1541 // throw exception 1542 $this->trigger_template_error( 1543 'not allowed method "' . $method . '" in registered object "' . 1544 $tag . '"', 1545 null, 1546 true 1547 ); 1548 } 1549 } 1550 // check if tag is registered 1551 foreach (array( 1552 Smarty::PLUGIN_COMPILER, 1553 Smarty::PLUGIN_FUNCTION, 1554 Smarty::PLUGIN_BLOCK, 1555 ) as $plugin_type) { 1556 if (isset($this->smarty->registered_plugins[ $plugin_type ][ $tag ])) { 1557 // if compiler function plugin call it now 1558 if ($plugin_type === Smarty::PLUGIN_COMPILER) { 1559 $new_args = array(); 1560 foreach ($args as $key => $mixed) { 1561 if (is_array($mixed)) { 1562 $new_args = array_merge($new_args, $mixed); 1563 } else { 1564 $new_args[ $key ] = $mixed; 1565 } 1566 } 1567 if (!$this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 1 ]) { 1568 $this->tag_nocache = true; 1569 } 1570 return call_user_func_array( 1571 $this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 0 ], 1572 array( 1573 $new_args, 1574 $this 1575 ) 1576 ); 1577 } 1578 // compile registered function or block function 1579 if ($plugin_type === Smarty::PLUGIN_FUNCTION || $plugin_type === Smarty::PLUGIN_BLOCK) { 1580 return $this->callTagCompiler( 1581 'private_registered_' . $plugin_type, 1582 $args, 1583 $parameter, 1584 $tag 1585 ); 1586 } 1587 } 1588 } 1589 // check plugins from plugins folder 1590 foreach ($this->plugin_search_order as $plugin_type) { 1591 if ($plugin_type === Smarty::PLUGIN_COMPILER 1592 && $this->smarty->loadPlugin('smarty_compiler_' . $tag) 1593 && (!isset($this->smarty->security_policy) 1594 || $this->smarty->security_policy->isTrustedTag($tag, $this)) 1595 ) { 1596 $plugin = 'smarty_compiler_' . $tag; 1597 if (is_callable($plugin)) { 1598 // convert arguments format for old compiler plugins 1599 $new_args = array(); 1600 foreach ($args as $key => $mixed) { 1601 if (is_array($mixed)) { 1602 $new_args = array_merge($new_args, $mixed); 1603 } else { 1604 $new_args[ $key ] = $mixed; 1605 } 1606 } 1607 return $plugin($new_args, $this->smarty); 1608 } 1609 if (class_exists($plugin, false)) { 1610 $plugin_object = new $plugin; 1611 if (method_exists($plugin_object, 'compile')) { 1612 return $plugin_object->compile($args, $this); 1613 } 1614 } 1615 throw new SmartyException("Plugin '{$tag}' not callable"); 1616 } else { 1617 if ($function = $this->getPlugin($tag, $plugin_type)) { 1618 if (!isset($this->smarty->security_policy) 1619 || $this->smarty->security_policy->isTrustedTag($tag, $this) 1620 ) { 1621 return $this->callTagCompiler( 1622 'private_' . $plugin_type . '_plugin', 1623 $args, 1624 $parameter, 1625 $tag, 1626 $function 1627 ); 1628 } 1629 } 1630 } 1631 } 1632 if (is_callable($this->smarty->default_plugin_handler_func)) { 1633 $found = false; 1634 // look for already resolved tags 1635 foreach ($this->plugin_search_order as $plugin_type) { 1636 if (isset($this->default_handler_plugins[ $plugin_type ][ $tag ])) { 1637 $found = true; 1638 break; 1639 } 1640 } 1641 if (!$found) { 1642 // call default handler 1643 foreach ($this->plugin_search_order as $plugin_type) { 1644 if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) { 1645 $found = true; 1646 break; 1647 } 1648 } 1649 } 1650 if ($found) { 1651 // if compiler function plugin call it now 1652 if ($plugin_type === Smarty::PLUGIN_COMPILER) { 1653 $new_args = array(); 1654 foreach ($args as $key => $mixed) { 1655 if (is_array($mixed)) { 1656 $new_args = array_merge($new_args, $mixed); 1657 } else { 1658 $new_args[ $key ] = $mixed; 1659 } 1660 } 1661 return call_user_func_array( 1662 $this->default_handler_plugins[ $plugin_type ][ $tag ][ 0 ], 1663 array( 1664 $new_args, 1665 $this 1666 ) 1667 ); 1668 } else { 1669 return $this->callTagCompiler( 1670 'private_registered_' . $plugin_type, 1671 $args, 1672 $parameter, 1673 $tag 1674 ); 1675 } 1676 } 1677 } 1678 } else { 1679 // compile closing tag of block function 1680 $base_tag = substr($tag, 0, -5); 1681 // check if closing tag is a registered object 1682 if (isset($this->smarty->registered_objects[ $base_tag ]) && isset($parameter[ 'object_method' ])) { 1683 $method = $parameter[ 'object_method' ]; 1684 if (in_array($method, $this->smarty->registered_objects[ $base_tag ][ 3 ])) { 1685 return $this->callTagCompiler( 1686 'private_object_block_function', 1687 $args, 1688 $parameter, 1689 $tag, 1690 $method 1691 ); 1692 } else { 1693 // throw exception 1694 $this->trigger_template_error( 1695 'not allowed closing tag method "' . $method . 1696 '" in registered object "' . $base_tag . '"', 1697 null, 1698 true 1699 ); 1700 } 1701 } 1702 // registered block tag ? 1703 if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ]) 1704 || isset($this->default_handler_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ]) 1705 ) { 1706 return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag); 1707 } 1708 // registered function tag ? 1709 if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_FUNCTION ][ $tag ])) { 1710 return $this->callTagCompiler('private_registered_function', $args, $parameter, $tag); 1711 } 1712 // block plugin? 1713 if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) { 1714 return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function); 1715 } 1716 // function plugin? 1717 if ($function = $this->getPlugin($tag, Smarty::PLUGIN_FUNCTION)) { 1718 if (!isset($this->smarty->security_policy) 1719 || $this->smarty->security_policy->isTrustedTag($tag, $this) 1720 ) { 1721 return $this->callTagCompiler('private_function_plugin', $args, $parameter, $tag, $function); 1722 } 1723 } 1724 // registered compiler plugin ? 1725 if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ])) { 1726 // if compiler function plugin call it now 1727 $args = array(); 1728 if (!$this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 1 ]) { 1729 $this->tag_nocache = true; 1730 } 1731 return call_user_func_array( 1732 $this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 0 ], 1733 array( 1734 $args, 1735 $this 1736 ) 1737 ); 1738 } 1739 if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) { 1740 $plugin = 'smarty_compiler_' . $tag; 1741 if (is_callable($plugin)) { 1742 return $plugin($args, $this->smarty); 1743 } 1744 if (class_exists($plugin, false)) { 1745 $plugin_object = new $plugin; 1746 if (method_exists($plugin_object, 'compile')) { 1747 return $plugin_object->compile($args, $this); 1748 } 1749 } 1750 throw new SmartyException("Plugin '{$tag}' not callable"); 1751 } 1752 } 1753 $this->trigger_template_error("unknown tag '{$tag}'", null, true); 1754 } 1755 } 1756} 1757