smarty_internal_templatecompilerbase.php 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295
  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 registerPostCompileCallback($callback, $parameter = array(), $key = null, $replace = false)
  19. * @method 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. static $_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. * counter for prefix variable number
  253. *
  254. * @var int
  255. */
  256. public static $prefixVariableNumber = 0;
  257. /**
  258. * method to compile a Smarty template
  259. *
  260. * @param mixed $_content template source
  261. * @param bool $isTemplateSource
  262. *
  263. * @return bool true if compiling succeeded, false if it failed
  264. */
  265. abstract protected function doCompile($_content, $isTemplateSource = false);
  266. /**
  267. * Initialize compiler
  268. *
  269. * @param Smarty $smarty global instance
  270. */
  271. public function __construct(Smarty $smarty)
  272. {
  273. $this->smarty = $smarty;
  274. $this->nocache_hash = str_replace(array('.', ','), '_', uniqid(rand(), true));
  275. }
  276. /**
  277. * Method to compile a Smarty template
  278. *
  279. * @param Smarty_Internal_Template $template template object to compile
  280. * @param bool $nocache true is shall be compiled in nocache mode
  281. * @param null|Smarty_Internal_TemplateCompilerBase $parent_compiler
  282. *
  283. * @return bool true if compiling succeeded, false if it failed
  284. * @throws \Exception
  285. */
  286. public function compileTemplate(Smarty_Internal_Template $template, $nocache = null,
  287. Smarty_Internal_TemplateCompilerBase $parent_compiler = null)
  288. {
  289. // get code frame of compiled template
  290. $_compiled_code = $template->smarty->ext->_codeFrame->create($template,
  291. $this->compileTemplateSource($template, $nocache,
  292. $parent_compiler),
  293. $this->postFilter($this->blockOrFunctionCode) .
  294. join('', $this->mergedSubTemplatesCode), false,
  295. $this);
  296. return $_compiled_code;
  297. }
  298. /**
  299. * Compile template source and run optional post filter
  300. *
  301. * @param \Smarty_Internal_Template $template
  302. * @param null|bool $nocache flag if template must be compiled in nocache mode
  303. * @param \Smarty_Internal_TemplateCompilerBase $parent_compiler
  304. *
  305. * @return string
  306. * @throws \Exception
  307. */
  308. public function compileTemplateSource(Smarty_Internal_Template $template, $nocache = null,
  309. Smarty_Internal_TemplateCompilerBase $parent_compiler = null)
  310. {
  311. try {
  312. // save template object in compiler class
  313. $this->template = $template;
  314. if (property_exists($this->template->smarty, 'plugin_search_order')) {
  315. $this->plugin_search_order = $this->template->smarty->plugin_search_order;
  316. }
  317. if ($this->smarty->debugging) {
  318. if (!isset($this->smarty->_debug)) {
  319. $this->smarty->_debug = new Smarty_Internal_Debug();
  320. }
  321. $this->smarty->_debug->start_compile($this->template);
  322. }
  323. if (isset($this->template->smarty->security_policy)) {
  324. $this->php_handling = $this->template->smarty->security_policy->php_handling;
  325. } else {
  326. $this->php_handling = $this->template->smarty->php_handling;
  327. }
  328. $this->parent_compiler = $parent_compiler ? $parent_compiler : $this;
  329. $nocache = isset($nocache) ? $nocache : false;
  330. if (empty($template->compiled->nocache_hash)) {
  331. $template->compiled->nocache_hash = $this->nocache_hash;
  332. } else {
  333. $this->nocache_hash = $template->compiled->nocache_hash;
  334. }
  335. // flag for nocache sections
  336. $this->nocache = $nocache;
  337. $this->tag_nocache = false;
  338. // reset has nocache code flag
  339. $this->template->compiled->has_nocache_code = false;
  340. $this->has_variable_string = false;
  341. $this->prefix_code = array();
  342. // add file dependency
  343. if ($this->smarty->merge_compiled_includes || $this->template->source->handler->checkTimestamps()) {
  344. $this->parent_compiler->template->compiled->file_dependency[ $this->template->source->uid ] =
  345. array($this->template->source->filepath, $this->template->source->getTimeStamp(),
  346. $this->template->source->type,);
  347. }
  348. $this->smarty->_current_file = $this->template->source->filepath;
  349. // get template source
  350. if (!empty($this->template->source->components)) {
  351. // we have array of inheritance templates by extends: resource
  352. // generate corresponding source code sequence
  353. $_content =
  354. Smarty_Internal_Compile_Extends::extendsSourceArrayCode($this->template->source->components);
  355. } else {
  356. // get template source
  357. $_content = $this->template->source->getContent();
  358. }
  359. $_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true));
  360. }
  361. catch (Exception $e) {
  362. if ($this->smarty->debugging) {
  363. $this->smarty->_debug->end_compile($this->template);
  364. }
  365. $this->_tag_stack = array();
  366. // free memory
  367. $this->parent_compiler = null;
  368. $this->template = null;
  369. $this->parser = null;
  370. throw $e;
  371. }
  372. if ($this->smarty->debugging) {
  373. $this->smarty->_debug->end_compile($this->template);
  374. }
  375. $this->parent_compiler = null;
  376. $this->parser = null;
  377. return $_compiled_code;
  378. }
  379. /**
  380. * Optionally process compiled code by post filter
  381. *
  382. * @param string $code compiled code
  383. *
  384. * @return string
  385. * @throws \SmartyException
  386. */
  387. public function postFilter($code)
  388. {
  389. // run post filter if on code
  390. if (!empty($code) &&
  391. (isset($this->smarty->autoload_filters[ 'post' ]) || isset($this->smarty->registered_filters[ 'post' ]))
  392. ) {
  393. return $this->smarty->ext->_filterHandler->runFilter('post', $code, $this->template);
  394. } else {
  395. return $code;
  396. }
  397. }
  398. /**
  399. * Run optional prefilter
  400. *
  401. * @param string $_content template source
  402. *
  403. * @return string
  404. * @throws \SmartyException
  405. */
  406. public function preFilter($_content)
  407. {
  408. // run pre filter if required
  409. if ($_content != '' &&
  410. ((isset($this->smarty->autoload_filters[ 'pre' ]) || isset($this->smarty->registered_filters[ 'pre' ])))
  411. ) {
  412. return $this->smarty->ext->_filterHandler->runFilter('pre', $_content, $this->template);
  413. } else {
  414. return $_content;
  415. }
  416. }
  417. /**
  418. * Compile Tag
  419. * This is a call back from the lexer/parser
  420. *
  421. * Save current prefix code
  422. * Compile tag
  423. * Merge tag prefix code with saved one
  424. * (required nested tags in attributes)
  425. *
  426. * @param string $tag tag name
  427. * @param array $args array with tag attributes
  428. * @param array $parameter array with compilation parameter
  429. *
  430. * @throws SmartyCompilerException
  431. * @throws SmartyException
  432. * @return string compiled code
  433. */
  434. public function compileTag($tag, $args, $parameter = array())
  435. {
  436. $this->prefixCodeStack[] = $this->prefix_code;
  437. $this->prefix_code = array();
  438. $result = $this->compileTag2($tag, $args, $parameter);
  439. $this->prefix_code = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
  440. return $result;
  441. }
  442. /**
  443. * Compile Tag
  444. *
  445. * @param string $tag tag name
  446. * @param array $args array with tag attributes
  447. * @param array $parameter array with compilation parameter
  448. *
  449. * @throws SmartyCompilerException
  450. * @throws SmartyException
  451. * @return string compiled code
  452. */
  453. private function compileTag2($tag, $args, $parameter)
  454. {
  455. $plugin_type = '';
  456. // $args contains the attributes parsed and compiled by the lexer/parser
  457. // assume that tag does compile into code, but creates no HTML output
  458. $this->has_code = true;
  459. $this->has_output = false;
  460. // log tag/attributes
  461. if (isset($this->smarty->_cache[ 'get_used_tags' ])) {
  462. $this->template->_cache[ 'used_tags' ][] = array($tag, $args);
  463. }
  464. // check nocache option flag
  465. foreach ($args as $arg) {
  466. if (!is_array($arg)) {
  467. if ($arg == "'nocache'") {
  468. $this->tag_nocache = true;
  469. }
  470. } else {
  471. foreach ($arg as $k => $v) {
  472. if ($k == "'nocache'" && (trim($v, "'\" ") == 'true')) {
  473. $this->tag_nocache = true;
  474. }
  475. }
  476. }
  477. }
  478. // compile the smarty tag (required compile classes to compile the tag are auto loaded)
  479. if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) {
  480. if (isset($this->parent_compiler->tpl_function[ $tag ])) {
  481. // template defined by {template} tag
  482. $args[ '_attr' ][ 'name' ] = "'" . $tag . "'";
  483. $_output = $this->callTagCompiler('call', $args, $parameter);
  484. }
  485. }
  486. if ($_output !== false) {
  487. if ($_output !== true) {
  488. // did we get compiled code
  489. if ($this->has_code) {
  490. // Does it create output?
  491. if ($this->has_output) {
  492. $_output .= "\n";
  493. }
  494. // return compiled code
  495. return $_output;
  496. }
  497. }
  498. // tag did not produce compiled code
  499. return null;
  500. } else {
  501. // map_named attributes
  502. if (isset($args[ '_attr' ])) {
  503. foreach ($args[ '_attr' ] as $key => $attribute) {
  504. if (is_array($attribute)) {
  505. $args = array_merge($args, $attribute);
  506. }
  507. }
  508. }
  509. // not an internal compiler tag
  510. if (strlen($tag) < 6 || substr($tag, - 5) != 'close') {
  511. // check if tag is a registered object
  512. if (isset($this->smarty->registered_objects[ $tag ]) && isset($parameter[ 'object_method' ])) {
  513. $method = $parameter[ 'object_method' ];
  514. if (!in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ]) &&
  515. (empty($this->smarty->registered_objects[ $tag ][ 1 ]) ||
  516. in_array($method, $this->smarty->registered_objects[ $tag ][ 1 ]))
  517. ) {
  518. return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method);
  519. } elseif (in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])) {
  520. return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag,
  521. $method);
  522. } else {
  523. // throw exception
  524. $this->trigger_template_error('not allowed method "' . $method . '" in registered object "' .
  525. $tag . '"', null, true);
  526. }
  527. }
  528. // check if tag is registered
  529. foreach (array(Smarty::PLUGIN_COMPILER, Smarty::PLUGIN_FUNCTION, Smarty::PLUGIN_BLOCK,) as $plugin_type)
  530. {
  531. if (isset($this->smarty->registered_plugins[ $plugin_type ][ $tag ])) {
  532. // if compiler function plugin call it now
  533. if ($plugin_type == Smarty::PLUGIN_COMPILER) {
  534. $new_args = array();
  535. foreach ($args as $key => $mixed) {
  536. if (is_array($mixed)) {
  537. $new_args = array_merge($new_args, $mixed);
  538. } else {
  539. $new_args[ $key ] = $mixed;
  540. }
  541. }
  542. if (!$this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 1 ]) {
  543. $this->tag_nocache = true;
  544. }
  545. return call_user_func_array($this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 0 ],
  546. array($new_args, $this));
  547. }
  548. // compile registered function or block function
  549. if ($plugin_type == Smarty::PLUGIN_FUNCTION || $plugin_type == Smarty::PLUGIN_BLOCK) {
  550. return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter,
  551. $tag);
  552. }
  553. }
  554. }
  555. // check plugins from plugins folder
  556. foreach ($this->plugin_search_order as $plugin_type) {
  557. if ($plugin_type == Smarty::PLUGIN_COMPILER &&
  558. $this->smarty->loadPlugin('smarty_compiler_' . $tag) &&
  559. (!isset($this->smarty->security_policy) ||
  560. $this->smarty->security_policy->isTrustedTag($tag, $this))
  561. ) {
  562. $plugin = 'smarty_compiler_' . $tag;
  563. if (is_callable($plugin)) {
  564. // convert arguments format for old compiler plugins
  565. $new_args = array();
  566. foreach ($args as $key => $mixed) {
  567. if (is_array($mixed)) {
  568. $new_args = array_merge($new_args, $mixed);
  569. } else {
  570. $new_args[ $key ] = $mixed;
  571. }
  572. }
  573. return $plugin($new_args, $this->smarty);
  574. }
  575. if (class_exists($plugin, false)) {
  576. $plugin_object = new $plugin;
  577. if (method_exists($plugin_object, 'compile')) {
  578. return $plugin_object->compile($args, $this);
  579. }
  580. }
  581. throw new SmartyException("Plugin \"{$tag}\" not callable");
  582. } else {
  583. if ($function = $this->getPlugin($tag, $plugin_type)) {
  584. if (!isset($this->smarty->security_policy) ||
  585. $this->smarty->security_policy->isTrustedTag($tag, $this)
  586. ) {
  587. return $this->callTagCompiler('private_' . $plugin_type . '_plugin', $args, $parameter,
  588. $tag, $function);
  589. }
  590. }
  591. }
  592. }
  593. if (is_callable($this->smarty->default_plugin_handler_func)) {
  594. $found = false;
  595. // look for already resolved tags
  596. foreach ($this->plugin_search_order as $plugin_type) {
  597. if (isset($this->default_handler_plugins[ $plugin_type ][ $tag ])) {
  598. $found = true;
  599. break;
  600. }
  601. }
  602. if (!$found) {
  603. // call default handler
  604. foreach ($this->plugin_search_order as $plugin_type) {
  605. if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) {
  606. $found = true;
  607. break;
  608. }
  609. }
  610. }
  611. if ($found) {
  612. // if compiler function plugin call it now
  613. if ($plugin_type == Smarty::PLUGIN_COMPILER) {
  614. $new_args = array();
  615. foreach ($args as $mixed) {
  616. $new_args = array_merge($new_args, $mixed);
  617. }
  618. return call_user_func_array($this->default_handler_plugins[ $plugin_type ][ $tag ][ 0 ],
  619. array($new_args, $this));
  620. } else {
  621. return $this->callTagCompiler('private_registered_' . $plugin_type, $args, $parameter,
  622. $tag);
  623. }
  624. }
  625. }
  626. } else {
  627. // compile closing tag of block function
  628. $base_tag = substr($tag, 0, - 5);
  629. // check if closing tag is a registered object
  630. if (isset($this->smarty->registered_objects[ $base_tag ]) && isset($parameter[ 'object_method' ])) {
  631. $method = $parameter[ 'object_method' ];
  632. if (in_array($method, $this->smarty->registered_objects[ $base_tag ][ 3 ])) {
  633. return $this->callTagCompiler('private_object_block_function', $args, $parameter, $tag,
  634. $method);
  635. } else {
  636. // throw exception
  637. $this->trigger_template_error('not allowed closing tag method "' . $method .
  638. '" in registered object "' . $base_tag . '"', null, true);
  639. }
  640. }
  641. // registered block tag ?
  642. if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ]) ||
  643. isset($this->default_handler_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ])
  644. ) {
  645. return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag);
  646. }
  647. // registered function tag ?
  648. if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_FUNCTION ][ $tag ])) {
  649. return $this->callTagCompiler('private_registered_function', $args, $parameter, $tag);
  650. }
  651. // block plugin?
  652. if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) {
  653. return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function);
  654. }
  655. // function plugin?
  656. if ($function = $this->getPlugin($tag, Smarty::PLUGIN_FUNCTION)) {
  657. if (!isset($this->smarty->security_policy) ||
  658. $this->smarty->security_policy->isTrustedTag($tag, $this)
  659. ) {
  660. return $this->callTagCompiler('private_function_plugin', $args, $parameter, $tag, $function);
  661. }
  662. }
  663. // registered compiler plugin ?
  664. if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ])) {
  665. // if compiler function plugin call it now
  666. $args = array();
  667. if (!$this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 1 ]) {
  668. $this->tag_nocache = true;
  669. }
  670. return call_user_func_array($this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 0 ],
  671. array($args, $this));
  672. }
  673. if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {
  674. $plugin = 'smarty_compiler_' . $tag;
  675. if (is_callable($plugin)) {
  676. return $plugin($args, $this->smarty);
  677. }
  678. if (class_exists($plugin, false)) {
  679. $plugin_object = new $plugin;
  680. if (method_exists($plugin_object, 'compile')) {
  681. return $plugin_object->compile($args, $this);
  682. }
  683. }
  684. throw new SmartyException("Plugin \"{$tag}\" not callable");
  685. }
  686. }
  687. $this->trigger_template_error("unknown tag \"" . $tag . "\"", null, true);
  688. }
  689. }
  690. /**
  691. * compile variable
  692. *
  693. * @param string $variable
  694. *
  695. * @return string
  696. */
  697. public function compileVariable($variable)
  698. {
  699. if (strpos($variable, '(') == 0) {
  700. // not a variable variable
  701. $var = trim($variable, '\'');
  702. $this->tag_nocache = $this->tag_nocache |
  703. $this->template->ext->getTemplateVars->_getVariable($this->template, $var, null, true,
  704. false)->nocache;
  705. // todo $this->template->compiled->properties['variables'][$var] = $this->tag_nocache | $this->nocache;
  706. }
  707. return '$_smarty_tpl->tpl_vars[' . $variable . ']->value';
  708. }
  709. /**
  710. * compile config variable
  711. *
  712. * @param string $variable
  713. *
  714. * @return string
  715. */
  716. public function compileConfigVariable($variable)
  717. {
  718. // return '$_smarty_tpl->config_vars[' . $variable . ']';
  719. return '$_smarty_tpl->smarty->ext->configLoad->_getConfigVariable($_smarty_tpl, ' . $variable . ')';
  720. }
  721. /**
  722. * This method is called from parser to process a text content section
  723. * - remove text from inheritance child templates as they may generate output
  724. * - strip text if strip is enabled
  725. *
  726. * @param string $text
  727. *
  728. * @return null|\Smarty_Internal_ParseTree_Text
  729. */
  730. public function processText($text)
  731. {
  732. if ((string) $text != '') {
  733. $store = array();
  734. $_store = 0;
  735. if ($this->parser->strip) {
  736. if (strpos($text, '<') !== false) {
  737. // capture html elements not to be messed with
  738. $_offset = 0;
  739. if (preg_match_all('#(<script[^>]*>.*?</script[^>]*>)|(<textarea[^>]*>.*?</textarea[^>]*>)|(<pre[^>]*>.*?</pre[^>]*>)#is',
  740. $text, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
  741. foreach ($matches as $match) {
  742. $store[] = $match[ 0 ][ 0 ];
  743. $_length = strlen($match[ 0 ][ 0 ]);
  744. $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@';
  745. $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length);
  746. $_offset += $_length - strlen($replace);
  747. $_store ++;
  748. }
  749. }
  750. $expressions = array(// replace multiple spaces between tags by a single space
  751. '#(:SMARTY@!@|>)[\040\011]+(?=@!@SMARTY:|<)#s' => '\1 \2',
  752. // remove newline between tags
  753. '#(:SMARTY@!@|>)[\040\011]*[\n]\s*(?=@!@SMARTY:|<)#s' => '\1\2',
  754. // remove multiple spaces between attributes (but not in attribute values!)
  755. '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5',
  756. '#>[\040\011]+$#Ss' => '> ', '#>[\040\011]*[\n]\s*$#Ss' => '>',
  757. $this->stripRegEx => '',);
  758. $text = preg_replace(array_keys($expressions), array_values($expressions), $text);
  759. $_offset = 0;
  760. if (preg_match_all('#@!@SMARTY:([0-9]+):SMARTY@!@#is', $text, $matches,
  761. PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
  762. foreach ($matches as $match) {
  763. $_length = strlen($match[ 0 ][ 0 ]);
  764. $replace = $store[ $match[ 1 ][ 0 ] ];
  765. $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length);
  766. $_offset += strlen($replace) - $_length;
  767. $_store ++;
  768. }
  769. }
  770. } else {
  771. $text = preg_replace($this->stripRegEx, '', $text);
  772. }
  773. }
  774. return new Smarty_Internal_ParseTree_Text($text);
  775. }
  776. return null;
  777. }
  778. /**
  779. * lazy loads internal compile plugin for tag and calls the compile method
  780. * compile objects cached for reuse.
  781. * class name format: Smarty_Internal_Compile_TagName
  782. * plugin filename format: Smarty_Internal_TagName.php
  783. *
  784. * @param string $tag tag name
  785. * @param array $args list of tag attributes
  786. * @param mixed $param1 optional parameter
  787. * @param mixed $param2 optional parameter
  788. * @param mixed $param3 optional parameter
  789. *
  790. * @return string compiled code
  791. */
  792. public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)
  793. {
  794. // re-use object if already exists
  795. if (!isset(self::$_tag_objects[ $tag ])) {
  796. // lazy load internal compiler plugin
  797. $_tag = explode('_', $tag);
  798. $_tag = array_map('ucfirst', $_tag);
  799. $class_name = 'Smarty_Internal_Compile_' . implode('_', $_tag);
  800. if (class_exists($class_name) &&
  801. (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))
  802. ) {
  803. self::$_tag_objects[ $tag ] = new $class_name;
  804. } else {
  805. self::$_tag_objects[ $tag ] = false;
  806. return false;
  807. }
  808. }
  809. // compile this tag
  810. return self::$_tag_objects[ $tag ] === false ? false :
  811. self::$_tag_objects[ $tag ]->compile($args, $this, $param1, $param2, $param3);
  812. }
  813. /**
  814. * Check for plugins and return function name
  815. *
  816. * @param $plugin_name
  817. * @param string $plugin_type type of plugin
  818. *
  819. * @return string call name of function
  820. */
  821. public function getPlugin($plugin_name, $plugin_type)
  822. {
  823. $function = null;
  824. if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
  825. if (isset($this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
  826. $function =
  827. $this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  828. } elseif (isset($this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
  829. $this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ] =
  830. $this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ];
  831. $function =
  832. $this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  833. }
  834. } else {
  835. if (isset($this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
  836. $function =
  837. $this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  838. } elseif (isset($this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
  839. $this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ] =
  840. $this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ];
  841. $function =
  842. $this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  843. }
  844. }
  845. if (isset($function)) {
  846. if ($plugin_type == 'modifier') {
  847. $this->modifier_plugins[ $plugin_name ] = true;
  848. }
  849. return $function;
  850. }
  851. // loop through plugin dirs and find the plugin
  852. $function = 'smarty_' . $plugin_type . '_' . $plugin_name;
  853. $file = $this->smarty->loadPlugin($function, false);
  854. if (is_string($file)) {
  855. if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
  856. $this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
  857. $file;
  858. $this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
  859. $function;
  860. } else {
  861. $this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
  862. $file;
  863. $this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
  864. $function;
  865. }
  866. if ($plugin_type == 'modifier') {
  867. $this->modifier_plugins[ $plugin_name ] = true;
  868. }
  869. return $function;
  870. }
  871. if (is_callable($function)) {
  872. // plugin function is defined in the script
  873. return $function;
  874. }
  875. return false;
  876. }
  877. /**
  878. * Check for plugins by default plugin handler
  879. *
  880. * @param string $tag name of tag
  881. * @param string $plugin_type type of plugin
  882. *
  883. * @return boolean true if found
  884. */
  885. public function getPluginFromDefaultHandler($tag, $plugin_type)
  886. {
  887. $callback = null;
  888. $script = null;
  889. $cacheable = true;
  890. $result = call_user_func_array($this->smarty->default_plugin_handler_func,
  891. array($tag, $plugin_type, $this->template, &$callback, &$script, &$cacheable,));
  892. if ($result) {
  893. $this->tag_nocache = $this->tag_nocache || !$cacheable;
  894. if ($script !== null) {
  895. if (is_file($script)) {
  896. if ($this->template->caching && ($this->nocache || $this->tag_nocache)) {
  897. $this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'file' ] =
  898. $script;
  899. $this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'function' ] =
  900. $callback;
  901. } else {
  902. $this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'file' ] =
  903. $script;
  904. $this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'function' ] =
  905. $callback;
  906. }
  907. require_once $script;
  908. } else {
  909. $this->trigger_template_error("Default plugin handler: Returned script file \"{$script}\" for \"{$tag}\" not found");
  910. }
  911. }
  912. if (is_callable($callback)) {
  913. $this->default_handler_plugins[ $plugin_type ][ $tag ] = array($callback, true, array());
  914. return true;
  915. } else {
  916. $this->trigger_template_error("Default plugin handler: Returned callback for \"{$tag}\" not callable");
  917. }
  918. }
  919. return false;
  920. }
  921. /**
  922. * Append code segments and remove unneeded ?> <?php transitions
  923. *
  924. * @param string $left
  925. * @param string $right
  926. *
  927. * @return string
  928. */
  929. public function appendCode($left, $right)
  930. {
  931. if (preg_match('/\s*\?>\s*$/', $left) && preg_match('/^\s*<\?php\s+/', $right)) {
  932. $left = preg_replace('/\s*\?>\s*$/', "\n", $left);
  933. $left .= preg_replace('/^\s*<\?php\s+/', '', $right);
  934. } else {
  935. $left .= $right;
  936. }
  937. return $left;
  938. }
  939. /**
  940. * Inject inline code for nocache template sections
  941. * This method gets the content of each template element from the parser.
  942. * If the content is compiled code and it should be not cached the code is injected
  943. * into the rendered output.
  944. *
  945. * @param string $content content of template element
  946. * @param boolean $is_code true if content is compiled code
  947. *
  948. * @return string content
  949. */
  950. public function processNocacheCode($content, $is_code)
  951. {
  952. // If the template is not evaluated and we have a nocache section and or a nocache tag
  953. if ($is_code && !empty($content)) {
  954. // generate replacement code
  955. if ((!($this->template->source->handler->recompiled) || $this->forceNocache) && $this->template->caching &&
  956. !$this->suppressNocacheProcessing && ($this->nocache || $this->tag_nocache)
  957. ) {
  958. $this->template->compiled->has_nocache_code = true;
  959. $_output = addcslashes($content, '\'\\');
  960. $_output = str_replace("^#^", "'", $_output);
  961. $_output = "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/" . $_output .
  962. "/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n";
  963. // make sure we include modifier plugins for nocache code
  964. foreach ($this->modifier_plugins as $plugin_name => $dummy) {
  965. if (isset($this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ])) {
  966. $this->parent_compiler->template->compiled->required_plugins[ 'nocache' ][ $plugin_name ][ 'modifier' ] =
  967. $this->parent_compiler->template->compiled->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ];
  968. }
  969. }
  970. } else {
  971. $_output = $content;
  972. }
  973. } else {
  974. $_output = $content;
  975. }
  976. $this->modifier_plugins = array();
  977. $this->suppressNocacheProcessing = false;
  978. $this->tag_nocache = false;
  979. return $_output;
  980. }
  981. /**
  982. * Get Id
  983. *
  984. * @param string $input
  985. *
  986. * @return bool|string
  987. */
  988. public function getId($input)
  989. {
  990. if (preg_match('~^([\'"]*)([0-9]*[a-zA-Z_]\w*)\1$~', $input, $match)) {
  991. return $match[ 2 ];
  992. }
  993. return false;
  994. }
  995. /**
  996. * Get variable name from string
  997. *
  998. * @param string $input
  999. *
  1000. * @return bool|string
  1001. */
  1002. public function getVariableName($input)
  1003. {
  1004. if (preg_match('~^[$]_smarty_tpl->tpl_vars\[[\'"]*([0-9]*[a-zA-Z_]\w*)[\'"]*\]->value$~', $input, $match)) {
  1005. return $match[ 1 ];
  1006. }
  1007. return false;
  1008. }
  1009. /**
  1010. * Set nocache flag in variable or create new variable
  1011. *
  1012. * @param string $varName
  1013. */
  1014. public function setNocacheInVariable($varName)
  1015. {
  1016. // create nocache var to make it know for further compiling
  1017. if ($_var = $this->getId($varName)) {
  1018. if (isset($this->template->tpl_vars[ $_var ])) {
  1019. $this->template->tpl_vars[ $_var ] = clone $this->template->tpl_vars[ $_var ];
  1020. $this->template->tpl_vars[ $_var ]->nocache = true;
  1021. } else {
  1022. $this->template->tpl_vars[ $_var ] = new Smarty_Variable(null, true);
  1023. }
  1024. }
  1025. }
  1026. /**
  1027. * @param array $_attr tag attributes
  1028. * @param array $validScopes
  1029. *
  1030. * @return int|string
  1031. * @throws \SmartyCompilerException
  1032. */
  1033. public function convertScope($_attr, $validScopes)
  1034. {
  1035. $_scope = 0;
  1036. if (isset($_attr[ 'scope' ])) {
  1037. $_scopeName = trim($_attr[ 'scope' ], "'\"");
  1038. if (is_numeric($_scopeName) && in_array($_scopeName, $validScopes)) {
  1039. $_scope = $_scopeName;
  1040. } elseif (is_string($_scopeName)) {
  1041. $_scopeName = trim($_scopeName, "'\"");
  1042. $_scope = isset($validScopes[ $_scopeName ]) ? $validScopes[ $_scopeName ] : false;
  1043. } else {
  1044. $_scope = false;
  1045. }
  1046. if ($_scope === false) {
  1047. $err = var_export($_scopeName, true);
  1048. $this->trigger_template_error("illegal value '{$err}' for \"scope\" attribute", null, true);
  1049. }
  1050. }
  1051. return $_scope;
  1052. }
  1053. /**
  1054. * Generate nocache code string
  1055. *
  1056. * @param string $code PHP code
  1057. *
  1058. * @return string
  1059. */
  1060. public function makeNocacheCode($code)
  1061. {
  1062. return "echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/<?php " .
  1063. str_replace("^#^", "'", addcslashes($code, '\'\\')) .
  1064. "?>/*/%%SmartyNocache:{$this->nocache_hash}%%*/';\n";
  1065. }
  1066. /**
  1067. * display compiler error messages without dying
  1068. * If parameter $args is empty it is a parser detected syntax error.
  1069. * In this case the parser is called to obtain information about expected tokens.
  1070. * If parameter $args contains a string this is used as error message
  1071. *
  1072. * @param string $args individual error message or null
  1073. * @param string $line line-number
  1074. * @param null|bool $tagline if true the line number of last tag
  1075. *
  1076. * @throws \SmartyCompilerException when an unexpected token is found
  1077. */
  1078. public function trigger_template_error($args = null, $line = null, $tagline = null)
  1079. {
  1080. $lex = $this->parser->lex;
  1081. if ($tagline === true) {
  1082. // get line number of Tag
  1083. $line = $lex->taglineno;
  1084. } elseif (!isset($line)) {
  1085. // get template source line which has error
  1086. $line = $lex->line;
  1087. } else {
  1088. $line = (int) $line;
  1089. }
  1090. if (in_array($this->template->source->type, array('eval', 'string'))) {
  1091. $templateName = $this->template->source->type . ':' . trim(preg_replace('![\t\r\n]+!', ' ',
  1092. strlen($lex->data) > 40 ?
  1093. substr($lex->data, 0, 40) .
  1094. '...' : $lex->data));
  1095. } else {
  1096. $templateName = $this->template->source->type . ':' . $this->template->source->filepath;
  1097. }
  1098. // $line += $this->trace_line_offset;
  1099. $match = preg_split("/\n/", $lex->data);
  1100. $error_text =
  1101. 'Syntax error in template "' . (empty($this->trace_filepath) ? $templateName : $this->trace_filepath) .
  1102. '" on line ' . ($line + $this->trace_line_offset) . ' "' .
  1103. trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ])) . '" ';
  1104. if (isset($args)) {
  1105. // individual error message
  1106. $error_text .= $args;
  1107. } else {
  1108. $expect = array();
  1109. // expected token from parser
  1110. $error_text .= ' - Unexpected "' . $lex->value . '"';
  1111. if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) {
  1112. foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
  1113. $exp_token = $this->parser->yyTokenName[ $token ];
  1114. if (isset($lex->smarty_token_names[ $exp_token ])) {
  1115. // token type from lexer
  1116. $expect[] = '"' . $lex->smarty_token_names[ $exp_token ] . '"';
  1117. } else {
  1118. // otherwise internal token name
  1119. $expect[] = $this->parser->yyTokenName[ $token ];
  1120. }
  1121. }
  1122. $error_text .= ', expected one of: ' . implode(' , ', $expect);
  1123. }
  1124. }
  1125. $e = new SmartyCompilerException($error_text);
  1126. $e->line = $line;
  1127. $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ]));
  1128. $e->desc = $args;
  1129. $e->template = $this->template->source->filepath;
  1130. throw $e;
  1131. }
  1132. /**
  1133. * Return var_export() value with all white spaces removed
  1134. *
  1135. * @param mixed $value
  1136. *
  1137. * @return string
  1138. */
  1139. public function getVarExport($value)
  1140. {
  1141. return preg_replace('/\s/', '', var_export($value, true));
  1142. }
  1143. /**
  1144. * Check if $value contains variable elements
  1145. *
  1146. * @param mixed $value
  1147. *
  1148. * @return bool|int
  1149. */
  1150. public function isVariable($value)
  1151. {
  1152. if (is_string($value)) {
  1153. return preg_match('/[$(]/', $value);
  1154. }
  1155. if (is_bool($value) || is_numeric($value)) {
  1156. return false;
  1157. }
  1158. if (is_array($value)) {
  1159. foreach ($value as $k => $v) {
  1160. if ($this->isVariable($k) || $this->isVariable($v)) {
  1161. return true;
  1162. }
  1163. }
  1164. return false;
  1165. }
  1166. return false;
  1167. }
  1168. /**
  1169. * Get new prefix variable name
  1170. *
  1171. * @return string
  1172. */
  1173. public function getNewPrefixVariable()
  1174. {
  1175. self::$prefixVariableNumber ++;
  1176. return $this->getPrefixVariable();
  1177. }
  1178. /**
  1179. * Get current prefix variable name
  1180. *
  1181. * @return string
  1182. */
  1183. public function getPrefixVariable()
  1184. {
  1185. return '$_prefixVariable' . self::$prefixVariableNumber;
  1186. }
  1187. /**
  1188. * append code to prefix buffer
  1189. *
  1190. * @param string $code
  1191. */
  1192. public function appendPrefixCode($code)
  1193. {
  1194. $this->prefix_code[] = $code;
  1195. }
  1196. /**
  1197. * get prefix code string
  1198. *
  1199. * @return string
  1200. */
  1201. public function getPrefixCode()
  1202. {
  1203. $code = '';
  1204. $prefixArray = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
  1205. $this->prefixCodeStack[] = array();
  1206. foreach ($prefixArray as $c) {
  1207. $code = $this->appendCode($code, $c);
  1208. }
  1209. $this->prefix_code = array();
  1210. return $code;
  1211. }
  1212. }