smarty_internal_templatecompilerbase.php 53 KB

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