/web/smarty/sysplugins/smarty_internal_templatecompilerbase.php

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