PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/bblog/libs/Smarty_Compiler.class.php

https://github.com/escherlat/loquacity
PHP | 2137 lines | 1388 code | 277 blank | 472 comment | 303 complexity | 4befe532d7e3a51cd080e78b42c2b015 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /**
  3. * ./bblog/libs/Smarty_Compiler.class.php
  4. *
  5. * @package default
  6. */
  7. /**
  8. * Project: Smarty: the PHP compiling template engine
  9. * File: Smarty_Compiler.class.php
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation; either
  14. * version 2.1 of the License, or (at your option) any later version.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  24. *
  25. * You may contact the authors of Smarty by e-mail at:
  26. * monte@ispi.net
  27. * andrei@php.net
  28. *
  29. * Or, write to:
  30. * Monte Ohrt
  31. * Director of Technology, ispi
  32. * 237 S. 70th suite 220
  33. * Lincoln, NE 68510
  34. *
  35. * The latest version of Smarty can be obtained from:
  36. * http://smarty.php.net/
  37. *
  38. * @link http://smarty.php.net/
  39. * @author Monte Ohrt <monte@ispi.net>
  40. * @author Andrei Zmievski <andrei@php.net>
  41. * @version 2.6.0
  42. * @copyright 2001-2003 ispi of Lincoln, Inc.
  43. * @package Smarty
  44. */
  45. /* $Id: Smarty_Compiler.class.php,v 1.4 2004/01/08 13:04:44 eadz Exp $ */
  46. /**
  47. * Template compiling class
  48. *
  49. * @package Smarty
  50. */
  51. class Smarty_Compiler extends Smarty {
  52. // internal vars
  53. /**#@+
  54. * @access private
  55. */
  56. var $_sectionelse_stack = array(); // keeps track of whether section had 'else' part
  57. var $_foreachelse_stack = array(); // keeps track of whether foreach had 'else' part
  58. var $_literal_blocks = array(); // keeps literal template blocks
  59. var $_php_blocks = array(); // keeps php code blocks
  60. var $_current_file = null; // the current template being compiled
  61. var $_current_line_no = 1; // line number for error messages
  62. var $_capture_stack = array(); // keeps track of nested capture buffers
  63. var $_plugin_info = array(); // keeps track of plugins to load
  64. var $_init_smarty_vars = false;
  65. var $_permitted_tokens = array('true', 'false', 'yes', 'no', 'on', 'off', 'null');
  66. var $_db_qstr_regexp = null; // regexps are setup in the constructor
  67. var $_si_qstr_regexp = null;
  68. var $_qstr_regexp = null;
  69. var $_func_regexp = null;
  70. var $_var_bracket_regexp = null;
  71. var $_dvar_guts_regexp = null;
  72. var $_dvar_regexp = null;
  73. var $_cvar_regexp = null;
  74. var $_svar_regexp = null;
  75. var $_avar_regexp = null;
  76. var $_mod_regexp = null;
  77. var $_var_regexp = null;
  78. var $_parenth_param_regexp = null;
  79. var $_func_call_regexp = null;
  80. var $_obj_ext_regexp = null;
  81. var $_obj_start_regexp = null;
  82. var $_obj_params_regexp = null;
  83. var $_obj_call_regexp = null;
  84. var $_cacheable_state = 0;
  85. var $_cache_attrs_count = 0;
  86. var $_nocache_count = 0;
  87. var $_cache_serial = null;
  88. var $_cache_include = null;
  89. var $_strip_depth = 0;
  90. var $_additional_newline = "\n";
  91. /**#@-*/
  92. /**
  93. * The class constructor.
  94. */
  95. function Smarty_Compiler() {
  96. // matches double quoted strings:
  97. // "foobar"
  98. // "foo\"bar"
  99. $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
  100. // matches single quoted strings:
  101. // 'foobar'
  102. // 'foo\'bar'
  103. $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
  104. // matches single or double quoted strings
  105. $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
  106. // matches bracket portion of vars
  107. // [0]
  108. // [foo]
  109. // [$bar]
  110. $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
  111. // matches $ vars (not objects):
  112. // $foo
  113. // $foo.bar
  114. // $foo.bar.foobar
  115. // $foo[0]
  116. // $foo[$bar]
  117. // $foo[5][blah]
  118. // $foo[5].bar[$foobar][4]
  119. $this->_dvar_math_regexp = '[\+\-\*\/\%]';
  120. $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
  121. $this->_dvar_num_var_regexp = '\-?\d+(?:\.\d+)?' . $this->_dvar_math_var_regexp;
  122. $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
  123. . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:\-?\d+(?:\.\d+)?|' . $this->_dvar_math_var_regexp . ')*)?';
  124. $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
  125. // matches config vars:
  126. // #foo#
  127. // #foobar123_foo#
  128. $this->_cvar_regexp = '\#\w+\#';
  129. // matches section vars:
  130. // %foo.bar%
  131. $this->_svar_regexp = '\%\w+\.\w+\%';
  132. // matches all valid variables (no quotes, no modifiers)
  133. $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
  134. . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
  135. // matches valid variable syntax:
  136. // $foo
  137. // $foo
  138. // #foo#
  139. // #foo#
  140. // "text"
  141. // "text"
  142. $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
  143. // matches valid object call (no objects allowed in parameters):
  144. // $foo->bar
  145. // $foo->bar()
  146. // $foo->bar("text")
  147. // $foo->bar($foo, $bar, "text")
  148. // $foo->bar($foo, "foo")
  149. // $foo->bar->foo()
  150. // $foo->bar->foo->bar()
  151. $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
  152. $this->_obj_params_regexp = '\((?:\w+|'
  153. . $this->_var_regexp . '(?:\s*,\s*(?:(?:\w+|'
  154. . $this->_var_regexp . ')))*)?\)';
  155. $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
  156. $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?)';
  157. // matches valid modifier syntax:
  158. // |foo
  159. // |@foo
  160. // |foo:"bar"
  161. // |foo:$bar
  162. // |foo:"bar":$foobar
  163. // |foo|bar
  164. // |foo:$foo->bar
  165. $this->_mod_regexp = '(?:\|@?\w+(?::(?>-?\w+|'
  166. . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
  167. // matches valid function name:
  168. // foo123
  169. // _foo_bar
  170. $this->_func_regexp = '[a-zA-Z_]\w*';
  171. // matches valid registered object:
  172. // foo->bar
  173. $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
  174. // matches valid parameter values:
  175. // true
  176. // $foo
  177. // $foo|bar
  178. // #foo#
  179. // #foo#|bar
  180. // "text"
  181. // "text"|bar
  182. // $foo->bar
  183. $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
  184. . $this->_var_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
  185. // matches valid parenthesised function parameters:
  186. //
  187. // "text"
  188. // $foo, $bar, "text"
  189. // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
  190. $this->_parenth_param_regexp = '(?:\((?:\w+|'
  191. . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
  192. . $this->_param_regexp . ')))*)?\))';
  193. // matches valid function call:
  194. // foo()
  195. // foo_bar($foo)
  196. // _foo_bar($foo,"bar")
  197. // foo123($foo,$foo->bar(),"foo")
  198. $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
  199. . $this->_parenth_param_regexp . '))';
  200. }
  201. /**
  202. * compile a resource
  203. *
  204. * sets $compiled_content to the compiled source
  205. *
  206. * @param string $resource_name
  207. * @param string $source_content
  208. * @param string $compiled_content
  209. * @return true
  210. */
  211. function _compile_file($resource_name, $source_content, &$compiled_content) {
  212. if ($this->security) {
  213. // do not allow php syntax to be executed unless specified
  214. if ($this->php_handling == SMARTY_PHP_ALLOW &&
  215. !$this->security_settings['PHP_HANDLING']) {
  216. $this->php_handling = SMARTY_PHP_PASSTHRU;
  217. }
  218. }
  219. $this->_load_filters();
  220. $this->_current_file = $resource_name;
  221. $this->_current_line_no = 1;
  222. $ldq = preg_quote($this->left_delimiter, '!');
  223. $rdq = preg_quote($this->right_delimiter, '!');
  224. // run template source through prefilter functions
  225. if (count($this->_plugins['prefilter']) > 0) {
  226. foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
  227. if ($prefilter === false) continue;
  228. if ($prefilter[3] || is_callable($prefilter[0])) {
  229. $source_content = call_user_func_array($prefilter[0],
  230. array($source_content, &$this));
  231. $this->_plugins['prefilter'][$filter_name][3] = true;
  232. } else {
  233. $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
  234. }
  235. }
  236. }
  237. /* Annihilate the comments. */
  238. $source_content = preg_replace("!({$ldq})\*(.*?)\*({$rdq})!se",
  239. "'\\1*'.str_repeat(\"\n\", substr_count('\\2', \"\n\")) .'*\\3'",
  240. $source_content);
  241. /* Pull out the literal blocks. */
  242. preg_match_all("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", $source_content, $_match);
  243. $this->_literal_blocks = $_match[1];
  244. $source_content = preg_replace("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s",
  245. $this->_quote_replace($this->left_delimiter.'literal'.$this->right_delimiter), $source_content);
  246. /* Pull out the php code blocks. */
  247. preg_match_all("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s", $source_content, $_match);
  248. $this->_php_blocks = $_match[1];
  249. $source_content = preg_replace("!{$ldq}php{$rdq}(.*?){$ldq}/php{$rdq}!s",
  250. $this->_quote_replace($this->left_delimiter.'php'.$this->right_delimiter), $source_content);
  251. /* Gather all template tags. */
  252. preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $source_content, $_match);
  253. $template_tags = $_match[1];
  254. /* Split content by template tags to obtain non-template content. */
  255. $text_blocks = preg_split("!{$ldq}.*?{$rdq}!s", $source_content);
  256. /* loop through text blocks */
  257. for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
  258. /* match anything resembling php tags */
  259. if (preg_match_all('!(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)!is', $text_blocks[$curr_tb], $sp_match)) {
  260. /* replace tags with placeholders to prevent recursive replacements */
  261. $sp_match[1] = array_unique($sp_match[1]);
  262. usort($sp_match[1], '_smarty_sort_length');
  263. for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
  264. $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp], '%%%SMARTYSP'.$curr_sp.'%%%', $text_blocks[$curr_tb]);
  265. }
  266. /* process each one */
  267. for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
  268. if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
  269. /* echo php contents */
  270. $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
  271. } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
  272. /* quote php tags */
  273. $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
  274. } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
  275. /* remove php tags */
  276. $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
  277. } else {
  278. /* SMARTY_PHP_ALLOW, but echo non php starting tags */
  279. $sp_match[1][$curr_sp] = preg_replace('%(<\?(?!php|=|$))%i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
  280. $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
  281. }
  282. }
  283. }
  284. }
  285. /* Compile the template tags into PHP code. */
  286. $compiled_tags = array();
  287. for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
  288. $this->_current_line_no += substr_count($text_blocks[$i], "\n");
  289. $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
  290. $this->_current_line_no += substr_count($template_tags[$i], "\n");
  291. }
  292. $compiled_content = '';
  293. /* Interleave the compiled contents and text blocks to get the final result. */
  294. for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
  295. if ($compiled_tags[$i] == '') {
  296. // tag result empty, remove first newline from following text block
  297. $text_blocks[$i+1] = preg_replace('!^(\r\n|\r|\n)!', '', $text_blocks[$i+1]);
  298. }
  299. $compiled_content .= $text_blocks[$i].$compiled_tags[$i];
  300. }
  301. $compiled_content .= $text_blocks[$i];
  302. /* Reformat data between 'strip' and '/strip' tags, removing spaces, tabs and newlines. */
  303. if (preg_match_all("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s", $compiled_content, $_match)) {
  304. $strip_tags = $_match[0];
  305. $strip_tags_modified = preg_replace("!{$ldq}/?strip{$rdq}|[\t ]+$|^[\t ]+!m", '', $strip_tags);
  306. $strip_tags_modified = preg_replace('![\r\n]+!m', '', $strip_tags_modified);
  307. for ($i = 0, $for_max = count($strip_tags); $i < $for_max; $i++)
  308. $compiled_content = preg_replace("!{$ldq}strip{$rdq}.*?{$ldq}/strip{$rdq}!s",
  309. $this->_quote_replace($strip_tags_modified[$i]),
  310. $compiled_content, 1);
  311. }
  312. // remove \n from the end of the file, if any
  313. if (($_len=strlen($compiled_content)) && ($compiled_content{$_len - 1} == "\n" )) {
  314. $compiled_content = substr($compiled_content, 0, -1);
  315. }
  316. if (!empty($this->_cache_serial)) {
  317. $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
  318. }
  319. // remove unnecessary close/open tags
  320. $compiled_content = preg_replace('!\?>\n?<\?php!', '', $compiled_content);
  321. // run compiled template through postfilter functions
  322. if (count($this->_plugins['postfilter']) > 0) {
  323. foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
  324. if ($postfilter === false) continue;
  325. if ($postfilter[3] || is_callable($postfilter[0])) {
  326. $compiled_content = call_user_func_array($postfilter[0],
  327. array($compiled_content, &$this));
  328. $this->_plugins['postfilter'][$filter_name][3] = true;
  329. } else {
  330. $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
  331. }
  332. }
  333. }
  334. // put header at the top of the compiled template
  335. $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
  336. $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
  337. /* Emit code to load needed plugins. */
  338. $this->_plugins_code = '';
  339. if (count($this->_plugin_info)) {
  340. $_plugins_params = "array('plugins' => array(";
  341. foreach ($this->_plugin_info as $plugin_type => $plugins) {
  342. foreach ($plugins as $plugin_name => $plugin_info) {
  343. $_plugins_params .= "array('$plugin_type', '$plugin_name', '$plugin_info[0]', $plugin_info[1], ";
  344. $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
  345. }
  346. }
  347. $_plugins_params .= '))';
  348. $plugins_code = "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
  349. $template_header .= $plugins_code;
  350. $this->_plugin_info = array();
  351. $this->_plugins_code = $plugins_code;
  352. }
  353. if ($this->_init_smarty_vars) {
  354. $template_header .= "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
  355. $this->_init_smarty_vars = false;
  356. }
  357. $compiled_content = $template_header . $compiled_content;
  358. return true;
  359. }
  360. /**
  361. * Compile a template tag
  362. *
  363. * @param string $template_tag
  364. * @return string
  365. */
  366. function _compile_tag($template_tag) {
  367. /* Matched comment. */
  368. if ($template_tag{0} == '*' && $template_tag{strlen($template_tag) - 1} == '*')
  369. return '';
  370. /* Split tag into two three parts: command, command modifiers and the arguments. */
  371. if (! preg_match('/^(?:(' . $this->_obj_call_regexp . '|' . $this->_var_regexp
  372. . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
  373. (?:\s+(.*))?$
  374. /xs', $template_tag, $match)) {
  375. $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
  376. }
  377. $tag_command = $match[1];
  378. $tag_modifier = isset($match[2]) ? $match[2] : null;
  379. $tag_args = isset($match[3]) ? $match[3] : null;
  380. if (preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$!', $tag_command)) {
  381. /* tag name is a variable or object */
  382. $_return = $this->_parse_var_props($tag_command . $tag_modifier, $this->_parse_attrs($tag_args));
  383. if (isset($_tag_attrs['assign'])) {
  384. return "<?php \$this->assign('" . $this->_dequote($_tag_attrs['assign']) . "', $_return ); ?>\n";
  385. } else {
  386. return "<?php echo $_return; ?>" . $this->_additional_newline;
  387. }
  388. }
  389. /* If the tag name is a registered object, we process it. */
  390. if (preg_match('!^\/?' . $this->_reg_obj_regexp . '$!', $tag_command)) {
  391. return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
  392. }
  393. switch ($tag_command) {
  394. case 'include':
  395. return $this->_compile_include_tag($tag_args);
  396. case 'include_php':
  397. return $this->_compile_include_php_tag($tag_args);
  398. case 'if':
  399. return $this->_compile_if_tag($tag_args);
  400. case 'else':
  401. return '<?php else: ?>';
  402. case 'elseif':
  403. return $this->_compile_if_tag($tag_args, true);
  404. case '/if':
  405. return '<?php endif; ?>';
  406. case 'capture':
  407. return $this->_compile_capture_tag(true, $tag_args);
  408. case '/capture':
  409. return $this->_compile_capture_tag(false);
  410. case 'ldelim':
  411. return $this->left_delimiter;
  412. case 'rdelim':
  413. return $this->right_delimiter;
  414. case 'section':
  415. array_push($this->_sectionelse_stack, false);
  416. return $this->_compile_section_start($tag_args);
  417. case 'sectionelse':
  418. $this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;
  419. return "<?php endfor; else: ?>";
  420. case '/section':
  421. if (array_pop($this->_sectionelse_stack))
  422. return "<?php endif; ?>";
  423. else
  424. return "<?php endfor; endif; ?>";
  425. case 'foreach':
  426. array_push($this->_foreachelse_stack, false);
  427. return $this->_compile_foreach_start($tag_args);
  428. break;
  429. case 'foreachelse':
  430. $this->_foreachelse_stack[count($this->_foreachelse_stack)-1] = true;
  431. return "<?php endforeach; unset(\$_from); else: ?>";
  432. case '/foreach':
  433. if (array_pop($this->_foreachelse_stack))
  434. return "<?php endif; ?>";
  435. else
  436. return "<?php endforeach; unset(\$_from); endif; ?>";
  437. case 'strip':
  438. case '/strip':
  439. if ($tag_command{0}=='/') {
  440. if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
  441. $this->_additional_newline = "\n";
  442. return $this->left_delimiter.$tag_command.$this->right_delimiter;
  443. }
  444. } else {
  445. if ($this->_strip_depth++==0) { /* outermost opening {strip} */
  446. $this->_additional_newline = "";
  447. return $this->left_delimiter.$tag_command.$this->right_delimiter;
  448. }
  449. }
  450. return '';
  451. case 'literal':
  452. list (, $literal_block) = each($this->_literal_blocks);
  453. $this->_current_line_no += substr_count($literal_block, "\n");
  454. return "<?php echo '".str_replace("'", "\'", str_replace("\\", "\\\\", $literal_block))."'; ?>" . $this->_additional_newline;
  455. case 'php':
  456. if ($this->security && !$this->security_settings['PHP_TAGS']) {
  457. $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
  458. return;
  459. }
  460. list (, $php_block) = each($this->_php_blocks);
  461. $this->_current_line_no += substr_count($php_block, "\n");
  462. return '<?php '.$php_block.' ?>';
  463. case 'insert':
  464. return $this->_compile_insert_tag($tag_args);
  465. default:
  466. if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
  467. return $output;
  468. } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
  469. return $output;
  470. } else {
  471. return $this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier);
  472. }
  473. }
  474. }
  475. /**
  476. * compile the custom compiler tag
  477. *
  478. * sets $output to the compiled custom compiler tag
  479. *
  480. * @param string $tag_command
  481. * @param string $tag_args
  482. * @param string $output
  483. * @return boolean
  484. */
  485. function _compile_compiler_tag($tag_command, $tag_args, &$output) {
  486. $found = false;
  487. $have_function = true;
  488. /*
  489. * First we check if the compiler function has already been registered
  490. * or loaded from a plugin file.
  491. */
  492. if (isset($this->_plugins['compiler'][$tag_command])) {
  493. $found = true;
  494. $plugin_func = $this->_plugins['compiler'][$tag_command][0];
  495. if (!is_callable($plugin_func)) {
  496. $message = "compiler function '$tag_command' is not implemented";
  497. $have_function = false;
  498. }
  499. }
  500. /*
  501. * Otherwise we need to load plugin file and look for the function
  502. * inside it.
  503. */
  504. else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
  505. $found = true;
  506. include_once $plugin_file;
  507. $plugin_func = 'smarty_compiler_' . $tag_command;
  508. if (!is_callable($plugin_func)) {
  509. $message = "plugin function $plugin_func() not found in $plugin_file\n";
  510. $have_function = false;
  511. } else {
  512. $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
  513. }
  514. }
  515. /*
  516. * True return value means that we either found a plugin or a
  517. * dynamically registered function. False means that we didn't and the
  518. * compiler should now emit code to load custom function plugin for this
  519. * tag.
  520. */
  521. if ($found) {
  522. if ($have_function) {
  523. $output = call_user_func_array($plugin_func, array($tag_args, &$this));
  524. if ($output != '') {
  525. $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
  526. . $output
  527. . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
  528. }
  529. } else {
  530. $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
  531. }
  532. return true;
  533. } else {
  534. return false;
  535. }
  536. }
  537. /**
  538. * compile block function tag
  539. *
  540. * sets $output to compiled block function tag
  541. *
  542. * @param string $tag_command
  543. * @param string $tag_args
  544. * @param string $tag_modifier
  545. * @param string $output
  546. * @return boolean
  547. */
  548. function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output) {
  549. if ($tag_command{0} == '/') {
  550. $start_tag = false;
  551. $tag_command = substr($tag_command, 1);
  552. } else
  553. $start_tag = true;
  554. $found = false;
  555. $have_function = true;
  556. /*
  557. * First we check if the block function has already been registered
  558. * or loaded from a plugin file.
  559. */
  560. if (isset($this->_plugins['block'][$tag_command])) {
  561. $found = true;
  562. $plugin_func = $this->_plugins['block'][$tag_command][0];
  563. if (!is_callable($plugin_func)) {
  564. $message = "block function '$tag_command' is not implemented";
  565. $have_function = false;
  566. }
  567. }
  568. /*
  569. * Otherwise we need to load plugin file and look for the function
  570. * inside it.
  571. */
  572. else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
  573. $found = true;
  574. include_once $plugin_file;
  575. $plugin_func = 'smarty_block_' . $tag_command;
  576. if (!function_exists($plugin_func)) {
  577. $message = "plugin function $plugin_func() not found in $plugin_file\n";
  578. $have_function = false;
  579. } else {
  580. $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
  581. }
  582. }
  583. if (!$found) {
  584. return false;
  585. } else if (!$have_function) {
  586. $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
  587. return true;
  588. }
  589. /*
  590. * Even though we've located the plugin function, compilation
  591. * happens only once, so the plugin will still need to be loaded
  592. * at runtime for future requests.
  593. */
  594. $this->_add_plugin('block', $tag_command);
  595. if ($start_tag) {
  596. $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
  597. $attrs = $this->_parse_attrs($tag_args);
  598. $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs='');
  599. $output .= "$_cache_attrs\$_params = \$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
  600. $output .= $this->_compile_plugin_call('block', $tag_command).'($_params[1], null, $this, $_block_repeat=true); unset($_params);';
  601. $output .= 'while ($_block_repeat) { ob_start(); ?>';
  602. } else {
  603. $output = '<?php $this->_block_content = ob_get_contents(); ob_end_clean(); ';
  604. $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $this->_block_content, $this, $_block_repeat=false)';
  605. if ($tag_modifier != '') {
  606. $this->_parse_modifiers($_out_tag_text, $tag_modifier);
  607. }
  608. $output .= 'echo '.$_out_tag_text.'; } ';
  609. $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
  610. }
  611. return true;
  612. }
  613. /**
  614. * compile custom function tag
  615. *
  616. * @param string $tag_command
  617. * @param string $tag_args
  618. * @param string $tag_modifier
  619. * @return string
  620. */
  621. function _compile_custom_tag($tag_command, $tag_args, $tag_modifier) {
  622. $this->_add_plugin('function', $tag_command);
  623. $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
  624. $attrs = $this->_parse_attrs($tag_args);
  625. $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs='');
  626. $_return = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
  627. if ($tag_modifier != '') {
  628. $this->_parse_modifiers($_return, $tag_modifier);
  629. }
  630. if ($_return != '') {
  631. $_return = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $_return . ';'
  632. . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
  633. }
  634. return $_return;
  635. }
  636. /**
  637. * compile a registered object tag
  638. *
  639. * @param string $tag_command
  640. * @param array $attrs
  641. * @param string $tag_modifier
  642. * @return string
  643. */
  644. function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier) {
  645. if ($tag_command{0} == '/') {
  646. $start_tag = false;
  647. $tag_command = substr($tag_command, 1);
  648. } else {
  649. $start_tag = true;
  650. }
  651. list($object, $obj_comp) = explode('->', $tag_command);
  652. $arg_list = array();
  653. if (count($attrs)) {
  654. $_assign_var = false;
  655. foreach ($attrs as $arg_name => $arg_value) {
  656. if ($arg_name == 'assign') {
  657. $_assign_var = $arg_value;
  658. unset($attrs['assign']);
  659. continue;
  660. }
  661. if (is_bool($arg_value))
  662. $arg_value = $arg_value ? 'true' : 'false';
  663. $arg_list[] = "'$arg_name' => $arg_value";
  664. }
  665. }
  666. if ($this->_reg_objects[$object][2]) {
  667. // smarty object argument format
  668. $args = "array(".implode(',', (array)$arg_list)."), \$this";
  669. } else {
  670. // traditional argument format
  671. $args = implode(',', array_values($attrs));
  672. if (empty($args)) {
  673. $args = 'null';
  674. }
  675. }
  676. $prefix = '';
  677. $postfix = '';
  678. $newline = '';
  679. if (!is_object($this->_reg_objects[$object][0])) {
  680. $this->_trigger_fatal_error("registered '$object' is not an object");
  681. } elseif (!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
  682. $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'");
  683. } elseif (method_exists($this->_reg_objects[$object][0], $obj_comp)) {
  684. // method
  685. if (in_array($obj_comp, $this->_reg_objects[$object][3])) {
  686. // block method
  687. if ($start_tag) {
  688. $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
  689. $prefix .= "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat=true); ";
  690. $prefix .= "while (\$_block_repeat) { ob_start();";
  691. $return = null;
  692. $postfix = '';
  693. } else {
  694. $prefix = "\$this->_obj_block_content = ob_get_contents(); ob_end_clean(); ";
  695. $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$this->_obj_block_content, \$this, \$_block_repeat=false)";
  696. $postfix = "} array_pop(\$this->_tag_stack);";
  697. }
  698. } else {
  699. // non-block method
  700. $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
  701. }
  702. } else {
  703. // property
  704. $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
  705. }
  706. if ($return != null) {
  707. if ($tag_modifier != '') {
  708. $this->_parse_modifiers($return, $tag_modifier);
  709. }
  710. if (!empty($_assign_var)) {
  711. $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);";
  712. } else {
  713. $output = 'echo ' . $return . ';';
  714. $newline = $this->_additional_newline;
  715. }
  716. } else {
  717. $output = '';
  718. }
  719. return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
  720. }
  721. /**
  722. * Compile {insert ...} tag
  723. *
  724. * @param string $tag_args
  725. * @return string
  726. */
  727. function _compile_insert_tag($tag_args) {
  728. $attrs = $this->_parse_attrs($tag_args);
  729. $name = $this->_dequote($attrs['name']);
  730. if (empty($name)) {
  731. $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
  732. }
  733. if (!empty($attrs['script'])) {
  734. $delayed_loading = true;
  735. } else {
  736. $delayed_loading = false;
  737. }
  738. foreach ($attrs as $arg_name => $arg_value) {
  739. if (is_bool($arg_value))
  740. $arg_value = $arg_value ? 'true' : 'false';
  741. $arg_list[] = "'$arg_name' => $arg_value";
  742. }
  743. $this->_add_plugin('insert', $name, $delayed_loading);
  744. $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
  745. return "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
  746. }
  747. /**
  748. * Compile {include ...} tag
  749. *
  750. * @param string $tag_args
  751. * @return string
  752. */
  753. function _compile_include_tag($tag_args) {
  754. $attrs = $this->_parse_attrs($tag_args);
  755. $arg_list = array();
  756. if (empty($attrs['file'])) {
  757. $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
  758. }
  759. foreach ($attrs as $arg_name => $arg_value) {
  760. if ($arg_name == 'file') {
  761. $include_file = $arg_value;
  762. continue;
  763. } else if ($arg_name == 'assign') {
  764. $assign_var = $arg_value;
  765. continue;
  766. }
  767. if (is_bool($arg_value))
  768. $arg_value = $arg_value ? 'true' : 'false';
  769. $arg_list[] = "'$arg_name' => $arg_value";
  770. }
  771. $output = '<?php ';
  772. if (isset($assign_var)) {
  773. $output .= "ob_start();\n";
  774. }
  775. $output .=
  776. "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
  777. $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
  778. $output .= "\$this->_smarty_include($_params);\n" .
  779. "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
  780. "unset(\$_smarty_tpl_vars);\n";
  781. if (isset($assign_var)) {
  782. $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
  783. }
  784. $output .= ' ?>';
  785. return $output;
  786. }
  787. /**
  788. * Compile {include ...} tag
  789. *
  790. * @param string $tag_args
  791. * @return string
  792. */
  793. function _compile_include_php_tag($tag_args) {
  794. $attrs = $this->_parse_attrs($tag_args);
  795. if (empty($attrs['file'])) {
  796. $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
  797. }
  798. $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
  799. $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
  800. foreach ($attrs as $arg_name => $arg_value) {
  801. if ($arg_name != 'file' and $arg_name != 'once' and $arg_name != 'assign') {
  802. if (is_bool($arg_value))
  803. $arg_value = $arg_value ? 'true' : 'false';
  804. $arg_list[] = "'$arg_name' => $arg_value";
  805. }
  806. }
  807. $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
  808. return "<?php require_once(SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
  809. }
  810. /**
  811. * Compile {section ...} tag
  812. *
  813. * @param string $tag_args
  814. * @return string
  815. */
  816. function _compile_section_start($tag_args) {
  817. $attrs = $this->_parse_attrs($tag_args);
  818. $arg_list = array();
  819. $output = '<?php ';
  820. $section_name = $attrs['name'];
  821. if (empty($section_name)) {
  822. $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
  823. }
  824. $output .= "if (isset(\$this->_sections[$section_name])) unset(\$this->_sections[$section_name]);\n";
  825. $section_props = "\$this->_sections[$section_name]";
  826. foreach ($attrs as $attr_name => $attr_value) {
  827. switch ($attr_name) {
  828. case 'loop':
  829. $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
  830. break;
  831. case 'show':
  832. if (is_bool($attr_value))
  833. $show_attr_value = $attr_value ? 'true' : 'false';
  834. else
  835. $show_attr_value = "(bool)$attr_value";
  836. $output .= "{$section_props}['show'] = $show_attr_value;\n";
  837. break;
  838. case 'name':
  839. $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
  840. break;
  841. case 'max':
  842. case 'start':
  843. $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
  844. break;
  845. case 'step':
  846. $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
  847. break;
  848. default:
  849. $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
  850. break;
  851. }
  852. }
  853. if (!isset($attrs['show']))
  854. $output .= "{$section_props}['show'] = true;\n";
  855. if (!isset($attrs['loop']))
  856. $output .= "{$section_props}['loop'] = 1;\n";
  857. if (!isset($attrs['max']))
  858. $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
  859. else
  860. $output .= "if ({$section_props}['max'] < 0)\n" .
  861. " {$section_props}['max'] = {$section_props}['loop'];\n";
  862. if (!isset($attrs['step']))
  863. $output .= "{$section_props}['step'] = 1;\n";
  864. if (!isset($attrs['start']))
  865. $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
  866. else {
  867. $output .= "if ({$section_props}['start'] < 0)\n" .
  868. " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
  869. "else\n" .
  870. " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
  871. }
  872. $output .= "if ({$section_props}['show']) {\n";
  873. if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
  874. $output .= " {$section_props}['total'] = {$section_props}['loop'];\n";
  875. } else {
  876. $output .= " {$section_props}['total'] = min(ceil(({$section_props}['step'] > 0 ? {$section_props}['loop'] - {$section_props}['start'] : {$section_props}['start']+1)/abs({$section_props}['step'])), {$section_props}['max']);\n";
  877. }
  878. $output .= " if ({$section_props}['total'] == 0)\n" .
  879. " {$section_props}['show'] = false;\n" .
  880. "} else\n" .
  881. " {$section_props}['total'] = 0;\n";
  882. $output .= "if ({$section_props}['show']):\n";
  883. $output .= "
  884. for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
  885. {$section_props}['iteration'] <= {$section_props}['total'];
  886. {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
  887. $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
  888. $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
  889. $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
  890. $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n";
  891. $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n";
  892. $output .= "?>";
  893. return $output;
  894. }
  895. /**
  896. * Compile {foreach ...} tag.
  897. *
  898. * @param string $tag_args
  899. * @return string
  900. */
  901. function _compile_foreach_start($tag_args) {
  902. $attrs = $this->_parse_attrs($tag_args);
  903. $arg_list = array();
  904. if (empty($attrs['from'])) {
  905. $this->_syntax_error("missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
  906. }
  907. if (empty($attrs['item'])) {
  908. $this->_syntax_error("missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
  909. }
  910. $from = $attrs['from'];
  911. $item = $this->_dequote($attrs['item']);
  912. if (isset($attrs['name']))
  913. $name = $attrs['name'];
  914. $output = '<?php ';
  915. if (isset($name)) {
  916. $output .= "if (isset(\$this->_foreach[$name])) unset(\$this->_foreach[$name]);\n";
  917. $foreach_props = "\$this->_foreach[$name]";
  918. }
  919. $key_part = '';
  920. foreach ($attrs as $attr_name => $attr_value) {
  921. switch ($attr_name) {
  922. case 'key':
  923. $key = $this->_dequote($attrs['key']);
  924. $key_part = "\$this->_tpl_vars['$key'] => ";
  925. break;
  926. case 'name':
  927. $output .= "{$foreach_props}['$attr_name'] = $attr_value;\n";
  928. break;
  929. }
  930. }
  931. if (isset($name)) {
  932. $output .= "{$foreach_props}['total'] = count(\$_from = (array)$from);\n";
  933. $output .= "{$foreach_props}['show'] = {$foreach_props}['total'] > 0;\n";
  934. $output .= "if ({$foreach_props}['show']):\n";
  935. $output .= "{$foreach_props}['iteration'] = 0;\n";
  936. $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
  937. $output .= " {$foreach_props}['iteration']++;\n";
  938. $output .= " {$foreach_props}['first'] = ({$foreach_props}['iteration'] == 1);\n";
  939. $output .= " {$foreach_props}['last'] = ({$foreach_props}['iteration'] == {$foreach_props}['total']);\n";
  940. } else {
  941. $output .= "if (count(\$_from = (array)$from)):\n";
  942. $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
  943. }
  944. $output .= '?>';
  945. return $output;
  946. }
  947. /**
  948. * Compile {capture} .. {/capture} tags
  949. *
  950. * @param boolean $start true if this is the {capture} tag
  951. * @param string $tag_args
  952. * @return string
  953. */
  954. function _compile_capture_tag($start, $tag_args = '') {
  955. $attrs = $this->_parse_attrs($tag_args);
  956. if ($start) {
  957. if (isset($attrs['name']))
  958. $buffer = $attrs['name'];
  959. else
  960. $buffer = "'default'";
  961. if (isset($attrs['assign']))
  962. $assign = $attrs['assign'];
  963. else
  964. $assign = null;
  965. $output = "<?php ob_start(); ?>";
  966. $this->_capture_stack[] = array($buffer, $assign);
  967. } else {
  968. list($buffer, $assign) = array_pop($this->_capture_stack);
  969. $output = "<?php \$this->_smarty_vars['capture'][$buffer] = ob_get_contents(); ";
  970. if (isset($assign)) {
  971. $output .= " \$this->assign($assign, ob_get_contents());";
  972. }
  973. $output .= "ob_end_clean(); ?>";
  974. }
  975. return $output;
  976. }
  977. /**
  978. * Compile {if ...} tag
  979. *
  980. * @param string $tag_args
  981. * @param boolean $elseif (optional) if true, uses elseif instead of if
  982. * @return string
  983. */
  984. function _compile_if_tag($tag_args, $elseif = false) {
  985. /* Tokenize args for 'if' tag. */
  986. preg_match_all('/(?>
  987. ' . $this->_obj_call_regexp . '(?:' . $this->_mod_regexp . '*)? | # valid object call
  988. ' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)? | # var or quoted string
  989. \-?0[xX][0-9a-fA-F]+|\-?\d+(?:\.\d+)?|\.\d+|!==|===|==|!=|<>|<<|>>|<=|>=|\&\&|\|\||\(|\)|,|\!|\^|=|\&|\~|<|>|\||\%|\+|\-|\/|\*|\@ | # valid non-word token
  990. \b\w+\b | # valid word token
  991. \S+ # anything else
  992. )/x', $tag_args, $match);
  993. $tokens = $match[0];
  994. // make sure we have balanced parenthesis
  995. $token_count = array_count_values($tokens);
  996. if (isset($token_count['(']) && $token_count['('] != $token_count[')']) {
  997. $this->_syntax_error("unbalanced parenthesis in if statement", E_USER_ERROR, __FILE__, __LINE__);
  998. }
  999. $is_arg_stack = array();
  1000. for ($i = 0; $i < count($tokens); $i++) {
  1001. $token = &$tokens[$i];
  1002. switch (strtolower($token)) {
  1003. case '!':
  1004. case '%':
  1005. case '!==':
  1006. case '==':
  1007. case '===':
  1008. case '>':
  1009. case '<':
  1010. case '!=':
  1011. case '<>':
  1012. case '<<':
  1013. case '>>':
  1014. case '<=':
  1015. case '>=':
  1016. case '&&':
  1017. case '||':
  1018. case '|':
  1019. case '^':
  1020. case '&':
  1021. case '~':
  1022. case ')':
  1023. case ',':
  1024. case '+':
  1025. case '-':
  1026. case '*':
  1027. case '/':
  1028. case '@':
  1029. break;
  1030. case 'eq':
  1031. $token = '==';
  1032. break;
  1033. case 'ne':
  1034. case 'neq':
  1035. $token = '!=';
  1036. break;
  1037. case 'lt':
  1038. $token = '<';
  1039. break;
  1040. case 'le':
  1041. case 'lte':
  1042. $token = '<=';
  1043. break;
  1044. case 'gt':
  1045. $token = '>';
  1046. break;
  1047. case 'ge':
  1048. case 'gte':
  1049. $token = '>=';
  1050. break;
  1051. case 'and':
  1052. $token = '&&';
  1053. break;
  1054. case 'or':
  1055. $token = '||';
  1056. break;
  1057. case 'not':
  1058. $token = '!';
  1059. break;
  1060. case 'mod':
  1061. $token = '%';
  1062. break;
  1063. case '(':
  1064. array_push($is_arg_stack, $i);
  1065. break;
  1066. case 'is':
  1067. /* If last token was a ')', we operate on the parenthesized
  1068. expression. The start of the expression is on the stack.
  1069. Otherwise, we operate on the last encountered token. */
  1070. if ($tokens[$i-1] == ')')
  1071. $is_arg_start = array_pop($is_arg_stack);
  1072. else
  1073. $is_arg_start = $i-1;
  1074. /* Construct the argument for 'is' expression, so it knows
  1075. what to operate on. */
  1076. $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
  1077. /* Pass all tokens from next one until the end to the
  1078. 'is' expression parsing function. The function will
  1079. return modified tokens, where the first one is the result
  1080. of the 'is' expression and the rest are the tokens it
  1081. didn't touch. */
  1082. $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
  1083. /* Replace the old tokens with the new ones. */
  1084. array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
  1085. /* Adjust argument start so that it won't change from the
  1086. current position for the next iteration. */
  1087. $i = $is_arg_start;
  1088. break;
  1089. default:
  1090. if (preg_match('!^' . $this->_func_regexp . '$!', $token) ) {
  1091. // function call
  1092. if ($this->security &&
  1093. !in_array($token, $this->security_settings['IF_FUNCS'])) {
  1094. $this->_syntax_error("(secure mode) '$token' not allowed in if statement", E_USER_ERROR, __FILE__, __LINE__);
  1095. }
  1096. } elseif (preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . '*)$!', $token)) {
  1097. // object or variable
  1098. $token = $this->_parse_var_props($token);
  1099. } elseif (is_numeric($token)) {
  1100. // number, skip it
  1101. } else {
  1102. $this->_syntax_error("unidentified token '$token'", E_USER_ERROR, __FILE__, __LINE__);
  1103. }
  1104. break;
  1105. }
  1106. }
  1107. if ($elseif)
  1108. return '<?php elseif ('.implode(' ', $tokens).'): ?>';
  1109. else
  1110. return '<?php if ('.implode(' ', $tokens).'): ?>';
  1111. }
  1112. /**
  1113. *
  1114. *
  1115. * @param unknown $type
  1116. * @param unknown $name
  1117. * @param unknown $attrs
  1118. * @param unknown $cache_code (reference)
  1119. * @return unknown
  1120. */
  1121. function _compile_arg_list($type, $name, $attrs, &$cache_code) {
  1122. $arg_list = array();
  1123. if (isset($type) && isset($name)
  1124. && isset($this->_plugins[$type])
  1125. && isset($this->_plugins[$type][$name])
  1126. && empty($this->_plugins[$type][$name][4])
  1127. && is_array($this->_plugins[$type][$name][5])
  1128. ) {
  1129. /* we have a list of parameters that should be cached */
  1130. $_cache_attrs = $this->_plugins[$type][$name][5];
  1131. $_count = $this->_cache_attrs_count++;
  1132. $cache_code = "\$_cache_attrs =& \$this->_smarty_cache_attrs('$this->_cache_serial','$_count');";
  1133. } else {
  1134. /* no parameters are cached */
  1135. $_cache_attrs = null;
  1136. }
  1137. foreach ($attrs as $arg_name => $arg_value) {
  1138. if (is_bool($arg_value))
  1139. $arg_value = $arg_value ? 'true' : 'false';
  1140. if (is_null($arg_value))
  1141. $arg_value = 'null';
  1142. if ($_cache_attrs && in_array($arg_name, $_cache_attrs)) {
  1143. $arg_list[] = "'$arg_name' => (\$this->_cache_including) ? \$_cache_attrs['$arg_name'] : (\$_cache_attrs['$arg_name']=$arg_value)";
  1144. } else {
  1145. $arg_list[] = "'$arg_name' => $arg_value";
  1146. }
  1147. }
  1148. return $arg_list;
  1149. }
  1150. /**
  1151. * Parse is expression
  1152. *
  1153. * @param string $is_arg
  1154. * @param array $tokens
  1155. * @return array
  1156. */
  1157. function _parse_is_expr($is_arg, $tokens) {
  1158. $expr_end = 0;
  1159. $negate_expr = false;
  1160. if (($first_token = array_shift($tokens)) == 'not') {
  1161. $negate_expr = true;
  1162. $expr_type = array_shift($tokens);
  1163. } else
  1164. $expr_type = $first_token;
  1165. switch ($expr_type) {
  1166. case 'even':
  1167. if (@$tokens[$expr_end] == 'by') {
  1168. $expr_end++;
  1169. $expr_arg = $tokens[$expr_end++];
  1170. $expr = "!(($is_arg / $expr_arg) % " . $this->_parse_var_props($expr_arg) . ")";
  1171. } else
  1172. $expr = "!($is_arg % 2)";
  1173. break;
  1174. case 'odd':
  1175. if (@$tokens[$expr_end] == 'by') {
  1176. $expr_end++;
  1177. $expr_arg = $tokens[$expr_end++];
  1178. $expr = "(($is_arg / $expr_arg) % ". $this->_parse_var_props($expr_arg) . ")";
  1179. } else
  1180. $expr = "($is_arg % 2)";
  1181. break;
  1182. case 'div':
  1183. if (@$tokens[$expr_end] == 'by') {
  1184. $expr_end++;
  1185. $expr_arg = $tokens[$expr_end++];
  1186. $expr = "!($is_arg % " . $this->_parse_var_props($expr_arg) . ")";
  1187. } else {
  1188. $this->_syntax_error("expecting 'by' after 'div'", E_USER_ERROR, __FILE__, __LINE__);
  1189. }
  1190. break;
  1191. default:
  1192. $this->_syntax_error("unknown 'is' expression - '$expr_type'", E_USER_ERROR, __FILE__, __LINE__);
  1193. break;
  1194. }
  1195. if ($negate_expr) {
  1196. $expr = "!($expr)";
  1197. }
  1198. array_splice($tokens, 0, $expr_end, $expr);
  1199. return $tokens;
  1200. }
  1201. /**
  1202. * Parse attribute string
  1203. *
  1204. * @param string $tag_args
  1205. * @return array
  1206. */
  1207. function _parse_attrs($tag_args) {
  1208. /* Tokenize tag attributes. */
  1209. preg_match_all('/(?:' . $this->_obj_call_regexp . '|' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+)
  1210. )+ |
  1211. [=]
  1212. /x', $tag_args, $match);
  1213. $tokens = $match[0];
  1214. $attrs = array();
  1215. /* Parse state:
  1216. 0 - expecting attribute name
  1217. 1 - expecting '='
  1218. 2 - expecting attribute value (not '=') */
  1219. $state = 0;
  1220. foreach ($tokens as $token) {
  1221. switch ($state) {
  1222. case 0:
  1223. /* If the token is a valid identifier, we set attribute name
  1224. and go to state 1. */
  1225. if (preg_match('!^\w+$!', $token)) {
  1226. $attr_name = $token;
  1227. $state = 1;
  1228. } else
  1229. $this->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
  1230. break;
  1231. case 1:
  1232. /* If the token is '=', then we go to state 2. */
  1233. if ($token == '=') {
  1234. $state = 2;
  1235. } else
  1236. $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
  1237. break;
  1238. case 2:
  1239. /* If token is not '=', we set the attribute value and go to
  1240. state 0. */
  1241. if ($token != '=') {
  1242. /* We booleanize the token if it's a non-quoted possible
  1243. boolean value. */
  1244. if (preg_match('!^(on|yes|true)$!', $token)) {
  1245. $token = 'true';
  1246. } else if (preg_match('!^(off|no|false)$!', $token)) {
  1247. $token = 'false';
  1248. } else if ($token == 'null') {
  1249. $token = 'null';
  1250. } else if (preg_match('!^-?([0-9]+|0[xX][0-9a-fA-F]+)$!', $token)) {
  1251. /* treat integer literally */
  1252. } else if (!preg_match('!^' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '(?:' . $this->_mod_regexp . ')*$!', $token)) {
  1253. /* treat as a string, double-quote it escaping quotes */
  1254. $token = '"'.addslashes($token).'"';
  1255. }
  1256. $attrs[$attr_name] = $token;
  1257. $state = 0;
  1258. } else
  1259. $this->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
  1260. break;
  1261. }
  1262. $last_token = $token;
  1263. }
  1264. if ($state != 0) {
  1265. if ($state == 1) {
  1266. $this->_syntax_error("expecting '=' after attribute name '$last_token'", E_USER_ERROR, __FILE__, __LINE__);
  1267. } else {
  1268. $this->_syntax_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
  1269. }
  1270. }
  1271. $this->_parse_vars_props($attrs);
  1272. return $attrs;
  1273. }
  1274. /**
  1275. * compile multiple variables and section properties tokens into
  1276. * PHP code
  1277. *
  1278. * @param array $tokens
  1279. */
  1280. function _parse_vars_props(&$tokens) {
  1281. foreach ($tokens as $key => $val) {
  1282. $tokens[$key] = $this->_parse_var_props($val);
  1283. }
  1284. }
  1285. /**
  1286. * compile single variable and section properties token into
  1287. * PHP code
  1288. *
  1289. * @param string $val
  1290. * @return string
  1291. */
  1292. function _parse_var_props($val) {
  1293. $val = trim($val);
  1294. if (preg_match('!^(' . $this->_obj_call_regexp . '|' . $this->_dvar_regexp . ')(' . $this->_mod_regexp . '*)$!', $val, $match)) {
  1295. // $ variable or object
  1296. $return = $this->_parse_var($match[1]);
  1297. if ($match[2] != '') {
  1298. $this->_parse_modifiers($return, $match[2]);
  1299. }
  1300. return $return;
  1301. }
  1302. elseif (preg_match('!^' . $this->_db_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
  1303. // double quoted text
  1304. preg_match('!^(' . $this->_db_qstr_regexp . ')('. $this->_mod_regexp . '*)$!', $val, $match);
  1305. $return = $this->_expand_quoted_text($match[1]);
  1306. if ($match[2] != '') {
  1307. $this->_parse_modifiers($return, $match[2]);
  1308. }
  1309. return $return;
  1310. }
  1311. elseif (preg_match('!^' . $this->_si_qstr_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
  1312. // single quoted text
  1313. preg_match('!^(' . $this->_si_qstr_regexp . ')('. $this->_mod_regexp . '*)$!', $val, $match);
  1314. if ($match[2] != '') {
  1315. $this->_parse_modifiers($match[1], $match[2]);
  1316. return $match[1];
  1317. }
  1318. }
  1319. elseif (preg_match('!^' . $this->_cvar_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
  1320. // config var
  1321. return $this->_parse_conf_var($val);
  1322. }
  1323. elseif (preg_match('!^' . $this->_svar_regexp . '(?:' . $this->_mod_regexp . '*)$!', $val)) {
  1324. // section var
  1325. return $this->_parse_section_prop($val);
  1326. }
  1327. elseif (!in_array($val, $this->_permitted_tokens) && !is_numeric($val)) {
  1328. // literal string
  1329. return $this->_expand_quoted_text('"' . $val .'"');
  1330. }
  1331. return $val;
  1332. }
  1333. /**
  1334. * expand quoted text with embedded variables
  1335. *
  1336. * @param string $var_expr
  1337. * @return string
  1338. */
  1339. function _expand_quoted_text($var_expr) {
  1340. // if contains unescaped $, expand it
  1341. if (preg_match_all('%(?:\`(?<!\\\\)\$' . $this->_dvar_guts_regexp . '\`)|(?:(?<!\\\\)\$\w+(\[[a-zA-Z0-9]+\])*)%', $var_expr, $_match)) {
  1342. $_match = $_match[0];
  1343. rsort($_match);
  1344. reset($_match);
  1345. foreach ($_match as $_var) {
  1346. $var_expr = str_replace($_var, '".(' . $this->_parse_var(str_replace('`', '', $_var)) . ')."', $var_expr);
  1347. }
  1348. $_return = preg_replace('%\.""|(?<!\\\\)""\.%', '', $var_expr);
  1349. } else {
  1350. $_return = $var_expr;
  1351. }
  1352. // replace double quoted literal string with single quotes
  1353. $_return = preg_replace('!^"([\s\w]+)"$!', "'\\1'", $_return);
  1354. return $_return;
  1355. }
  1356. /**
  1357. * parse variable expression into PHP code
  1358. *
  1359. * @param string $var_expr
  1360. * @return string
  1361. */
  1362. function _parse_var($var_expr) {
  1363. $_has_math = false;
  1364. $_math_vars = preg_split('!('.$this->_dvar_math_regexp.'|'.$this->_qstr_regexp.')!', $var_expr, -1, PREG_SPLIT_DELIM_CAPTURE);
  1365. if (count($_math_vars) > 1) {
  1366. $_first_var = "";
  1367. $_complete_var = "";
  1368. // simple check if there is any math, to stop recursion (due to modifiers with "xx % yy" as parameter)
  1369. foreach ($_math_vars as $_k => $_math_var) {
  1370. $_math_var = $_math_vars[$_k];
  1371. if (!empty($_math_var) || is_numeric($_math_var)) {
  1372. // hit a math operator, so process the stuff which came before it
  1373. if (preg_match('!^' . $this->_dvar_math_regexp . '$!', $_math_var)) {
  1374. $_has_math = true;
  1375. if (!empty($_complete_var) || is_numeric($_complete_var)) {
  1376. $_output .= $this->_parse_var($_complete_var);
  1377. }
  1378. // just output the math operator to php
  1379. $_output .= $_math_var;
  1380. if (empty($_first_var))
  1381. $_first_var = $_complete_var;
  1382. $_complete_var = "";
  1383. } else {
  1384. // fetch multiple -> (like $foo->bar->baz ) which wouldn't get fetched else, because it would only get $foo->bar and treat the ->baz as "-" ">baz" then
  1385. for ($_i = $_k + 1; $_i <= count($_math_vars); $_i += 2) {
  1386. // fetch -> because it gets splitted at - and move it back together
  1387. if ( /* prevent notice */ (isset($_math_vars[$_i]) && isset($_math_vars[$_i+1])) && ($_math_vars[$_i] === '-' && $_math_vars[$_i+1]{0} === '>')) {
  1388. $_math_var .= $_math_vars[$_i].$_math_vars[$_i+1];
  1389. $_math_vars[$_i] = $_math_vars[$_i+1] = '';
  1390. } else {
  1391. break;
  1392. }
  1393. }
  1394. $_complete_var .= $_math_var;
  1395. }
  1396. }
  1397. }
  1398. if ($_has_math) {
  1399. if (!empty($_complete_var) || is_numeric($_complete_var))
  1400. $_output .= $this->_parse_var($_complete_var, true);
  1401. // get the modifiers working (only the last var from math + modifier is left)
  1402. $var_expr = $_complete_var;
  1403. }
  1404. }
  1405. // prevent cutting of first digit in the number (we _definitly_ got a number if the first char is a digit)
  1406. if (is_numeric($var_expr{0}))
  1407. $_var_ref = $var_expr;
  1408. else
  1409. $_var_ref = substr($var_expr, 1);
  1410. if (!$_has_math) {
  1411. // get [foo] and .foo and ->foo and (...) pieces
  1412. preg_match_all('!(?:^\w+)|' . $this->_obj_params_regexp . '|(?:' . $this->_var_bracket_regexp . ')|->\$?\w+|\.\$?\w+|\S+!', $_var_ref, $match);
  1413. $_indexes = $match[0];
  1414. $_var_name = array_shift($_indexes);
  1415. /* Handle $smarty.* variable references as a special case. */
  1416. if ($_var_name == 'smarty') {
  1417. /*
  1418. * If the reference could be compiled, use the compiled output;
  1419. * otherwise, fall back on the $smarty variable generated at
  1420. * run-time.
  1421. */
  1422. if (($smarty_ref = $this->_compile_smarty_ref($_indexes)) !== null) {
  1423. $_output = $smarty_ref;
  1424. } else {
  1425. $_var_name = substr(array_shift($_indexes), 1);
  1426. $_output = "\$this->_smarty_vars['$_var_name']";
  1427. }
  1428. } elseif (is_numeric($_var_name) && is_numeric($var_expr{0})) {
  1429. // because . is the operator for accessing arrays thru inidizes we need to put it together again for floating point numbers
  1430. if (count($_indexes) > 0) {
  1431. $_var_name .= implode("", $_indexes);
  1432. $_indexes = array();
  1433. }
  1434. $_output = $_var_name;
  1435. } else {
  1436. $_output = "\$this->_tpl_vars['$_var_name']";
  1437. }
  1438. foreach ($_indexes as $_index) {
  1439. if ($_index{0} == '[') {
  1440. $_index = substr($_index, 1, -1);
  1441. if (is_numeric($_index)) {
  1442. $_output .= "[$_index]";
  1443. } elseif ($_index{0} == '$') {
  1444. if (strpos($_index, '.') !== false) {
  1445. $_output .= '[' . $this->_parse_var($_index) . ']';
  1446. } else {
  1447. $_output .= "[\$this->_tpl_vars['" . substr($_index, 1) . "']]";
  1448. }
  1449. } else {
  1450. $_var_parts = explode('.', $_index);
  1451. $_var_section = $_var_parts[0];
  1452. $_var_section_prop = isset($_var_parts[1]) ? $_var_parts[1] : 'index';
  1453. $_output .= "[\$this->_sections['$_var_section']['$_var_section_prop']]";
  1454. }
  1455. } else if ($_index{0} == '.') {
  1456. if ($_index{1} == '$')
  1457. $_output .= "[\$this->_tpl_vars['" . substr($_index, 2) . "']]";
  1458. else
  1459. $_output .= "['" . substr($_index, 1) . "']";
  1460. } else if (substr($_index, 0, 2) == '->') {
  1461. if (substr($_index, 2, 2) == '__') {
  1462. $this->_syntax_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
  1463. } elseif ($this->security && substr($_index, 2, 1) == '_') {
  1464. $this->_syntax_error('(secure) call to private object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
  1465. } elseif ($_index{2} == '$') {
  1466. if ($this->security) {
  1467. $this->_syntax_error('(secure) call to dynamic object member is not allowed', E_USER_ERROR, __FILE__, __LINE__);
  1468. } else {
  1469. $_output .= '->{(($_var=$this->_tpl_vars[\''.substr($_index, 3).'\']) && substr($_var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$_var\\"")}';
  1470. }
  1471. } else {
  1472. $_output .= $_index;
  1473. }
  1474. } elseif ($_index{0} == '(') {
  1475. $_index = $this->_parse_parenth_args($_index);
  1476. $_output .= $_index;
  1477. } else {
  1478. $_output .= $_index;
  1479. }
  1480. }
  1481. }
  1482. return $_output;
  1483. }
  1484. /**
  1485. * parse arguments in function call parenthesis
  1486. *
  1487. * @param string $parenth_args
  1488. * @return string
  1489. */
  1490. function _parse_parenth_args($parenth_args) {
  1491. preg_match_all('!' . $this->_param_regexp . '!', $parenth_args, $match);
  1492. $match = $match[0];
  1493. rsort($match);
  1494. reset($match);
  1495. $orig_vals = $match;
  1496. $this->_parse_vars_props($match);
  1497. return str_replace($orig_vals, $match, $parenth_args);
  1498. }
  1499. /**
  1500. * parse configuration variable expression into PHP code
  1501. *
  1502. * @param string $conf_var_expr
  1503. * @return unknown
  1504. */
  1505. function _parse_conf_var($conf_var_expr) {
  1506. $parts = explode('|', $conf_var_expr, 2);
  1507. $var_ref = $parts[0];
  1508. $modifiers = isset($parts[1]) ? $parts[1] : '';
  1509. $var_name = substr($var_ref, 1, -1);
  1510. $output = "\$this->_config[0]['vars']['$var_name']";
  1511. $this->_parse_modifiers($output, $modifiers);
  1512. return $output;
  1513. }
  1514. /**
  1515. * parse section property expression into PHP code
  1516. *
  1517. * @param string $section_prop_expr
  1518. * @return string
  1519. */
  1520. function _parse_section_prop($section_prop_expr) {
  1521. $parts = explode('|', $section_prop_expr, 2);
  1522. $var_ref = $parts[0];
  1523. $modifiers = isset($parts[1]) ? $parts[1] : '';
  1524. preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
  1525. $section_name = $match[1];
  1526. $prop_name = $match[2];
  1527. $output = "\$this->_sections['$section_name']['$prop_name']";
  1528. $this->_parse_modifiers($output, $modifiers);
  1529. return $output;
  1530. }
  1531. /**
  1532. * parse modifier chain into PHP code
  1533. *
  1534. * sets $output to parsed modified chain
  1535. *
  1536. * @param string $output
  1537. * @param string $modifier_string
  1538. */
  1539. function _parse_modifiers(&$output, $modifier_string) {
  1540. preg_match_all('!\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)!', '|' . $modifier_string, $_match);
  1541. list(, $_modifiers, $modifier_arg_strings) = $_match;
  1542. for ($_i = 0, $_for_max = count($_modifiers); $_i < $_for_max; $_i++) {
  1543. $_modifier_name = $_modifiers[$_i];
  1544. if ($_modifier_name == 'smarty') {
  1545. // skip smarty modifier
  1546. continue;
  1547. }
  1548. preg_match_all('!:(' . $this->_qstr_regexp . '|[^:]+)!', $modifier_arg_strings[$_i], $_match);
  1549. $_modifier_args = $_match[1];
  1550. if ($_modifier_name{0} == '@') {
  1551. $_map_array = false;
  1552. $_modifier_name = substr($_modifier_name, 1);
  1553. } else {
  1554. $_map_array = true;
  1555. }
  1556. $this->_add_plugin('modifier', $_modifier_name);
  1557. if (empty($this->_plugins['modifier'][$_modifier_name])
  1558. && !$this->_get_plugin_filepath('modifier', $_modifier_name)
  1559. && function_exists($_modifier_name)) {
  1560. if ($this->security && !in_array($_modifier_name, $this->security_settings['MODIFIER_FUNCS'])) {
  1561. $this->_trigger_fatal_error("[plugin] (secure mode) modifier '$_modifier_name' is not allowed" , $_tpl_file, $_tpl_line, __FILE__, __LINE__);
  1562. } else {
  1563. $this->_plugins['modifier'][$_modifier_name] = array($_modifier_name, null, null, false);
  1564. }
  1565. }
  1566. $this->_parse_vars_props($_modifier_args);
  1567. if ($_modifier_name == 'default') {
  1568. // supress notifications of default modifier vars and args
  1569. if ($output{0} == '$') {
  1570. $output = '@' . $output;
  1571. }
  1572. if (isset($_modifier_args[0]) && $_modifier_args[0]{0} == '$') {
  1573. $_modifier_args[0] = '@' . $_modifier_args[0];
  1574. }
  1575. }
  1576. if (count($_modifier_args) > 0)
  1577. $_modifier_args = ', '.implode(', ', $_modifier_args);
  1578. else
  1579. $_modifier_args = '';
  1580. if ($_map_array) {
  1581. $output = "((is_array(\$_tmp=$output)) ? \$this->_run_mod_handler('$_modifier_name', true, \$_tmp$_modifier_args) : " . $this->_compile_plugin_call('modifier', $_modifier_name) . "(\$_tmp$_modifier_args))";
  1582. } else {
  1583. $output = $this->_compile_plugin_call('modifier', $_modifier_name)."($output$_modifier_args)";
  1584. }
  1585. }
  1586. }
  1587. /**
  1588. * add plugin
  1589. *
  1590. * @param string $type
  1591. * @param string $name
  1592. * @param boolean? $delayed_loading
  1593. */
  1594. function _add_plugin($type, $name, $delayed_loading = null) {
  1595. if (!isset($this->_plugin_info[$type])) {
  1596. $this->_plugin_info[$type] = array();
  1597. }
  1598. if (!isset($this->_plugin_info[$type][$name])) {
  1599. $this->_plugin_info[$type][$name] = array($this->_current_file,
  1600. $this->_current_line_no,
  1601. $delayed_loading);
  1602. }
  1603. }
  1604. /**
  1605. * Compiles references of type $smarty.foo
  1606. *
  1607. * @param string $indexes
  1608. * @return string
  1609. */
  1610. function _compile_smarty_ref(&$indexes) {
  1611. /* Extract the reference name. */
  1612. $_ref = substr($indexes[0], 1);
  1613. foreach ($indexes as $_index_no=>$_index) {
  1614. if ($_index{0} != '.' && $_index_no<2 || !preg_match('!^(\.|\[|->)!', $_index)) {
  1615. $this->_syntax_error('$smarty' . implode('', array_slice($indexes, 0, 2)) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
  1616. }
  1617. }
  1618. switch ($_ref) {
  1619. case 'now':
  1620. $compiled_ref = 'time()';
  1621. $_max_index = 1;
  1622. break;
  1623. case 'foreach':
  1624. case 'section':
  1625. array_shift($indexes);
  1626. $_var = $this->_parse_var_props(substr($indexes[0], 1));
  1627. if ($_ref == 'foreach')
  1628. $compiled_ref = "\$this->_foreach[$_var]";
  1629. else
  1630. $compiled_ref = "\$this->_sections[$_var]";
  1631. break;
  1632. case 'get':
  1633. $compiled_ref = ($this->request_use_auto_globals) ? '$_GET' : "\$GLOBALS['HTTP_GET_VARS']";
  1634. break;
  1635. case 'post':
  1636. $compiled_ref = ($this->request_use_auto_globals) ? '$_POST' : "\$GLOBALS['HTTP_POST_VARS']";
  1637. break;
  1638. case 'cookies':
  1639. $compiled_ref = ($this->request_use_auto_globals) ? '$_COOKIE' : "\$GLOBALS['HTTP_COOKIE_VARS']";
  1640. break;
  1641. case 'env':
  1642. $compiled_ref = ($this->request_use_auto_globals) ? '$_ENV' : "\$GLOBALS['HTTP_ENV_VARS']";
  1643. break;
  1644. case 'server':
  1645. $compiled_ref = ($this->request_use_auto_globals) ? '$_SERVER' : "\$GLOBALS['HTTP_SERVER_VARS']";
  1646. break;
  1647. case 'session':
  1648. $compiled_ref = ($this->request_use_auto_globals) ? '$_SESSION' : "\$GLOBALS['HTTP_SESSION_VARS']";
  1649. break;
  1650. /*
  1651. * These cases are handled either at run-time or elsewhere in the
  1652. * compiler.
  1653. */
  1654. case 'request':
  1655. if ($this->request_use_auto_globals) {
  1656. $compiled_ref = '$_REQUEST';
  1657. break;
  1658. } else {
  1659. $this->_init_smarty_vars = true;
  1660. }
  1661. return null;
  1662. case 'capture':
  1663. return null;
  1664. case 'template':
  1665. $compiled_ref = "'$this->_current_file'";
  1666. $_max_index = 1;
  1667. break;
  1668. case 'version':
  1669. $compiled_ref = "'$this->_version'";
  1670. $_max_index = 1;
  1671. break;
  1672. case 'const':
  1673. array_shift($indexes);
  1674. $_val = $this->_parse_var_props(substr($indexes[0], 1));
  1675. $compiled_ref = '@constant(' . $_val . ')';
  1676. $_max_index = 1;
  1677. break;
  1678. case 'config':
  1679. $compiled_ref = "\$this->_config[0]['vars']";
  1680. $_max_index = 2;
  1681. break;
  1682. default:
  1683. $this->_syntax_error('$smarty.' . $_ref . ' is an unknown reference', E_USER_ERROR, __FILE__, __LINE__);
  1684. break;
  1685. }
  1686. if (isset($_max_index) && count($indexes) > $_max_index) {
  1687. $this->_syntax_error('$smarty' . implode('', $indexes) .' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
  1688. }
  1689. array_shift($indexes);
  1690. return $compiled_ref;
  1691. }
  1692. /**
  1693. * compiles call to plugin of type $type with name $name
  1694. * returns a string containing the function-name or method call
  1695. * without the paramter-list that would have follow to make the
  1696. * call valid php-syntax
  1697. *
  1698. * @param string $type
  1699. * @param string $name
  1700. * @return string
  1701. */
  1702. function _compile_plugin_call($type, $name) {
  1703. if (isset($this->_plugins[$type][$name])) {
  1704. /* plugin loaded */
  1705. if (is_array($this->_plugins[$type][$name][0])) {
  1706. return ((is_object($this->_plugins[$type][$name][0][0])) ?
  1707. "\$this->_plugins['$type']['$name'][0][0]->" /* method callback */
  1708. : (string)($this->_plugins[$type][$name][0][0]).'::' /* class callback */
  1709. ). $this->_plugins[$type][$name][0][1];
  1710. } else {
  1711. /* function callback */
  1712. return $this->_plugins[$type][$name][0];
  1713. }
  1714. } else {
  1715. /* plugin not loaded -> auto-loadable-plugin */
  1716. return 'smarty_'.$type.'_'.$name;
  1717. }
  1718. }
  1719. /**
  1720. * load pre- and post-filters
  1721. */
  1722. function _load_filters() {
  1723. if (count($this->_plugins['prefilter']) > 0) {
  1724. foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
  1725. if ($prefilter === false) {
  1726. unset($this->_plugins['prefilter'][$filter_name]);
  1727. $_params = array('plugins' => array(array('prefilter', $filter_name, null, null, false)));
  1728. require_once SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php';
  1729. smarty_core_load_plugins($_params, $this);
  1730. }
  1731. }
  1732. }
  1733. if (count($this->_plugins['postfilter']) > 0) {
  1734. foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
  1735. if ($postfilter === false) {
  1736. unset($this->_plugins['postfilter'][$filter_name]);
  1737. $_params = array('plugins' => array(array('postfilter', $filter_name, null, null, false)));
  1738. require_once SMARTY_DIR . 'core' . DIRECTORY_SEPARATOR . 'core.load_plugins.php';
  1739. smarty_core_load_plugins($_params, $this);
  1740. }
  1741. }
  1742. }
  1743. }
  1744. /**
  1745. * Quote subpattern references
  1746. *
  1747. * @param string $string
  1748. * @return string
  1749. */
  1750. function _quote_replace($string) {
  1751. return preg_replace('![\\$]\d!', '\\\\\\0', $string);
  1752. }
  1753. /**
  1754. * display Smarty syntax error
  1755. *
  1756. * @param string $error_msg
  1757. * @param integer $error_type
  1758. * @param string $file
  1759. * @param integer $line
  1760. */
  1761. function _syntax_error($error_msg, $error_type = E_USER_ERROR, $file=null, $line=null) {
  1762. if (isset($file) && isset($line)) {
  1763. $info = ' ('.basename($file).", line $line)";
  1764. } else {
  1765. $info = null;
  1766. }
  1767. trigger_error('Smarty: [in ' . $this->_current_file . ' line ' .
  1768. $this->_current_line_no . "]: syntax error: $error_msg$info", $error_type);
  1769. }
  1770. /**
  1771. * check if the compilation changes from cacheable to
  1772. * non-cacheable state with the beginning of the current
  1773. * plugin. return php-code to reflect the transition.
  1774. *
  1775. * @param unknown $type
  1776. * @param unknown $name
  1777. * @return string
  1778. */
  1779. function _push_cacheable_state($type, $name) {
  1780. $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
  1781. if ($_cacheable
  1782. || 0<$this->_cacheable_state++) return '';
  1783. if (!isset($this->_cache_serial)) $this->_cache_serial = md5(uniqid('Smarty'));
  1784. $_ret = 'if ($this->caching) { echo \'{nocache:'
  1785. . $this->_cache_serial . '#' . $this->_nocache_count
  1786. . '}\';}';
  1787. return $_ret;
  1788. }
  1789. /**
  1790. * check if the compilation changes from non-cacheable to
  1791. * cacheable state with the end of the current plugin return
  1792. * php-code to reflect the transition.
  1793. *
  1794. * @param unknown $type
  1795. * @param unknown $name
  1796. * @return string
  1797. */
  1798. function _pop_cacheable_state($type, $name) {
  1799. $_cacheable = !isset($this->_plugins[$type][$name]) || $this->_plugins[$type][$name][4];
  1800. if ($_cacheable
  1801. || --$this->_cacheable_state>0) return '';
  1802. return 'if ($this->caching) { echo \'{/nocache:'
  1803. . $this->_cache_serial . '#' . ($this->_nocache_count++)
  1804. . '}\';}';
  1805. }
  1806. }
  1807. /**
  1808. * compare to values by their string length
  1809. *
  1810. * @access private
  1811. * @param string $a
  1812. * @param string $b
  1813. * @return 0|-1|1
  1814. */
  1815. function _smarty_sort_length($a, $b) {
  1816. if ($a == $b)
  1817. return 0;
  1818. if (strlen($a) == strlen($b))
  1819. return ($a > $b) ? -1 : 1;
  1820. return (strlen($a) > strlen($b)) ? -1 : 1;
  1821. }
  1822. /* vim: set et: */
  1823. ?>