smarty_internal_templatecompilerbase.php 33 KB

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