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 60KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631
  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. * compile tag objects cache
  25. *
  26. * @var array
  27. */
  28. static $_tag_objects = array();
  29. /**
  30. * counter for prefix variable number
  31. *
  32. * @var int
  33. */
  34. public static $prefixVariableNumber = 0;
  35. /**
  36. * Smarty object
  37. *
  38. * @var Smarty
  39. */
  40. public $smarty = null;
  41. /**
  42. * Parser object
  43. *
  44. * @var Smarty_Internal_Templateparser
  45. */
  46. public $parser = null;
  47. /**
  48. * hash for nocache sections
  49. *
  50. * @var mixed
  51. */
  52. public $nocache_hash = null;
  53. /**
  54. * suppress generation of nocache code
  55. *
  56. * @var bool
  57. */
  58. public $suppressNocacheProcessing = false;
  59. /**
  60. * caching enabled (copied from template object)
  61. *
  62. * @var int
  63. */
  64. public $caching = 0;
  65. /**
  66. * tag stack
  67. *
  68. * @var array
  69. */
  70. public $_tag_stack = array();
  71. /**
  72. * tag stack count
  73. *
  74. * @var array
  75. */
  76. public $_tag_stack_count = array();
  77. /**
  78. * Plugins used by template
  79. *
  80. * @var array
  81. */
  82. public $required_plugins = array('compiled' => array(), 'nocache' => array());
  83. /**
  84. * Required plugins stack
  85. *
  86. * @var array
  87. */
  88. public $required_plugins_stack = array();
  89. /**
  90. * current template
  91. *
  92. * @var Smarty_Internal_Template
  93. */
  94. public $template = null;
  95. /**
  96. * merged included sub template data
  97. *
  98. * @var array
  99. */
  100. public $mergedSubTemplatesData = array();
  101. /**
  102. * merged sub template code
  103. *
  104. * @var array
  105. */
  106. public $mergedSubTemplatesCode = array();
  107. /**
  108. * collected template properties during compilation
  109. *
  110. * @var array
  111. */
  112. public $templateProperties = array();
  113. /**
  114. * source line offset for error messages
  115. *
  116. * @var int
  117. */
  118. public $trace_line_offset = 0;
  119. /**
  120. * trace uid
  121. *
  122. * @var string
  123. */
  124. public $trace_uid = '';
  125. /**
  126. * trace file path
  127. *
  128. * @var string
  129. */
  130. public $trace_filepath = '';
  131. /**
  132. * stack for tracing file and line of nested {block} tags
  133. *
  134. * @var array
  135. */
  136. public $trace_stack = array();
  137. /**
  138. * plugins loaded by default plugin handler
  139. *
  140. * @var array
  141. */
  142. public $default_handler_plugins = array();
  143. /**
  144. * saved preprocessed modifier list
  145. *
  146. * @var mixed
  147. */
  148. public $default_modifier_list = null;
  149. /**
  150. * force compilation of complete template as nocache
  151. *
  152. * @var boolean
  153. */
  154. public $forceNocache = false;
  155. /**
  156. * flag if compiled template file shall we written
  157. *
  158. * @var bool
  159. */
  160. public $write_compiled_code = true;
  161. /**
  162. * Template functions
  163. *
  164. * @var array
  165. */
  166. public $tpl_function = array();
  167. /**
  168. * called sub functions from template function
  169. *
  170. * @var array
  171. */
  172. public $called_functions = array();
  173. /**
  174. * compiled template or block function code
  175. *
  176. * @var string
  177. */
  178. public $blockOrFunctionCode = '';
  179. /**
  180. * php_handling setting either from Smarty or security
  181. *
  182. * @var int
  183. */
  184. public $php_handling = 0;
  185. /**
  186. * flags for used modifier plugins
  187. *
  188. * @var array
  189. */
  190. public $modifier_plugins = array();
  191. /**
  192. * type of already compiled modifier
  193. *
  194. * @var array
  195. */
  196. public $known_modifier_type = array();
  197. /**
  198. * parent compiler object for merged subtemplates and template functions
  199. *
  200. * @var Smarty_Internal_TemplateCompilerBase
  201. */
  202. public $parent_compiler = null;
  203. /**
  204. * Flag true when compiling nocache section
  205. *
  206. * @var bool
  207. */
  208. public $nocache = false;
  209. /**
  210. * Flag true when tag is compiled as nocache
  211. *
  212. * @var bool
  213. */
  214. public $tag_nocache = false;
  215. /**
  216. * Compiled tag prefix code
  217. *
  218. * @var array
  219. */
  220. public $prefix_code = array();
  221. /**
  222. * used prefix variables by current compiled tag
  223. *
  224. * @var array
  225. */
  226. public $usedPrefixVariables = array();
  227. /**
  228. * Prefix code stack
  229. *
  230. * @var array
  231. */
  232. public $prefixCodeStack = array();
  233. /**
  234. * Tag has compiled code
  235. *
  236. * @var bool
  237. */
  238. public $has_code = false;
  239. /**
  240. * A variable string was compiled
  241. *
  242. * @var bool
  243. */
  244. public $has_variable_string = false;
  245. /**
  246. * Stack for {setfilter} {/setfilter}
  247. *
  248. * @var array
  249. */
  250. public $variable_filter_stack = array();
  251. /**
  252. * variable filters for {setfilter} {/setfilter}
  253. *
  254. * @var array
  255. */
  256. public $variable_filters = array();
  257. /**
  258. * Nesting count of looping tags like {foreach}, {for}, {section}, {while}
  259. *
  260. * @var int
  261. */
  262. public $loopNesting = 0;
  263. /**
  264. * Strip preg pattern
  265. *
  266. * @var string
  267. */
  268. public $stripRegEx = '![\t ]*[\r\n]+[\t ]*!';
  269. /**
  270. * plugin search order
  271. *
  272. * @var array
  273. */
  274. public $plugin_search_order = array('function',
  275. 'block',
  276. 'compiler',
  277. 'class');
  278. /**
  279. * General storage area for tag compiler plugins
  280. *
  281. * @var array
  282. */
  283. public $_cache = array();
  284. /**
  285. * Lexer preg pattern for left delimiter
  286. *
  287. * @var string
  288. */
  289. private $ldelPreg = '[{]';
  290. /**
  291. * Lexer preg pattern for right delimiter
  292. *
  293. * @var string
  294. */
  295. private $rdelPreg = '[}]';
  296. /**
  297. * Length of right delimiter
  298. *
  299. * @var int
  300. */
  301. private $rdelLength = 0;
  302. /**
  303. * Length of left delimiter
  304. *
  305. * @var int
  306. */
  307. private $ldelLength = 0;
  308. /**
  309. * Lexer preg pattern for user literals
  310. *
  311. * @var string
  312. */
  313. private $literalPreg = '';
  314. /**
  315. * Initialize compiler
  316. *
  317. * @param Smarty $smarty global instance
  318. */
  319. public function __construct(Smarty $smarty)
  320. {
  321. $this->smarty = $smarty;
  322. $this->nocache_hash = str_replace(array('.',
  323. ','),
  324. '_',
  325. uniqid(rand(), true));
  326. }
  327. /**
  328. * Method to compile a Smarty template
  329. *
  330. * @param Smarty_Internal_Template $template template object to compile
  331. * @param bool $nocache true is shall be compiled in nocache mode
  332. * @param null|Smarty_Internal_TemplateCompilerBase $parent_compiler
  333. *
  334. * @return bool true if compiling succeeded, false if it failed
  335. * @throws \Exception
  336. */
  337. public function compileTemplate(Smarty_Internal_Template $template,
  338. $nocache = null,
  339. Smarty_Internal_TemplateCompilerBase $parent_compiler = null)
  340. {
  341. // get code frame of compiled template
  342. $_compiled_code = $template->smarty->ext->_codeFrame->create($template,
  343. $this->compileTemplateSource($template,
  344. $nocache,
  345. $parent_compiler),
  346. $this->postFilter($this->blockOrFunctionCode) .
  347. join('', $this->mergedSubTemplatesCode),
  348. false,
  349. $this);
  350. return $_compiled_code;
  351. }
  352. /**
  353. * Compile template source and run optional post filter
  354. *
  355. * @param \Smarty_Internal_Template $template
  356. * @param null|bool $nocache flag if template must be compiled in nocache mode
  357. * @param \Smarty_Internal_TemplateCompilerBase $parent_compiler
  358. *
  359. * @return string
  360. * @throws \Exception
  361. */
  362. public function compileTemplateSource(Smarty_Internal_Template $template,
  363. $nocache = null,
  364. Smarty_Internal_TemplateCompilerBase $parent_compiler = null)
  365. {
  366. try {
  367. // save template object in compiler class
  368. $this->template = $template;
  369. if (property_exists($this->template->smarty, 'plugin_search_order')) {
  370. $this->plugin_search_order = $this->template->smarty->plugin_search_order;
  371. }
  372. if ($this->smarty->debugging) {
  373. if (!isset($this->smarty->_debug)) {
  374. $this->smarty->_debug = new Smarty_Internal_Debug();
  375. }
  376. $this->smarty->_debug->start_compile($this->template);
  377. }
  378. if (isset($this->template->smarty->security_policy)) {
  379. $this->php_handling = $this->template->smarty->security_policy->php_handling;
  380. } else {
  381. $this->php_handling = $this->template->smarty->php_handling;
  382. }
  383. $this->parent_compiler = $parent_compiler ? $parent_compiler : $this;
  384. $nocache = isset($nocache) ? $nocache : false;
  385. if (empty($template->compiled->nocache_hash)) {
  386. $template->compiled->nocache_hash = $this->nocache_hash;
  387. } else {
  388. $this->nocache_hash = $template->compiled->nocache_hash;
  389. }
  390. $this->caching = $template->caching;
  391. // flag for nocache sections
  392. $this->nocache = $nocache;
  393. $this->tag_nocache = false;
  394. // reset has nocache code flag
  395. $this->template->compiled->has_nocache_code = false;
  396. $this->has_variable_string = false;
  397. $this->prefix_code = array();
  398. // add file dependency
  399. if ($this->smarty->merge_compiled_includes || $this->template->source->handler->checkTimestamps()) {
  400. $this->parent_compiler->template->compiled->file_dependency[ $this->template->source->uid ] =
  401. array($this->template->source->filepath,
  402. $this->template->source->getTimeStamp(),
  403. $this->template->source->type,);
  404. }
  405. $this->smarty->_current_file = $this->template->source->filepath;
  406. // get template source
  407. if (!empty($this->template->source->components)) {
  408. // we have array of inheritance templates by extends: resource
  409. // generate corresponding source code sequence
  410. $_content =
  411. Smarty_Internal_Compile_Extends::extendsSourceArrayCode($this->template);
  412. } else {
  413. // get template source
  414. $_content = $this->template->source->getContent();
  415. }
  416. $_compiled_code = $this->postFilter($this->doCompile($this->preFilter($_content), true));
  417. }
  418. catch (Exception $e) {
  419. if ($this->smarty->debugging) {
  420. $this->smarty->_debug->end_compile($this->template);
  421. }
  422. $this->_tag_stack = array();
  423. // free memory
  424. $this->parent_compiler = null;
  425. $this->template = null;
  426. $this->parser = null;
  427. throw $e;
  428. }
  429. if ($this->smarty->debugging) {
  430. $this->smarty->_debug->end_compile($this->template);
  431. }
  432. $this->parent_compiler = null;
  433. $this->parser = null;
  434. return $_compiled_code;
  435. }
  436. /**
  437. * Optionally process compiled code by post filter
  438. *
  439. * @param string $code compiled code
  440. *
  441. * @return string
  442. * @throws \SmartyException
  443. */
  444. public function postFilter($code)
  445. {
  446. // run post filter if on code
  447. if (!empty($code) &&
  448. (isset($this->smarty->autoload_filters[ 'post' ]) || isset($this->smarty->registered_filters[ 'post' ]))
  449. ) {
  450. return $this->smarty->ext->_filterHandler->runFilter('post', $code, $this->template);
  451. } else {
  452. return $code;
  453. }
  454. }
  455. /**
  456. * Run optional prefilter
  457. *
  458. * @param string $_content template source
  459. *
  460. * @return string
  461. * @throws \SmartyException
  462. */
  463. public function preFilter($_content)
  464. {
  465. // run pre filter if required
  466. if ($_content !== '' &&
  467. ((isset($this->smarty->autoload_filters[ 'pre' ]) || isset($this->smarty->registered_filters[ 'pre' ])))
  468. ) {
  469. return $this->smarty->ext->_filterHandler->runFilter('pre', $_content, $this->template);
  470. } else {
  471. return $_content;
  472. }
  473. }
  474. /**
  475. * Compile Tag
  476. * This is a call back from the lexer/parser
  477. *
  478. * Save current prefix code
  479. * Compile tag
  480. * Merge tag prefix code with saved one
  481. * (required nested tags in attributes)
  482. *
  483. * @param string $tag tag name
  484. * @param array $args array with tag attributes
  485. * @param array $parameter array with compilation parameter
  486. *
  487. * @throws SmartyCompilerException
  488. * @throws SmartyException
  489. * @return string compiled code
  490. */
  491. public function compileTag($tag, $args, $parameter = array())
  492. {
  493. $this->prefixCodeStack[] = $this->prefix_code;
  494. $this->prefix_code = array();
  495. $result = $this->compileTag2($tag, $args, $parameter);
  496. $this->prefix_code = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
  497. return $result;
  498. }
  499. /**
  500. * compile variable
  501. *
  502. * @param string $variable
  503. *
  504. * @return string
  505. */
  506. public function compileVariable($variable)
  507. {
  508. if (!strpos($variable, '(')) {
  509. // not a variable variable
  510. $var = trim($variable, '\'');
  511. $this->tag_nocache = $this->tag_nocache |
  512. $this->template->ext->getTemplateVars->_getVariable($this->template,
  513. $var,
  514. null,
  515. true,
  516. false)->nocache;
  517. // todo $this->template->compiled->properties['variables'][$var] = $this->tag_nocache | $this->nocache;
  518. }
  519. return '$_smarty_tpl->tpl_vars[' . $variable . ']->value';
  520. }
  521. /**
  522. * compile config variable
  523. *
  524. * @param string $variable
  525. *
  526. * @return string
  527. */
  528. public function compileConfigVariable($variable)
  529. {
  530. // return '$_smarty_tpl->config_vars[' . $variable . ']';
  531. return '$_smarty_tpl->smarty->ext->configLoad->_getConfigVariable($_smarty_tpl, ' . $variable . ')';
  532. }
  533. /**
  534. * compile PHP function call
  535. *
  536. * @param string $name
  537. * @param array $parameter
  538. *
  539. * @return string
  540. * @throws \SmartyCompilerException
  541. */
  542. public function compilePHPFunctionCall($name, $parameter)
  543. {
  544. if (!$this->smarty->security_policy || $this->smarty->security_policy->isTrustedPhpFunction($name, $this)) {
  545. if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0 ||
  546. strcasecmp($name, 'array') === 0 || is_callable($name)
  547. ) {
  548. $func_name = strtolower($name);
  549. $par = implode(',', $parameter);
  550. $parHasFuction = strpos($par, '(') !== false;
  551. if ($func_name === 'isset') {
  552. if (count($parameter) === 0) {
  553. $this->trigger_template_error('Illegal number of parameter in "isset()"');
  554. }
  555. if ($parHasFuction) {
  556. $pa = array();
  557. foreach ($parameter as $p) {
  558. $pa[] = (strpos($p, '(') === false) ? ('isset(' . $p . ')') : ('(' . $p . ' !== null )');
  559. }
  560. return '(' . implode(' && ', $pa) . ')';
  561. } else {
  562. $isset_par = str_replace("')->value", "',null,true,false)->value", $par);
  563. }
  564. return $name . '(' . $isset_par . ')';
  565. } else if (in_array($func_name,
  566. array('empty',
  567. 'reset',
  568. 'current',
  569. 'end',
  570. 'prev',
  571. 'next'))) {
  572. if (count($parameter) !== 1) {
  573. $this->trigger_template_error("Illegal number of parameter in '{$func_name()}'");
  574. }
  575. if ($func_name === 'empty') {
  576. if ($parHasFuction && version_compare(PHP_VERSION, '5.5.0', '<')) {
  577. return '(' . $parameter[ 0 ] . ' === false )';
  578. } else {
  579. return $func_name . '(' .
  580. str_replace("')->value", "',null,true,false)->value", $parameter[ 0 ]) . ')';
  581. }
  582. } else {
  583. return $func_name . '(' . $parameter[ 0 ] . ')';
  584. }
  585. } else {
  586. return $name . '(' . implode(',', $parameter) . ')';
  587. }
  588. } else {
  589. $this->trigger_template_error("unknown function '{$name}'");
  590. }
  591. }
  592. }
  593. /**
  594. * This method is called from parser to process a text content section
  595. * - remove text from inheritance child templates as they may generate output
  596. * - strip text if strip is enabled
  597. *
  598. * @param string $text
  599. *
  600. * @return null|\Smarty_Internal_ParseTree_Text
  601. */
  602. public function processText($text)
  603. {
  604. if ((string)$text !== '') {
  605. $store = array();
  606. $_store = 0;
  607. if ($this->parser->strip) {
  608. if (strpos($text, '<') !== false) {
  609. // capture html elements not to be messed with
  610. $_offset = 0;
  611. if (preg_match_all('#(<script[^>]*>.*?</script[^>]*>)|(<textarea[^>]*>.*?</textarea[^>]*>)|(<pre[^>]*>.*?</pre[^>]*>)#is',
  612. $text,
  613. $matches,
  614. PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
  615. foreach ($matches as $match) {
  616. $store[] = $match[ 0 ][ 0 ];
  617. $_length = strlen($match[ 0 ][ 0 ]);
  618. $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@';
  619. $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length);
  620. $_offset += $_length - strlen($replace);
  621. ++$_store;
  622. }
  623. }
  624. $expressions = array(// replace multiple spaces between tags by a single space
  625. '#(:SMARTY@!@|>)[\040\011]+(?=@!@SMARTY:|<)#s' => '\1 \2',
  626. // remove newline between tags
  627. '#(:SMARTY@!@|>)[\040\011]*[\n]\s*(?=@!@SMARTY:|<)#s' => '\1\2',
  628. // remove multiple spaces between attributes (but not in attribute values!)
  629. '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5',
  630. '#>[\040\011]+$#Ss' => '> ',
  631. '#>[\040\011]*[\n]\s*$#Ss' => '>',
  632. $this->stripRegEx => '',);
  633. $text = preg_replace(array_keys($expressions), array_values($expressions), $text);
  634. $_offset = 0;
  635. if (preg_match_all('#@!@SMARTY:([0-9]+):SMARTY@!@#is',
  636. $text,
  637. $matches,
  638. PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
  639. foreach ($matches as $match) {
  640. $_length = strlen($match[ 0 ][ 0 ]);
  641. $replace = $store[ $match[ 1 ][ 0 ] ];
  642. $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length);
  643. $_offset += strlen($replace) - $_length;
  644. ++$_store;
  645. }
  646. }
  647. } else {
  648. $text = preg_replace($this->stripRegEx, '', $text);
  649. }
  650. }
  651. return new Smarty_Internal_ParseTree_Text($text);
  652. }
  653. return null;
  654. }
  655. /**
  656. * lazy loads internal compile plugin for tag and calls the compile method
  657. * compile objects cached for reuse.
  658. * class name format: Smarty_Internal_Compile_TagName
  659. * plugin filename format: Smarty_Internal_TagName.php
  660. *
  661. * @param string $tag tag name
  662. * @param array $args list of tag attributes
  663. * @param mixed $param1 optional parameter
  664. * @param mixed $param2 optional parameter
  665. * @param mixed $param3 optional parameter
  666. *
  667. * @return bool|string compiled code or false
  668. * @throws \SmartyCompilerException
  669. */
  670. public function callTagCompiler($tag, $args, $param1 = null, $param2 = null, $param3 = null)
  671. {
  672. /* @var Smarty_Internal_CompileBase $tagCompiler */
  673. $tagCompiler = $this->getTagCompiler($tag);
  674. // compile this tag
  675. return $tagCompiler === false ? false : $tagCompiler->compile($args, $this, $param1, $param2, $param3);
  676. }
  677. /**
  678. * lazy loads internal compile plugin for tag compile objects cached for reuse.
  679. *
  680. * class name format: Smarty_Internal_Compile_TagName
  681. * plugin filename format: Smarty_Internal_TagName.php
  682. *
  683. * @param string $tag tag name
  684. *
  685. * @return bool|\Smarty_Internal_CompileBase tag compiler object or false if not found
  686. * @throws \SmartyCompilerException
  687. */
  688. public function getTagCompiler($tag)
  689. {
  690. // re-use object if already exists
  691. if (!isset(self::$_tag_objects[ $tag ])) {
  692. // lazy load internal compiler plugin
  693. $_tag = explode('_', $tag);
  694. $_tag = array_map('ucfirst', $_tag);
  695. $class_name = 'Smarty_Internal_Compile_' . implode('_', $_tag);
  696. if (class_exists($class_name) &&
  697. (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))
  698. ) {
  699. self::$_tag_objects[ $tag ] = new $class_name;
  700. } else {
  701. self::$_tag_objects[ $tag ] = false;
  702. }
  703. }
  704. return self::$_tag_objects[ $tag ];
  705. }
  706. /**
  707. * Check for plugins and return function name
  708. *
  709. * @param $plugin_name
  710. * @param string $plugin_type type of plugin
  711. *
  712. * @return string call name of function
  713. * @throws \SmartyException
  714. */
  715. public function getPlugin($plugin_name, $plugin_type)
  716. {
  717. $function = null;
  718. if ($this->caching && ($this->nocache || $this->tag_nocache)) {
  719. if (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
  720. $function =
  721. $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  722. } else if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
  723. $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ] =
  724. $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ];
  725. $function =
  726. $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  727. }
  728. } else {
  729. if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ])) {
  730. $function =
  731. $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  732. } else if (isset($this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ])) {
  733. $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ] =
  734. $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ];
  735. $function =
  736. $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ];
  737. }
  738. }
  739. if (isset($function)) {
  740. if ($plugin_type === 'modifier') {
  741. $this->modifier_plugins[ $plugin_name ] = true;
  742. }
  743. return $function;
  744. }
  745. // loop through plugin dirs and find the plugin
  746. $function = 'smarty_' . $plugin_type . '_' . $plugin_name;
  747. $file = $this->smarty->loadPlugin($function, false);
  748. if (is_string($file)) {
  749. if ($this->caching && ($this->nocache || $this->tag_nocache)) {
  750. $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
  751. $file;
  752. $this->required_plugins[ 'nocache' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
  753. $function;
  754. } else {
  755. $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'file' ] =
  756. $file;
  757. $this->required_plugins[ 'compiled' ][ $plugin_name ][ $plugin_type ][ 'function' ] =
  758. $function;
  759. }
  760. if ($plugin_type === 'modifier') {
  761. $this->modifier_plugins[ $plugin_name ] = true;
  762. }
  763. return $function;
  764. }
  765. if (is_callable($function)) {
  766. // plugin function is defined in the script
  767. return $function;
  768. }
  769. return false;
  770. }
  771. /**
  772. * Check for plugins by default plugin handler
  773. *
  774. * @param string $tag name of tag
  775. * @param string $plugin_type type of plugin
  776. *
  777. * @return bool true if found
  778. * @throws \SmartyCompilerException
  779. */
  780. public function getPluginFromDefaultHandler($tag, $plugin_type)
  781. {
  782. $callback = null;
  783. $script = null;
  784. $cacheable = true;
  785. $result = call_user_func_array($this->smarty->default_plugin_handler_func,
  786. array($tag,
  787. $plugin_type,
  788. $this->template,
  789. &$callback,
  790. &$script,
  791. &$cacheable,));
  792. if ($result) {
  793. $this->tag_nocache = $this->tag_nocache || !$cacheable;
  794. if ($script !== null) {
  795. if (is_file($script)) {
  796. if ($this->caching && ($this->nocache || $this->tag_nocache)) {
  797. $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'file' ] =
  798. $script;
  799. $this->required_plugins[ 'nocache' ][ $tag ][ $plugin_type ][ 'function' ] =
  800. $callback;
  801. } else {
  802. $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'file' ] =
  803. $script;
  804. $this->required_plugins[ 'compiled' ][ $tag ][ $plugin_type ][ 'function' ] =
  805. $callback;
  806. }
  807. require_once $script;
  808. } else {
  809. $this->trigger_template_error("Default plugin handler: Returned script file '{$script}' for '{$tag}' not found");
  810. }
  811. }
  812. if (is_callable($callback)) {
  813. $this->default_handler_plugins[ $plugin_type ][ $tag ] = array($callback,
  814. true,
  815. array());
  816. return true;
  817. } else {
  818. $this->trigger_template_error("Default plugin handler: Returned callback for '{$tag}' not callable");
  819. }
  820. }
  821. return false;
  822. }
  823. /**
  824. * Append code segments and remove unneeded ?> <?php transitions
  825. *
  826. * @param string $left
  827. * @param string $right
  828. *
  829. * @return string
  830. */
  831. public function appendCode($left, $right)
  832. {
  833. if (preg_match('/\s*\?>\s?$/D', $left) && preg_match('/^<\?php\s+/', $right)) {
  834. $left = preg_replace('/\s*\?>\s?$/D', "\n", $left);
  835. $left .= preg_replace('/^<\?php\s+/', '', $right);
  836. } else {
  837. $left .= $right;
  838. }
  839. return $left;
  840. }
  841. /**
  842. * Inject inline code for nocache template sections
  843. * This method gets the content of each template element from the parser.
  844. * If the content is compiled code and it should be not cached the code is injected
  845. * into the rendered output.
  846. *
  847. * @param string $content content of template element
  848. * @param boolean $is_code true if content is compiled code
  849. *
  850. * @return string content
  851. */
  852. public function processNocacheCode($content, $is_code)
  853. {
  854. // If the template is not evaluated and we have a nocache section and or a nocache tag
  855. if ($is_code && !empty($content)) {
  856. // generate replacement code
  857. if ((!($this->template->source->handler->recompiled) || $this->forceNocache) && $this->caching &&
  858. !$this->suppressNocacheProcessing && ($this->nocache || $this->tag_nocache)
  859. ) {
  860. $this->template->compiled->has_nocache_code = true;
  861. $_output = addcslashes($content, '\'\\');
  862. $_output = str_replace('^#^', '\'', $_output);
  863. $_output = "<?php echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/{$_output}/*/%%SmartyNocache:{$this->nocache_hash}%%*/';?>\n";
  864. // make sure we include modifier plugins for nocache code
  865. foreach ($this->modifier_plugins as $plugin_name => $dummy) {
  866. if (isset($this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ])) {
  867. $this->required_plugins[ 'nocache' ][ $plugin_name ][ 'modifier' ] =
  868. $this->required_plugins[ 'compiled' ][ $plugin_name ][ 'modifier' ];
  869. }
  870. }
  871. } else {
  872. $_output = $content;
  873. }
  874. } else {
  875. $_output = $content;
  876. }
  877. $this->modifier_plugins = array();
  878. $this->suppressNocacheProcessing = false;
  879. $this->tag_nocache = false;
  880. return $_output;
  881. }
  882. /**
  883. * Get Id
  884. *
  885. * @param string $input
  886. *
  887. * @return bool|string
  888. */
  889. public function getId($input)
  890. {
  891. if (preg_match('~^([\'"]*)([0-9]*[a-zA-Z_]\w*)\1$~', $input, $match)) {
  892. return $match[ 2 ];
  893. }
  894. return false;
  895. }
  896. /**
  897. * Get variable name from string
  898. *
  899. * @param string $input
  900. *
  901. * @return bool|string
  902. */
  903. public function getVariableName($input)
  904. {
  905. if (preg_match('~^[$]_smarty_tpl->tpl_vars\[[\'"]*([0-9]*[a-zA-Z_]\w*)[\'"]*\]->value$~', $input, $match)) {
  906. return $match[ 1 ];
  907. }
  908. return false;
  909. }
  910. /**
  911. * Set nocache flag in variable or create new variable
  912. *
  913. * @param string $varName
  914. */
  915. public function setNocacheInVariable($varName)
  916. {
  917. // create nocache var to make it know for further compiling
  918. if ($_var = $this->getId($varName)) {
  919. if (isset($this->template->tpl_vars[ $_var ])) {
  920. $this->template->tpl_vars[ $_var ] = clone $this->template->tpl_vars[ $_var ];
  921. $this->template->tpl_vars[ $_var ]->nocache = true;
  922. } else {
  923. $this->template->tpl_vars[ $_var ] = new Smarty_Variable(null, true);
  924. }
  925. }
  926. }
  927. /**
  928. * @param array $_attr tag attributes
  929. * @param array $validScopes
  930. *
  931. * @return int|string
  932. * @throws \SmartyCompilerException
  933. */
  934. public function convertScope($_attr, $validScopes)
  935. {
  936. $_scope = 0;
  937. if (isset($_attr[ 'scope' ])) {
  938. $_scopeName = trim($_attr[ 'scope' ], '\'"');
  939. if (is_numeric($_scopeName) && in_array($_scopeName, $validScopes)) {
  940. $_scope = $_scopeName;
  941. } else if (is_string($_scopeName)) {
  942. $_scopeName = trim($_scopeName, '\'"');
  943. $_scope = isset($validScopes[ $_scopeName ]) ? $validScopes[ $_scopeName ] : false;
  944. } else {
  945. $_scope = false;
  946. }
  947. if ($_scope === false) {
  948. $err = var_export($_scopeName, true);
  949. $this->trigger_template_error("illegal value '{$err}' for \"scope\" attribute", null, true);
  950. }
  951. }
  952. return $_scope;
  953. }
  954. /**
  955. * Generate nocache code string
  956. *
  957. * @param string $code PHP code
  958. *
  959. * @return string
  960. */
  961. public function makeNocacheCode($code)
  962. {
  963. return "echo '/*%%SmartyNocache:{$this->nocache_hash}%%*/<?php " .
  964. str_replace('^#^', '\'', addcslashes($code, '\'\\')) .
  965. "?>/*/%%SmartyNocache:{$this->nocache_hash}%%*/';\n";
  966. }
  967. /**
  968. * display compiler error messages without dying
  969. * If parameter $args is empty it is a parser detected syntax error.
  970. * In this case the parser is called to obtain information about expected tokens.
  971. * If parameter $args contains a string this is used as error message
  972. *
  973. * @param string $args individual error message or null
  974. * @param string $line line-number
  975. * @param null|bool $tagline if true the line number of last tag
  976. *
  977. * @throws \SmartyCompilerException when an unexpected token is found
  978. */
  979. public function trigger_template_error($args = null, $line = null, $tagline = null)
  980. {
  981. $lex = $this->parser->lex;
  982. if ($tagline === true) {
  983. // get line number of Tag
  984. $line = $lex->taglineno;
  985. } else if (!isset($line)) {
  986. // get template source line which has error
  987. $line = $lex->line;
  988. } else {
  989. $line = (int)$line;
  990. }
  991. if (in_array($this->template->source->type,
  992. array('eval',
  993. 'string'))) {
  994. $templateName = $this->template->source->type . ':' . trim(preg_replace('![\t\r\n]+!',
  995. ' ',
  996. strlen($lex->data) > 40 ?
  997. substr($lex->data, 0, 40) .
  998. '...' : $lex->data));
  999. } else {
  1000. $templateName = $this->template->source->type . ':' . $this->template->source->filepath;
  1001. }
  1002. // $line += $this->trace_line_offset;
  1003. $match = preg_split("/\n/", $lex->data);
  1004. $error_text =
  1005. 'Syntax error in template "' . (empty($this->trace_filepath) ? $templateName : $this->trace_filepath) .
  1006. '" on line ' . ($line + $this->trace_line_offset) . ' "' .
  1007. trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ])) . '" ';
  1008. if (isset($args)) {
  1009. // individual error message
  1010. $error_text .= $args;
  1011. } else {
  1012. $expect = array();
  1013. // expected token from parser
  1014. $error_text .= ' - Unexpected "' . $lex->value . '"';
  1015. if (count($this->parser->yy_get_expected_tokens($this->parser->yymajor)) <= 4) {
  1016. foreach ($this->parser->yy_get_expected_tokens($this->parser->yymajor) as $token) {
  1017. $exp_token = $this->parser->yyTokenName[ $token ];
  1018. if (isset($lex->smarty_token_names[ $exp_token ])) {
  1019. // token type from lexer
  1020. $expect[] = '"' . $lex->smarty_token_names[ $exp_token ] . '"';
  1021. } else {
  1022. // otherwise internal token name
  1023. $expect[] = $this->parser->yyTokenName[ $token ];
  1024. }
  1025. }
  1026. $error_text .= ', expected one of: ' . implode(' , ', $expect);
  1027. }
  1028. }
  1029. if ($this->smarty->_parserdebug) {
  1030. $this->parser->errorRunDown();
  1031. echo ob_get_clean();
  1032. flush();
  1033. }
  1034. $e = new SmartyCompilerException($error_text);
  1035. $e->line = $line;
  1036. $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ]));
  1037. $e->desc = $args;
  1038. $e->template = $this->template->source->filepath;
  1039. throw $e;
  1040. }
  1041. /**
  1042. * Return var_export() value with all white spaces removed
  1043. *
  1044. * @param mixed $value
  1045. *
  1046. * @return string
  1047. */
  1048. public function getVarExport($value)
  1049. {
  1050. return preg_replace('/\s/', '', var_export($value, true));
  1051. }
  1052. /**
  1053. * enter double quoted string
  1054. * - save tag stack count
  1055. */
  1056. public function enterDoubleQuote()
  1057. {
  1058. array_push($this->_tag_stack_count, $this->getTagStackCount());
  1059. }
  1060. /**
  1061. * Return tag stack count
  1062. *
  1063. * @return int
  1064. */
  1065. public function getTagStackCount()
  1066. {
  1067. return count($this->_tag_stack);
  1068. }
  1069. /**
  1070. * @param $lexerPreg
  1071. *
  1072. * @return mixed
  1073. */
  1074. public function replaceDelimiter($lexerPreg)
  1075. {
  1076. return str_replace(array('SMARTYldel', 'SMARTYliteral', 'SMARTYrdel', 'SMARTYautoliteral', 'SMARTYal'),
  1077. array($this->ldelPreg, $this->literalPreg, $this->rdelPreg,
  1078. $this->smarty->getAutoLiteral() ? '{1,}' : '{9}',
  1079. $this->smarty->getAutoLiteral() ? '' : '\\s*'),
  1080. $lexerPreg);
  1081. }
  1082. /**
  1083. * Build lexer regular expressions for left and right delimiter and user defined literals
  1084. */
  1085. public function initDelimiterPreg()
  1086. {
  1087. $ldel = $this->smarty->getLeftDelimiter();
  1088. $this->ldelLength = strlen($ldel);
  1089. $this->ldelPreg = '';
  1090. foreach (str_split($ldel, 1) as $chr) {
  1091. $this->ldelPreg .= '[' . ($chr === '\\' ? '\\' : '') . $chr . ']';
  1092. }
  1093. $rdel = $this->smarty->getRightDelimiter();
  1094. $this->rdelLength = strlen($rdel);
  1095. $this->rdelPreg = '';
  1096. foreach (str_split($rdel, 1) as $chr) {
  1097. $this->rdelPreg .= '[' . ($chr === '\\' ? '\\' : '') . $chr . ']';
  1098. }
  1099. $literals = $this->smarty->getLiterals();
  1100. if (!empty($literals)) {
  1101. foreach ($literals as $key => $literal) {
  1102. $literalPreg = '';
  1103. foreach (str_split($literal, 1) as $chr) {
  1104. $literalPreg .= '[' . ($chr === '\\' ? '\\' : '') . $chr . ']';
  1105. }
  1106. $literals[ $key ] = $literalPreg;
  1107. }
  1108. $this->literalPreg = '|' . implode('|', $literals);
  1109. } else {
  1110. $this->literalPreg = '';
  1111. }
  1112. }
  1113. /**
  1114. * leave double quoted string
  1115. * - throw exception if block in string was not closed
  1116. *
  1117. * @throws \SmartyCompilerException
  1118. */
  1119. public function leaveDoubleQuote()
  1120. {
  1121. if (array_pop($this->_tag_stack_count) !== $this->getTagStackCount()) {
  1122. $tag = $this->getOpenBlockTag();
  1123. $this->trigger_template_error("unclosed '{{$tag}}' in doubled quoted string",
  1124. null,
  1125. true);
  1126. }
  1127. }
  1128. /**
  1129. * Get left delimiter preg
  1130. *
  1131. * @return string
  1132. */
  1133. public function getLdelPreg()
  1134. {
  1135. return $this->ldelPreg;
  1136. }
  1137. /**
  1138. * Get right delimiter preg
  1139. *
  1140. * @return string
  1141. */
  1142. public function getRdelPreg()
  1143. {
  1144. return $this->rdelPreg;
  1145. }
  1146. /**
  1147. * Get length of left delimiter
  1148. *
  1149. * @return int
  1150. */
  1151. public function getLdelLength()
  1152. {
  1153. return $this->ldelLength;
  1154. }
  1155. /**
  1156. * Get length of right delimiter
  1157. *
  1158. * @return int
  1159. */
  1160. public function getRdelLength()
  1161. {
  1162. return $this->rdelLength;
  1163. }
  1164. /**
  1165. * Get name of current open block tag
  1166. *
  1167. * @return string|boolean
  1168. */
  1169. public function getOpenBlockTag()
  1170. {
  1171. $tagCount = $this->getTagStackCount();
  1172. if ($tagCount) {
  1173. return $this->_tag_stack[ $tagCount - 1 ][ 0 ];
  1174. } else {
  1175. return false;
  1176. }
  1177. }
  1178. /**
  1179. * Check if $value contains variable elements
  1180. *
  1181. * @param mixed $value
  1182. *
  1183. * @return bool|int
  1184. */
  1185. public function isVariable($value)
  1186. {
  1187. if (is_string($value)) {
  1188. return preg_match('/[$(]/', $value);
  1189. }
  1190. if (is_bool($value) || is_numeric($value)) {
  1191. return false;
  1192. }
  1193. if (is_array($value)) {
  1194. foreach ($value as $k => $v) {
  1195. if ($this->isVariable($k) || $this->isVariable($v)) {
  1196. return true;
  1197. }
  1198. }
  1199. return false;
  1200. }
  1201. return false;
  1202. }
  1203. /**
  1204. * Get new prefix variable name
  1205. *
  1206. * @return string
  1207. */
  1208. public function getNewPrefixVariable()
  1209. {
  1210. ++self::$prefixVariableNumber;
  1211. return $this->getPrefixVariable();
  1212. }
  1213. /**
  1214. * Get current prefix variable name
  1215. *
  1216. * @return string
  1217. */
  1218. public function getPrefixVariable()
  1219. {
  1220. return '$_prefixVariable' . self::$prefixVariableNumber;
  1221. }
  1222. /**
  1223. * append code to prefix buffer
  1224. *
  1225. * @param string $code
  1226. */
  1227. public function appendPrefixCode($code)
  1228. {
  1229. $this->prefix_code[] = $code;
  1230. }
  1231. /**
  1232. * get prefix code string
  1233. *
  1234. * @return string
  1235. */
  1236. public function getPrefixCode()
  1237. {
  1238. $code = '';
  1239. $prefixArray = array_merge($this->prefix_code, array_pop($this->prefixCodeStack));
  1240. $this->prefixCodeStack[] = array();
  1241. foreach ($prefixArray as $c) {
  1242. $code = $this->appendCode($code, $c);
  1243. }
  1244. $this->prefix_code = array();
  1245. return $code;
  1246. }
  1247. /**
  1248. * Save current required plugins
  1249. *
  1250. * @param bool $init if true init required plugins
  1251. */
  1252. public function saveRequiredPlugins($init=false)
  1253. {
  1254. $this->required_plugins_stack[] = $this->required_plugins;
  1255. if ($init) {
  1256. $this->required_plugins = array('compiled' => array(), 'nocache' => array());
  1257. }
  1258. }
  1259. /**
  1260. * Restore required plugins
  1261. */
  1262. public function restoreRequiredPlugins()
  1263. {
  1264. $this->required_plugins = array_pop($this->required_plugins_stack);
  1265. }
  1266. /**
  1267. * Compile code to call Smarty_Internal_Template::_checkPlugins()
  1268. * for required plugins
  1269. *
  1270. * @return string
  1271. */
  1272. public function compileRequiredPlugins()
  1273. {
  1274. $code = $this->compileCheckPlugins($this->required_plugins[ 'compiled' ]);
  1275. if ($this->caching && !empty($this->required_plugins[ 'nocache' ])) {
  1276. $code .= $this->makeNocacheCode($this->compileCheckPlugins($this->required_plugins[ 'nocache' ]));
  1277. }
  1278. return $code;
  1279. }
  1280. /**
  1281. * Compile code to call Smarty_Internal_Template::_checkPlugins
  1282. * - checks if plugin is callable require otherwise
  1283. *
  1284. * @param $requiredPlugins
  1285. *
  1286. * @return string
  1287. */
  1288. public function compileCheckPlugins($requiredPlugins)
  1289. {
  1290. if (!empty($requiredPlugins)) {
  1291. $plugins = array();
  1292. foreach ($requiredPlugins as $plugin) {
  1293. foreach ($plugin as $data) {
  1294. $plugins[] = $data;
  1295. }
  1296. }
  1297. return '$_smarty_tpl->_checkPlugins(' . $this->getVarExport($plugins) . ');' . "\n";
  1298. } else {
  1299. return '';
  1300. }
  1301. }
  1302. /**
  1303. * method to compile a Smarty template
  1304. *
  1305. * @param mixed $_content template source
  1306. * @param bool $isTemplateSource
  1307. *
  1308. * @return bool true if compiling succeeded, false if it failed
  1309. */
  1310. abstract protected function doCompile($_content, $isTemplateSource = false);
  1311. /**
  1312. * Compile Tag
  1313. *
  1314. * @param string $tag tag name
  1315. * @param array $args array with tag attributes
  1316. * @param array $parameter array with compilation parameter
  1317. *
  1318. * @throws SmartyCompilerException
  1319. * @throws SmartyException
  1320. * @return string compiled code
  1321. */
  1322. private function compileTag2($tag, $args, $parameter)
  1323. {
  1324. $plugin_type = '';
  1325. // $args contains the attributes parsed and compiled by the lexer/parser
  1326. // assume that tag does compile into code, but creates no HTML output
  1327. $this->has_code = true;
  1328. // log tag/attributes
  1329. if (isset($this->smarty->_cache[ 'get_used_tags' ])) {
  1330. $this->template->_cache[ 'used_tags' ][] = array($tag,
  1331. $args);
  1332. }
  1333. // check nocache option flag
  1334. foreach ($args as $arg) {
  1335. if (!is_array($arg)) {
  1336. if ($arg === "'nocache'" || $arg === 'nocache') {
  1337. $this->tag_nocache = true;
  1338. }
  1339. } else {
  1340. foreach ($arg as $k => $v) {
  1341. if (($k === "'nocache'" || $k === 'nocache') && (trim($v, "'\" ") === 'true')) {
  1342. $this->tag_nocache = true;
  1343. }
  1344. }
  1345. }
  1346. }
  1347. // compile the smarty tag (required compile classes to compile the tag are auto loaded)
  1348. if (($_output = $this->callTagCompiler($tag, $args, $parameter)) === false) {
  1349. if (isset($this->parent_compiler->tpl_function[ $tag ]) ||
  1350. (isset ($this->template->smarty->ext->_tplFunction) &&
  1351. $this->template->smarty->ext->_tplFunction->getTplFunction($this->template, $tag) !== false)
  1352. ) {
  1353. // template defined by {template} tag
  1354. $args[ '_attr' ][ 'name' ] = "'{$tag}'";
  1355. $_output = $this->callTagCompiler('call', $args, $parameter);
  1356. }
  1357. }
  1358. if ($_output !== false) {
  1359. if ($_output !== true) {
  1360. // did we get compiled code
  1361. if ($this->has_code) {
  1362. // return compiled code
  1363. return $_output;
  1364. }
  1365. }
  1366. // tag did not produce compiled code
  1367. return null;
  1368. } else {
  1369. // map_named attributes
  1370. if (isset($args[ '_attr' ])) {
  1371. foreach ($args[ '_attr' ] as $key => $attribute) {
  1372. if (is_array($attribute)) {
  1373. $args = array_merge($args, $attribute);
  1374. }
  1375. }
  1376. }
  1377. // not an internal compiler tag
  1378. if (strlen($tag) < 6 || substr($tag, -5) !== 'close') {
  1379. // check if tag is a registered object
  1380. if (isset($this->smarty->registered_objects[ $tag ]) && isset($parameter[ 'object_method' ])) {
  1381. $method = $parameter[ 'object_method' ];
  1382. if (!in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ]) &&
  1383. (empty($this->smarty->registered_objects[ $tag ][ 1 ]) ||
  1384. in_array($method, $this->smarty->registered_objects[ $tag ][ 1 ]))
  1385. ) {
  1386. return $this->callTagCompiler('private_object_function', $args, $parameter, $tag, $method);
  1387. } else if (in_array($method, $this->smarty->registered_objects[ $tag ][ 3 ])) {
  1388. return $this->callTagCompiler('private_object_block_function',
  1389. $args,
  1390. $parameter,
  1391. $tag,
  1392. $method);
  1393. } else {
  1394. // throw exception
  1395. $this->trigger_template_error('not allowed method "' . $method . '" in registered object "' .
  1396. $tag . '"',
  1397. null,
  1398. true);
  1399. }
  1400. }
  1401. // check if tag is registered
  1402. foreach (array(Smarty::PLUGIN_COMPILER,
  1403. Smarty::PLUGIN_FUNCTION,
  1404. Smarty::PLUGIN_BLOCK,) as $plugin_type) {
  1405. if (isset($this->smarty->registered_plugins[ $plugin_type ][ $tag ])) {
  1406. // if compiler function plugin call it now
  1407. if ($plugin_type === Smarty::PLUGIN_COMPILER) {
  1408. $new_args = array();
  1409. foreach ($args as $key => $mixed) {
  1410. if (is_array($mixed)) {
  1411. $new_args = array_merge($new_args, $mixed);
  1412. } else {
  1413. $new_args[ $key ] = $mixed;
  1414. }
  1415. }
  1416. if (!$this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 1 ]) {
  1417. $this->tag_nocache = true;
  1418. }
  1419. return call_user_func_array($this->smarty->registered_plugins[ $plugin_type ][ $tag ][ 0 ],
  1420. array($new_args,
  1421. $this));
  1422. }
  1423. // compile registered function or block function
  1424. if ($plugin_type === Smarty::PLUGIN_FUNCTION || $plugin_type === Smarty::PLUGIN_BLOCK) {
  1425. return $this->callTagCompiler('private_registered_' . $plugin_type,
  1426. $args,
  1427. $parameter,
  1428. $tag);
  1429. }
  1430. }
  1431. }
  1432. // check plugins from plugins folder
  1433. foreach ($this->plugin_search_order as $plugin_type) {
  1434. if ($plugin_type === Smarty::PLUGIN_COMPILER &&
  1435. $this->smarty->loadPlugin('smarty_compiler_' . $tag) &&
  1436. (!isset($this->smarty->security_policy) ||
  1437. $this->smarty->security_policy->isTrustedTag($tag, $this))
  1438. ) {
  1439. $plugin = 'smarty_compiler_' . $tag;
  1440. if (is_callable($plugin)) {
  1441. // convert arguments format for old compiler plugins
  1442. $new_args = array();
  1443. foreach ($args as $key => $mixed) {
  1444. if (is_array($mixed)) {
  1445. $new_args = array_merge($new_args, $mixed);
  1446. } else {
  1447. $new_args[ $key ] = $mixed;
  1448. }
  1449. }
  1450. return $plugin($new_args, $this->smarty);
  1451. }
  1452. if (class_exists($plugin, false)) {
  1453. $plugin_object = new $plugin;
  1454. if (method_exists($plugin_object, 'compile')) {
  1455. return $plugin_object->compile($args, $this);
  1456. }
  1457. }
  1458. throw new SmartyException("Plugin '{$tag}' not callable");
  1459. } else {
  1460. if ($function = $this->getPlugin($tag, $plugin_type)) {
  1461. if (!isset($this->smarty->security_policy) ||
  1462. $this->smarty->security_policy->isTrustedTag($tag, $this)
  1463. ) {
  1464. return $this->callTagCompiler('private_' . $plugin_type . '_plugin',
  1465. $args,
  1466. $parameter,
  1467. $tag,
  1468. $function);
  1469. }
  1470. }
  1471. }
  1472. }
  1473. if (is_callable($this->smarty->default_plugin_handler_func)) {
  1474. $found = false;
  1475. // look for already resolved tags
  1476. foreach ($this->plugin_search_order as $plugin_type) {
  1477. if (isset($this->default_handler_plugins[ $plugin_type ][ $tag ])) {
  1478. $found = true;
  1479. break;
  1480. }
  1481. }
  1482. if (!$found) {
  1483. // call default handler
  1484. foreach ($this->plugin_search_order as $plugin_type) {
  1485. if ($this->getPluginFromDefaultHandler($tag, $plugin_type)) {
  1486. $found = true;
  1487. break;
  1488. }
  1489. }
  1490. }
  1491. if ($found) {
  1492. // if compiler function plugin call it now
  1493. if ($plugin_type === Smarty::PLUGIN_COMPILER) {
  1494. $new_args = array();
  1495. foreach ($args as $key => $mixed) {
  1496. if (is_array($mixed)) {
  1497. $new_args = array_merge($new_args, $mixed);
  1498. } else {
  1499. $new_args[ $key ] = $mixed;
  1500. }
  1501. }
  1502. return call_user_func_array($this->default_handler_plugins[ $plugin_type ][ $tag ][ 0 ],
  1503. array($new_args,
  1504. $this));
  1505. } else {
  1506. return $this->callTagCompiler('private_registered_' . $plugin_type,
  1507. $args,
  1508. $parameter,
  1509. $tag);
  1510. }
  1511. }
  1512. }
  1513. } else {
  1514. // compile closing tag of block function
  1515. $base_tag = substr($tag, 0, -5);
  1516. // check if closing tag is a registered object
  1517. if (isset($this->smarty->registered_objects[ $base_tag ]) && isset($parameter[ 'object_method' ])) {
  1518. $method = $parameter[ 'object_method' ];
  1519. if (in_array($method, $this->smarty->registered_objects[ $base_tag ][ 3 ])) {
  1520. return $this->callTagCompiler('private_object_block_function',
  1521. $args,
  1522. $parameter,
  1523. $tag,
  1524. $method);
  1525. } else {
  1526. // throw exception
  1527. $this->trigger_template_error('not allowed closing tag method "' . $method .
  1528. '" in registered object "' . $base_tag . '"',
  1529. null,
  1530. true);
  1531. }
  1532. }
  1533. // registered block tag ?
  1534. if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ]) ||
  1535. isset($this->default_handler_plugins[ Smarty::PLUGIN_BLOCK ][ $base_tag ])
  1536. ) {
  1537. return $this->callTagCompiler('private_registered_block', $args, $parameter, $tag);
  1538. }
  1539. // registered function tag ?
  1540. if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_FUNCTION ][ $tag ])) {
  1541. return $this->callTagCompiler('private_registered_function', $args, $parameter, $tag);
  1542. }
  1543. // block plugin?
  1544. if ($function = $this->getPlugin($base_tag, Smarty::PLUGIN_BLOCK)) {
  1545. return $this->callTagCompiler('private_block_plugin', $args, $parameter, $tag, $function);
  1546. }
  1547. // function plugin?
  1548. if ($function = $this->getPlugin($tag, Smarty::PLUGIN_FUNCTION)) {
  1549. if (!isset($this->smarty->security_policy) ||
  1550. $this->smarty->security_policy->isTrustedTag($tag, $this)
  1551. ) {
  1552. return $this->callTagCompiler('private_function_plugin', $args, $parameter, $tag, $function);
  1553. }
  1554. }
  1555. // registered compiler plugin ?
  1556. if (isset($this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ])) {
  1557. // if compiler function plugin call it now
  1558. $args = array();
  1559. if (!$this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 1 ]) {
  1560. $this->tag_nocache = true;
  1561. }
  1562. return call_user_func_array($this->smarty->registered_plugins[ Smarty::PLUGIN_COMPILER ][ $tag ][ 0 ],
  1563. array($args,
  1564. $this));
  1565. }
  1566. if ($this->smarty->loadPlugin('smarty_compiler_' . $tag)) {
  1567. $plugin = 'smarty_compiler_' . $tag;
  1568. if (is_callable($plugin)) {
  1569. return $plugin($args, $this->smarty);
  1570. }
  1571. if (class_exists($plugin, false)) {
  1572. $plugin_object = new $plugin;
  1573. if (method_exists($plugin_object, 'compile')) {
  1574. return $plugin_object->compile($args, $this);
  1575. }
  1576. }
  1577. throw new SmartyException("Plugin '{$tag}' not callable");
  1578. }
  1579. }
  1580. $this->trigger_template_error("unknown tag '{$tag}'", null, true);
  1581. }
  1582. }
  1583. }