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