You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

smarty_internal_templatecompilerbase.php 46KB

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