PageRenderTime 65ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/core/Associates/Smarty/sysplugins/smarty_internal_templatecompilerbase.php

https://gitlab.com/fiesta-framework/Documentation
PHP | 823 lines | 475 code | 47 blank | 301 comment | 162 complexity | b9e4054fdd05746b99c470bce1cd88f6 MD5 | raw file
  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. * Main abstract compiler class
  12. *
  13. * @package Smarty
  14. * @subpackage Compiler
  15. */
  16. abstract class Smarty_Internal_TemplateCompilerBase
  17. {
  18. /**
  19. * hash for nocache sections
  20. *
  21. * @var mixed
  22. */
  23. private $nocache_hash = null;
  24. /**
  25. * suppress generation of nocache code
  26. *
  27. * @var bool
  28. */
  29. public $suppressNocacheProcessing = false;
  30. /**
  31. * suppress generation of merged template code
  32. *
  33. * @var bool
  34. */
  35. public $suppressMergedTemplates = false;
  36. /**
  37. * compile tag objects
  38. *
  39. * @var array
  40. */
  41. public static $_tag_objects = array();
  42. /**
  43. * tag stack
  44. *
  45. * @var array
  46. */
  47. public $_tag_stack = array();
  48. /**
  49. * current template
  50. *
  51. * @var Smarty_Internal_Template
  52. */
  53. public $template = null;
  54. /**
  55. * merged templates
  56. *
  57. * @var array
  58. */
  59. public $merged_templates = array();
  60. /**
  61. * sources which must be compiled
  62. *
  63. * @var array
  64. */
  65. public $sources = array();
  66. /**
  67. * flag that we are inside {block}
  68. *
  69. * @var bool
  70. */
  71. public $inheritance = false;
  72. /**
  73. * flag when compiling inheritance child template
  74. *
  75. * @var bool
  76. */
  77. public $inheritance_child = false;
  78. /**
  79. * uid of templates called by {extends} for recursion check
  80. *
  81. * @var array
  82. */
  83. public $extends_uid = array();
  84. /**
  85. * source line offset for error messages
  86. *
  87. * @var int
  88. */
  89. public $trace_line_offset = 0;
  90. /**
  91. * trace uid
  92. *
  93. * @var string
  94. */
  95. public $trace_uid = '';
  96. /**
  97. * trace file path
  98. *
  99. * @var string
  100. */
  101. public $trace_filepath = '';
  102. /**
  103. * stack for tracing file and line of nested {block} tags
  104. *
  105. * @var array
  106. */
  107. public $trace_stack = array();
  108. /**
  109. * plugins loaded by default plugin handler
  110. *
  111. * @var array
  112. */
  113. public $default_handler_plugins = array();
  114. /**
  115. * saved preprocessed modifier list
  116. *
  117. * @var mixed
  118. */
  119. public $default_modifier_list = null;
  120. /**
  121. * force compilation of complete template as nocache
  122. *
  123. * @var boolean
  124. */
  125. public $forceNocache = false;
  126. /**
  127. * suppress Smarty header code in compiled template
  128. *
  129. * @var bool
  130. */
  131. public $suppressHeader = false;
  132. /**
  133. * suppress template property header code in compiled template
  134. *
  135. * @var bool
  136. */
  137. public $suppressTemplatePropertyHeader = false;
  138. /**
  139. * suppress pre and post filter
  140. *
  141. * @var bool
  142. */
  143. public $suppressFilter = false;
  144. /**
  145. * flag if compiled template file shall we written
  146. *
  147. * @var bool
  148. */
  149. public $write_compiled_code = true;
  150. /**
  151. * flag if currently a template function is compiled
  152. *
  153. * @var bool
  154. */
  155. public $compiles_template_function = false;
  156. /**
  157. * called subfuntions from template function
  158. *
  159. * @var array
  160. */
  161. public $called_functions = array();
  162. /**
  163. * flags for used modifier plugins
  164. *
  165. * @var array
  166. */
  167. public $modifier_plugins = array();
  168. /**
  169. * type of already compiled modifier
  170. *
  171. * @var array
  172. */
  173. public $known_modifier_type = array();
  174. /**
  175. * method to compile a Smarty template
  176. *
  177. * @param mixed $_content template source
  178. *
  179. * @return bool true if compiling succeeded, false if it failed
  180. */
  181. abstract protected function doCompile($_content);
  182. /**
  183. * Initialize compiler
  184. */
  185. public function __construct()
  186. {
  187. $this->nocache_hash = str_replace(array('.', ','), '-', uniqid(rand(), true));
  188. }
  189. /**
  190. * Method to compile a Smarty template
  191. *
  192. * @param Smarty_Internal_Template $template template object to compile
  193. * @param bool $nocache true is shall be compiled in nocache mode
  194. *
  195. * @return bool true if compiling succeeded, false if it failed
  196. */
  197. public function compileTemplate(Smarty_Internal_Template $template, $nocache = false)
  198. {
  199. if (empty($template->properties['nocache_hash'])) {
  200. $template->properties['nocache_hash'] = $this->nocache_hash;
  201. } else {
  202. $this->nocache_hash = $template->properties['nocache_hash'];
  203. }
  204. // flag for nochache sections
  205. $this->nocache = $nocache;
  206. $this->tag_nocache = false;
  207. // save template object in compiler class
  208. $this->template = $template;
  209. // reset has nocache code flag
  210. $this->template->has_nocache_code = false;
  211. $save_source = $this->template->source;
  212. // template header code
  213. $template_header = '';
  214. if (!$this->suppressHeader) {
  215. $template_header .= "<?php /* Smarty version " . Smarty::SMARTY_VERSION . ", created on " . strftime("%Y-%m-%d %H:%M:%S") . "\n";
  216. $template_header .= " compiled from \"" . $this->template->source->filepath . "\" */ ?>\n";
  217. }
  218. if (empty($this->template->source->components)) {
  219. $this->sources = array($template->source);
  220. } else {
  221. // we have array of inheritance templates by extends: resource
  222. $this->sources = array_reverse($template->source->components);
  223. }
  224. $loop = 0;
  225. // the $this->sources array can get additional elements while compiling by the {extends} tag
  226. while ($this->template->source = array_shift($this->sources)) {
  227. $this->smarty->_current_file = $this->template->source->filepath;
  228. if ($this->smarty->debugging) {
  229. Smarty_Internal_Debug::start_compile($this->template);
  230. }
  231. $no_sources = count($this->sources);
  232. if ($loop || $no_sources) {
  233. $this->template->properties['file_dependency'][$this->template->source->uid] = array($this->template->source->filepath, $this->template->source->timestamp, $this->template->source->type);
  234. }
  235. $loop ++;
  236. if ($no_sources) {
  237. $this->inheritance_child = true;
  238. } else {
  239. $this->inheritance_child = false;
  240. }
  241. do {
  242. $_compiled_code = '';
  243. // flag for aborting current and start recompile
  244. $this->abort_and_recompile = false;
  245. // get template source
  246. $_content = $this->template->source->content;
  247. if ($_content != '') {
  248. // run prefilter if required
  249. if ((isset($this->smarty->autoload_filters['pre']) || isset($this->smarty->registered_filters['pre'])) && !$this->suppressFilter) {
  250. $_content = Smarty_Internal_Filter_Handler::runFilter('pre', $_content, $template);
  251. }
  252. // call compiler
  253. $_compiled_code = $this->doCompile($_content);
  254. }
  255. } while ($this->abort_and_recompile);
  256. if ($this->smarty->debugging) {
  257. Smarty_Internal_Debug::end_compile($this->template);
  258. }
  259. }
  260. // restore source
  261. $this->template->source = $save_source;
  262. unset($save_source);
  263. $this->smarty->_current_file = $this->template->source->filepath;
  264. // free memory
  265. unset($this->parser->root_buffer, $this->parser->current_buffer, $this->parser, $this->lex, $this->template);
  266. self::$_tag_objects = array();
  267. // return compiled code to template object
  268. $merged_code = '';
  269. if (!$this->suppressMergedTemplates && !empty($this->merged_templates)) {
  270. foreach ($this->merged_templates as $code) {
  271. $merged_code .= $code;
  272. }
  273. }
  274. // run postfilter if required on compiled template code
  275. if ((isset($this->smarty->autoload_filters['post']) || isset($this->smarty->registered_filters['post'])) && !$this->suppressFilter && $_compiled_code != '') {
  276. $_compiled_code = Smarty_Internal_Filter_Handler::runFilter('post', $_compiled_code, $template);
  277. }
  278. if ($this->suppressTemplatePropertyHeader) {
  279. $code = $_compiled_code . $merged_code;
  280. } else {
  281. $code = $template_header . $template->createTemplateCodeFrame($_compiled_code) . $merged_code;
  282. }
  283. // unset content because template inheritance could have replace source with parent code
  284. unset ($template->source->content);
  285. return $code;
  286. }
  287. /**
  288. * Compile Tag
  289. * This is a call back from the lexer/parser
  290. * It executes the required compile plugin for the Smarty tag
  291. *
  292. * @param string $tag tag name
  293. * @param array $args array with tag attributes
  294. * @param array $parameter array with compilation parameter
  295. *
  296. * @throws SmartyCompilerException
  297. * @throws SmartyException
  298. * @return string compiled code
  299. */
  300. public function compileTag($tag, $args, $parameter = array())
  301. {
  302. // $args contains the attributes parsed and compiled by the lexer/parser
  303. // assume that tag does compile into code, but creates no HTML output
  304. $this->has_code = true;
  305. $this->has_output = false;
  306. // log tag/attributes
  307. if (isset($this->smarty->get_used_tags) && $this->smarty->get_used_tags) {
  308. $this->template->used_tags[] = array($tag, $args);
  309. }
  310. // check nocache option flag
  311. if (in_array("'nocache'", $args) || in_array(array('nocache' => 'true'), $args)
  312. || in_array(array('nocache' => '"true"'), $args) || in_array(array('nocache' => "'true'"), $args)
  313. ) {
  314. $this->tag_nocache = true;
  315. }
  316. // compile the smarty tag (required compile classes to compile the tag are autoloaded)
  317. if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) {
  318. if (isset($this->smarty->template_functions[$tag])) {
  319. // template defined by {template} tag
  320. $args['_attr']['name'] = "'" . $tag . "'";
  321. $_output = $this->callTagCompiler('call', $args, $parameter);
  322. }
  323. }
  324. if ($_output !== false) {
  325. if ($_output !== true) {
  326. // did we get compiled code
  327. if ($this->has_code) {
  328. // Does it create output?
  329. if ($this->has_output) {
  330. $_output .= "\n";
  331. }
  332. // return compiled code
  333. return $_output;
  334. }
  335. }
  336. // tag did not produce compiled code
  337. return null;
  338. } else {
  339. // map_named attributes
  340. if (isset($args['_attr'])) {
  341. foreach ($args['_attr'] as $key => $attribute) {
  342. if (is_array($attribute)) {
  343. $args = array_merge($args, $attribute);
  344. }
  345. }
  346. }
  347. // not an internal compiler tag
  348. if (strlen($tag) < 6 || substr($tag, - 5) != 'close') {
  349. // check if tag is a registered object
  350. if (isset($this->smarty->registered_objects[$tag]) && isset($parameter['object_method'])) {
  351. $method = $parameter['object_method'];
  352. if (!in_array($method, $this->smarty->registered_objects[$tag][3]) &&
  353. (empty($this->smarty->registered_objects[$tag][1]) || in_array($method, $this->smarty->registered_objects[$tag][1]))
  354. ) {
  355. return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method);
  356. } elseif (in_array($method, $this->smarty->registered_objects[$tag][3])) {
  357. return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag, $method);
  358. } else {
  359. // throw exception
  360. $this->trigger_template_error('not allowed method "' . $method . '" in registered object "' . $tag . '"', $this->lex->taglineno);
  361. }
  362. }
  363. // check if tag is registered
  364. foreach (array(Smarty::PLUGIN_COMPILER, Smarty::PLUGIN_FUNCTION, Smarty::PLUGIN_BLOCK) as $plugin_type) {
  365. if (isset($this->smarty->registered_plugins[$plugin_type][$tag])) {
  366. // if compiler function plugin call it now
  367. if ($plugin_type == Smarty::PLUGIN_COMPILER) {
  368. $new_args = array();
  369. foreach ($args as $key => $mixed) {
  370. if (is_array($mixed)) {
  371. $new_args = array_merge($new_args, $mixed);
  372. } else {
  373. $new_args[$key] = $mixed;
  374. }
  375. }
  376. if (!$this->smarty->registered_plugins[$plugin_type][$tag][1]) {
  377. $this->tag_nocache = true;
  378. }
  379. $function = $this->smarty->registered_plugins[$plugin_type][$tag][0];
  380. if (!is_array($function)) {
  381. return $function($new_args, $this);
  382. } elseif (is_object($function[0])) {
  383. return $this->smarty->registered_plugins[$plugin_type][$tag][0][0]->$function[1]($new_args, $this);
  384. } else {
  385. return call_user_func_array($function, array($new_args, $this));
  386. }
  387. }
  388. // compile registered function or block function
  389. if ($plugin_type == Smarty::PLUGIN_FUNCTION || $plugin_type == Smarty::PLUGIN_BLOCK) {
  390. return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter, $tag);
  391. }
  392. }
  393. }
  394. // check plugins from plugins folder
  395. foreach ($this->smarty->plugin_search_order as $plugin_type) {
  396. if ($plugin_type == Smarty::PLUGIN_COMPILER && $this->smarty->loadPlugin('smarty_compiler_' . $tag) && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))) {
  397. $plugin = 'smarty_compiler_' . $tag;
  398. if (is_callable($plugin)) {
  399. // convert arguments format for old compiler plugins
  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. return $plugin($new_args, $this->smarty);
  409. }
  410. if (class_exists($plugin, false)) {
  411. $plugin_object = new $plugin;
  412. if (method_exists($plugin_object, 'compile')) {
  413. return $plugin_object->compile($args, $this);
  414. }
  415. }
  416. throw new SmartyException("Plugin \"{$tag}\" not callable");
  417. } else {
  418. if ($function = $this->getPlugin($tag, $plugin_type)) {
  419. if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) {
  420. return $this->callTagCompiler('private_' . $plugin_type . '_plugin', $args, $parameter, $tag, $function);
  421. }
  422. }
  423. }
  424. }
  425. if (is_callable($this->smarty->default_plugin_handler_func)) {
  426. $found = false;
  427. // look for already resolved tags
  428. foreach ($this->smarty->plugin_search_order as $plugin_type) {
  429. if (isset($this->default_handler_plugins[$plugin_type][$tag])) {
  430. $found = true;
  431. break;
  432. }
  433. }
  434. if (!$found) {
  435. // call default handler
  436. foreach ($this->smarty->plugin_search_order as $plugin_type) {
  437. if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) {
  438. $found = true;
  439. break;
  440. }
  441. }
  442. }
  443. if ($found) {
  444. // if compiler function plugin call it now
  445. if ($plugin_type == Smarty::PLUGIN_COMPILER) {
  446. $new_args = array();
  447. foreach ($args as $mixed) {
  448. $new_args = array_merge($new_args, $mixed);
  449. }
  450. $function = $this->default_handler_plugins[$plugin_type][$tag][0];
  451. if (!is_array($function)) {
  452. return $function($new_args, $this);
  453. } elseif (is_object($function[0])) {
  454. return $this->default_handler_plugins[$plugin_type][$tag][0][0]->$function[1]($new_args, $this);
  455. } else {
  456. return call_user_func_array($function, array($new_args, $this));
  457. }
  458. } else {
  459. return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter, $tag);
  460. }
  461. }
  462. }
  463. } else {
  464. // compile closing tag of block function
  465. $base_tag = substr($tag, 0, - 5);
  466. // check if closing tag is a registered object
  467. if (isset($this->smarty->registered_objects[$base_tag]) && isset($parameter['object_method'])) {
  468. $method = $parameter['object_method'];
  469. if (in_array($method, $this->smarty->registered_objects[$base_tag][3])) {
  470. return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag, $method);
  471. } else {
  472. // throw exception
  473. $this->trigger_template_error('not allowed closing tag method "' . $method . '" in registered object "' . $base_tag . '"', $this->lex->taglineno);
  474. }
  475. }
  476. // registered block tag ?
  477. if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_BLOCK][$base_tag]) || isset($this->default_handler_plugins[Smarty::PLUGIN_BLOCK][$base_tag])) {
  478. return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag);
  479. }
  480. // block plugin?
  481. if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) {
  482. return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function);
  483. }
  484. // registered compiler plugin ?
  485. if (isset($this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag])) {
  486. // if compiler function plugin call it now
  487. $args = array();
  488. if (!$this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][1]) {
  489. $this->tag_nocache = true;
  490. }
  491. $function = $this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][0];
  492. if (!is_array($function)) {
  493. return $function($args, $this);
  494. } elseif (is_object($function[0])) {
  495. return $this->smarty->registered_plugins[Smarty::PLUGIN_COMPILER][$tag][0][0]->$function[1]($args, $this);
  496. } else {
  497. return call_user_func_array($function, array($args, $this));
  498. }
  499. }
  500. if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {
  501. $plugin = 'smarty_compiler_' . $tag;
  502. if (is_callable($plugin)) {
  503. return $plugin($args, $this->smarty);
  504. }
  505. if (class_exists($plugin, false)) {
  506. $plugin_object = new $plugin;
  507. if (method_exists($plugin_object, 'compile')) {
  508. return $plugin_object->compile($args, $this);
  509. }
  510. }
  511. throw new SmartyException("Plugin \"{$tag}\" not callable");
  512. }
  513. }
  514. $this->trigger_template_error("unknown tag \"" . $tag . "\"", $this->lex->taglineno);
  515. }
  516. }
  517. /**
  518. * lazy loads internal compile plugin for tag and calls the compile method
  519. * compile objects cached for reuse.
  520. * class name format: Smarty_Internal_Compile_TagName
  521. * plugin filename format: Smarty_Internal_Tagname.php
  522. *
  523. * @param string $tag tag name
  524. * @param array $args list of tag attributes
  525. * @param mixed $param1 optional parameter
  526. * @param mixed $param2 optional parameter
  527. * @param mixed $param3 optional parameter
  528. *
  529. * @return string compiled code
  530. */
  531. public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)
  532. {
  533. // re-use object if already exists
  534. if (isset(self::$_tag_objects[$tag])) {
  535. // compile this tag
  536. return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3);
  537. }
  538. // lazy load internal compiler plugin
  539. $class_name = 'Smarty_Internal_Compile_' . $tag;
  540. if ($this->smarty->loadPlugin($class_name)) {
  541. // check if tag allowed by security
  542. if (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) {
  543. // use plugin if found
  544. self::$_tag_objects[$tag] = new $class_name;
  545. // compile this tag
  546. return self::$_tag_objects[$tag]->compile($args, $this, $param1, $param2, $param3);
  547. }
  548. }
  549. // no internal compile plugin for this tag
  550. return false;
  551. }
  552. /**
  553. * Check for plugins and return function name
  554. *
  555. * @param $plugin_name
  556. * @param string $plugin_type type of plugin
  557. *
  558. * @return string call name of function
  559. */
  560. public function getPlugin($plugin_name, $plugin_type)
  561. {
  562. $function = null;
  563. if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
  564. if (isset($this->template->required_plugins['nocache'][$plugin_name][$plugin_type])) {
  565. $function = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'];
  566. } elseif (isset($this->template->required_plugins['compiled'][$plugin_name][$plugin_type])) {
  567. $this->template->required_plugins['nocache'][$plugin_name][$plugin_type] = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type];
  568. $function = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'];
  569. }
  570. } else {
  571. if (isset($this->template->required_plugins['compiled'][$plugin_name][$plugin_type])) {
  572. $function = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'];
  573. } elseif (isset($this->template->required_plugins['nocache'][$plugin_name][$plugin_type])) {
  574. $this->template->required_plugins['compiled'][$plugin_name][$plugin_type] = $this->template->required_plugins['nocache'][$plugin_name][$plugin_type];
  575. $function = $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'];
  576. }
  577. }
  578. if (isset($function)) {
  579. if ($plugin_type == 'modifier') {
  580. $this->modifier_plugins[$plugin_name] = true;
  581. }
  582. return $function;
  583. }
  584. // loop through plugin dirs and find the plugin
  585. $function = 'smarty_' . $plugin_type . '_' . $plugin_name;
  586. $file = $this->smarty->loadPlugin($function, false);
  587. if (is_string($file)) {
  588. if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
  589. $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['file'] = $file;
  590. $this->template->required_plugins['nocache'][$plugin_name][$plugin_type]['function'] = $function;
  591. } else {
  592. $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['file'] = $file;
  593. $this->template->required_plugins['compiled'][$plugin_name][$plugin_type]['function'] = $function;
  594. }
  595. if ($plugin_type == 'modifier') {
  596. $this->modifier_plugins[$plugin_name] = true;
  597. }
  598. return $function;
  599. }
  600. if (is_callable($function)) {
  601. // plugin function is defined in the script
  602. return $function;
  603. }
  604. return false;
  605. }
  606. /**
  607. * Check for plugins by default plugin handler
  608. *
  609. * @param string $tag name of tag
  610. * @param string $plugin_type type of plugin
  611. *
  612. * @return boolean true if found
  613. */
  614. public function getPluginFromDefaultHandler($tag, $plugin_type)
  615. {
  616. $callback = null;
  617. $script = null;
  618. $cacheable = true;
  619. $result = call_user_func_array(
  620. $this->smarty->default_plugin_handler_func, array($tag, $plugin_type, $this->template, &$callback, &$script, &$cacheable)
  621. );
  622. if ($result) {
  623. $this->tag_nocache = $this->tag_nocache || !$cacheable;
  624. if ($script !== null) {
  625. if (is_file($script)) {
  626. if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
  627. $this->template->required_plugins['nocache'][$tag][$plugin_type]['file'] = $script;
  628. $this->template->required_plugins['nocache'][$tag][$plugin_type]['function'] = $callback;
  629. } else {
  630. $this->template->required_plugins['compiled'][$tag][$plugin_type]['file'] = $script;
  631. $this->template->required_plugins['compiled'][$tag][$plugin_type]['function'] = $callback;
  632. }
  633. include_once $script;
  634. } else {
  635. $this->trigger_template_error("Default plugin handler: Returned script file \"{$script}\" for \"{$tag}\" not found");
  636. }
  637. }
  638. if (!is_string($callback) && !(is_array($callback) && is_string($callback[0]) && is_string($callback[1]))) {
  639. $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" must be a static function name or array of class and function name");
  640. }
  641. if (is_callable($callback)) {
  642. $this->default_handler_plugins[$plugin_type][$tag] = array($callback, true, array());
  643. return true;
  644. } else {
  645. $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" not callable");
  646. }
  647. }
  648. return false;
  649. }
  650. /**
  651. * Inject inline code for nocache template sections
  652. * This method gets the content of each template element from the parser.
  653. * If the content is compiled code and it should be not cached the code is injected
  654. * into the rendered output.
  655. *
  656. * @param string $content content of template element
  657. * @param boolean $is_code true if content is compiled code
  658. *
  659. * @return string content
  660. */
  661. public function processNocacheCode($content, $is_code)
  662. {
  663. // If the template is not evaluated and we have a nocache section and or a nocache tag
  664. if ($is_code && !empty($content)) {
  665. // generate replacement code
  666. if ((!($this->template->source->recompiled) || $this->forceNocache) && $this->template->caching && !$this->suppressNocacheProcessing &&
  667. ($this->nocache || $this->tag_nocache)
  668. ) {
  669. $this->template->has_nocache_code = true;
  670. $_output = addcslashes($content, '\'\\');
  671. $_output = str_replace("^#^", "'", $_output);
  672. $_output = "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/" . $_output . "/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n";
  673. // make sure we include modifier plugins for nocache code
  674. foreach ($this->modifier_plugins as $plugin_name => $dummy) {
  675. if (isset($this->template->required_plugins['compiled'][$plugin_name]['modifier'])) {
  676. $this->template->required_plugins['nocache'][$plugin_name]['modifier'] = $this->template->required_plugins['compiled'][$plugin_name]['modifier'];
  677. }
  678. }
  679. } else {
  680. $_output = $content;
  681. }
  682. } else {
  683. $_output = $content;
  684. }
  685. $this->modifier_plugins = array();
  686. $this->suppressNocacheProcessing = false;
  687. $this->tag_nocache = false;
  688. return $_output;
  689. }
  690. /**
  691. * push current file and line offset on stack for tracing {block} source lines
  692. *
  693. * @param string $file new filename
  694. * @param string $uid uid of file
  695. * @param int $line line offset to source
  696. * @param bool $debug false debug end_compile shall not be called
  697. */
  698. public function pushTrace($file, $uid, $line, $debug = true)
  699. {
  700. if ($this->smarty->debugging && $debug) {
  701. Smarty_Internal_Debug::end_compile($this->template);
  702. }
  703. array_push($this->trace_stack, array($this->smarty->_current_file, $this->trace_filepath, $this->trace_uid, $this->trace_line_offset));
  704. $this->trace_filepath = $this->smarty->_current_file = $file;
  705. $this->trace_uid = $uid;
  706. $this->trace_line_offset = $line;
  707. if ($this->smarty->debugging) {
  708. Smarty_Internal_Debug::start_compile($this->template);
  709. }
  710. }
  711. /**
  712. * restore file and line offset
  713. */
  714. public function popTrace()
  715. {
  716. if ($this->smarty->debugging) {
  717. Smarty_Internal_Debug::end_compile($this->template);
  718. }
  719. $r = array_pop($this->trace_stack);
  720. $this->smarty->_current_file = $r[0];
  721. $this->trace_filepath = $r[1];
  722. $this->trace_uid = $r[2];
  723. $this->trace_line_offset = $r[3];
  724. if ($this->smarty->debugging) {
  725. Smarty_Internal_Debug::start_compile($this->template);
  726. }
  727. }
  728. /**
  729. * display compiler error messages without dying
  730. * If parameter $args is empty it is a parser detected syntax error.
  731. * In this case the parser is called to obtain information about expected tokens.
  732. * If parameter $args contains a string this is used as error message
  733. *
  734. * @param string $args individual error message or null
  735. * @param string $line line-number
  736. *
  737. * @throws SmartyCompilerException when an unexpected token is found
  738. */
  739. public function trigger_template_error($args = null, $line = null)
  740. {
  741. // get template source line which has error
  742. if (!isset($line)) {
  743. $line = $this->lex->line;
  744. }
  745. // $line += $this->trace_line_offset;
  746. $match = preg_split("/\n/", $this->lex->data);
  747. $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])) . '" ';
  748. if (isset($args)) {
  749. // individual error message
  750. $error_text .= $args;
  751. } else {
  752. // expected token from parser
  753. $error_text .= ' - Unexpected "' . $this->lex->value . '"';
  754. if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) {
  755. foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
  756. $exp_token = $this->parser->yyTokenName[$token];
  757. if (isset($this->lex->smarty_token_names[$exp_token])) {
  758. // token type from lexer
  759. $expect[] = '"' . $this->lex->smarty_token_names[$exp_token] . '"';
  760. } else {
  761. // otherwise internal token name
  762. $expect[] = $this->parser->yyTokenName[$token];
  763. }
  764. }
  765. $error_text .= ', expected one of: ' . implode(' , ', $expect);
  766. }
  767. }
  768. $e = new SmartyCompilerException($error_text);
  769. $e->line = $line;
  770. $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[$line - 1]));
  771. $e->desc = $args;
  772. $e->template = $this->template->source->filepath;
  773. throw $e;
  774. }
  775. }