PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/ui/smarty/Smarty_Compiler.class.php

https://github.com/ibuildingsnl/ATK
PHP | 2381 lines | 1599 code | 295 blank | 487 comment | 377 complexity | ac14099e2b83834994a7292d440c6378 MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. include_once(atkconfig("atkroot")."atk/ui/smarty/Smarty.class.php");
  3. /**
  4. * Project: Smarty: the PHP compiling template engine
  5. * File: Smarty_Compiler.class.php
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20. *
  21. * @link http://smarty.php.net/
  22. * @author Monte Ohrt <monte at ohrt dot com>
  23. * @author Andrei Zmievski <andrei@php.net>
  24. * @version 2.6.11
  25. * @copyright 2001-2005 New Digital Group, Inc.
  26. * @package Smarty
  27. */
  28. /* $Id$ */
  29. /**
  30. * Template compiling class
  31. * @package Smarty
  32. */
  33. class Smarty_Compiler extends Smarty {
  34. // internal vars
  35. /**#@+
  36. * @access private
  37. */
  38. var $_folded_blocks = array(); // keeps folded template blocks
  39. var $_current_file = null; // the current template being compiled
  40. var $_current_line_no = 1; // line number for error messages
  41. var $_capture_stack = array(); // keeps track of nested capture buffers
  42. var $_plugin_info = array(); // keeps track of plugins to load
  43. var $_init_smarty_vars = false;
  44. var $_permitted_tokens = array('true','false','yes','no','on','off','null');
  45. var $_db_qstr_regexp = null; // regexps are setup in the constructor
  46. var $_si_qstr_regexp = null;
  47. var $_qstr_regexp = null;
  48. var $_func_regexp = null;
  49. var $_reg_obj_regexp = null;
  50. var $_var_bracket_regexp = null;
  51. var $_num_const_regexp = null;
  52. var $_dvar_guts_regexp = null;
  53. var $_dvar_regexp = null;
  54. var $_cvar_regexp = null;
  55. var $_svar_regexp = null;
  56. var $_avar_regexp = null;
  57. var $_mod_regexp = null;
  58. var $_var_regexp = null;
  59. var $_parenth_param_regexp = null;
  60. var $_func_call_regexp = null;
  61. var $_obj_ext_regexp = null;
  62. var $_obj_start_regexp = null;
  63. var $_obj_params_regexp = null;
  64. var $_obj_call_regexp = null;
  65. var $_cacheable_state = 0;
  66. var $_cache_attrs_count = 0;
  67. var $_nocache_count = 0;
  68. var $_cache_serial = null;
  69. var $_cache_include = null;
  70. var $_strip_depth = 0;
  71. var $_additional_newline = "\n";
  72. /**#@-*/
  73. /**
  74. * The class constructor.
  75. */
  76. function Smarty_Compiler()
  77. {
  78. // matches double quoted strings:
  79. // "foobar"
  80. // "foo\"bar"
  81. $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
  82. // matches single quoted strings:
  83. // 'foobar'
  84. // 'foo\'bar'
  85. $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
  86. // matches single or double quoted strings
  87. $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
  88. // matches bracket portion of vars
  89. // [0]
  90. // [foo]
  91. // [$bar]
  92. $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
  93. // matches numerical constants
  94. // 30
  95. // -12
  96. // 13.22
  97. $this->_num_const_regexp = '(?:\-?\d+(?:\.\d+)?)';
  98. // matches $ vars (not objects):
  99. // $foo
  100. // $foo.bar
  101. // $foo.bar.foobar
  102. // $foo[0]
  103. // $foo[$bar]
  104. // $foo[5][blah]
  105. // $foo[5].bar[$foobar][4]
  106. $this->_dvar_math_regexp = '(?:[\+\*\/\%]|(?:-(?!>)))';
  107. $this->_dvar_math_var_regexp = '[\$\w\.\+\-\*\/\%\d\>\[\]]';
  108. $this->_dvar_guts_regexp = '\w+(?:' . $this->_var_bracket_regexp
  109. . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?';
  110. $this->_dvar_regexp = '\$' . $this->_dvar_guts_regexp;
  111. // matches config vars:
  112. // #foo#
  113. // #foobar123_foo#
  114. $this->_cvar_regexp = '\#\w+\#';
  115. // matches section vars:
  116. // %foo.bar%
  117. $this->_svar_regexp = '\%\w+\.\w+\%';
  118. // matches all valid variables (no quotes, no modifiers)
  119. $this->_avar_regexp = '(?:' . $this->_dvar_regexp . '|'
  120. . $this->_cvar_regexp . '|' . $this->_svar_regexp . ')';
  121. // matches valid variable syntax:
  122. // $foo
  123. // $foo
  124. // #foo#
  125. // #foo#
  126. // "text"
  127. // "text"
  128. $this->_var_regexp = '(?:' . $this->_avar_regexp . '|' . $this->_qstr_regexp . ')';
  129. // matches valid object call (one level of object nesting allowed in parameters):
  130. // $foo->bar
  131. // $foo->bar()
  132. // $foo->bar("text")
  133. // $foo->bar($foo, $bar, "text")
  134. // $foo->bar($foo, "foo")
  135. // $foo->bar->foo()
  136. // $foo->bar->foo->bar()
  137. // $foo->bar($foo->bar)
  138. // $foo->bar($foo->bar())
  139. // $foo->bar($foo->bar($blah,$foo,44,"foo",$foo[0].bar))
  140. $this->_obj_ext_regexp = '\->(?:\$?' . $this->_dvar_guts_regexp . ')';
  141. $this->_obj_restricted_param_regexp = '(?:'
  142. . '(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')(?:' . $this->_obj_ext_regexp . '(?:\((?:(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . ')'
  143. . '(?:\s*,\s*(?:' . $this->_var_regexp . '|' . $this->_num_const_regexp . '))*)?\))?)*)';
  144. $this->_obj_single_param_regexp = '(?:\w+|' . $this->_obj_restricted_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
  145. . $this->_var_regexp . $this->_obj_restricted_param_regexp . ')))*)';
  146. $this->_obj_params_regexp = '\((?:' . $this->_obj_single_param_regexp
  147. . '(?:\s*,\s*' . $this->_obj_single_param_regexp . ')*)?\)';
  148. $this->_obj_start_regexp = '(?:' . $this->_dvar_regexp . '(?:' . $this->_obj_ext_regexp . ')+)';
  149. $this->_obj_call_regexp = '(?:' . $this->_obj_start_regexp . '(?:' . $this->_obj_params_regexp . ')?(?:' . $this->_dvar_math_regexp . '(?:' . $this->_num_const_regexp . '|' . $this->_dvar_math_var_regexp . ')*)?)';
  150. // matches valid modifier syntax:
  151. // |foo
  152. // |@foo
  153. // |foo:"bar"
  154. // |foo:$bar
  155. // |foo:"bar":$foobar
  156. // |foo|bar
  157. // |foo:$foo->bar
  158. $this->_mod_regexp = '(?:\|@?\w+(?::(?:\w+|' . $this->_num_const_regexp . '|'
  159. . $this->_obj_call_regexp . '|' . $this->_avar_regexp . '|' . $this->_qstr_regexp .'))*)';
  160. // matches valid function name:
  161. // foo123
  162. // _foo_bar
  163. $this->_func_regexp = '[a-zA-Z_]\w*';
  164. // matches valid registered object:
  165. // foo->bar
  166. $this->_reg_obj_regexp = '[a-zA-Z_]\w*->[a-zA-Z_]\w*';
  167. // matches valid parameter values:
  168. // true
  169. // $foo
  170. // $foo|bar
  171. // #foo#
  172. // #foo#|bar
  173. // "text"
  174. // "text"|bar
  175. // $foo->bar
  176. $this->_param_regexp = '(?:\s*(?:' . $this->_obj_call_regexp . '|'
  177. . $this->_var_regexp . '|' . $this->_num_const_regexp . '|\w+)(?>' . $this->_mod_regexp . '*)\s*)';
  178. // matches valid parenthesised function parameters:
  179. //
  180. // "text"
  181. // $foo, $bar, "text"
  182. // $foo|bar, "foo"|bar, $foo->bar($foo)|bar
  183. $this->_parenth_param_regexp = '(?:\((?:\w+|'
  184. . $this->_param_regexp . '(?:\s*,\s*(?:(?:\w+|'
  185. . $this->_param_regexp . ')))*)?\))';
  186. // matches valid function call:
  187. // foo()
  188. // foo_bar($foo)
  189. // _foo_bar($foo,"bar")
  190. // foo123($foo,$foo->bar(),"foo")
  191. $this->_func_call_regexp = '(?:' . $this->_func_regexp . '\s*(?:'
  192. . $this->_parenth_param_regexp . '))';
  193. }
  194. /**
  195. * compile a resource
  196. *
  197. * sets $compiled_content to the compiled source
  198. * @param string $resource_name
  199. * @param string $source_content
  200. * @param string $compiled_content
  201. * @return true
  202. */
  203. function _compile_file($resource_name, $source_content, &$compiled_content)
  204. {
  205. if ($this->security) {
  206. // do not allow php syntax to be executed unless specified
  207. if ($this->php_handling == SMARTY_PHP_ALLOW &&
  208. !$this->security_settings['PHP_HANDLING']) {
  209. $this->php_handling = SMARTY_PHP_PASSTHRU;
  210. }
  211. }
  212. $this->_load_filters();
  213. $this->_current_file = $resource_name;
  214. $this->_current_line_no = 1;
  215. $ldq = preg_quote($this->left_delimiter, '~');
  216. $rdq = preg_quote($this->right_delimiter, '~');
  217. // run template source through prefilter functions
  218. if (count($this->_plugins['prefilter']) > 0) {
  219. foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter) {
  220. if ($prefilter === false) continue;
  221. if ($prefilter[3] || is_callable($prefilter[0])) {
  222. $source_content = call_user_func_array($prefilter[0],
  223. array($source_content, &$this));
  224. $this->_plugins['prefilter'][$filter_name][3] = true;
  225. } else {
  226. $this->_trigger_fatal_error("[plugin] prefilter '$filter_name' is not implemented");
  227. }
  228. }
  229. }
  230. /* fetch all special blocks */
  231. $search = "~{$ldq}\*(.*?)\*{$rdq}|{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}|{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}~s";
  232. preg_match_all($search, $source_content, $match, PREG_SET_ORDER);
  233. $this->_folded_blocks = $match;
  234. reset($this->_folded_blocks);
  235. /* replace special blocks by "{php}" */
  236. $source_content = preg_replace($search.'e', "'"
  237. . $this->_quote_replace($this->left_delimiter) . 'php'
  238. . "' . str_repeat(\"\n\", substr_count('\\0', \"\n\")) .'"
  239. . $this->_quote_replace($this->right_delimiter)
  240. . "'"
  241. , $source_content);
  242. /* Gather all template tags. */
  243. preg_match_all("~{$ldq}\s*(.*?)\s*{$rdq}~s", $source_content, $_match);
  244. $template_tags = $_match[1];
  245. /* Split content by template tags to obtain non-template content. */
  246. $text_blocks = preg_split("~{$ldq}.*?{$rdq}~s", $source_content);
  247. /* loop through text blocks */
  248. for ($curr_tb = 0, $for_max = count($text_blocks); $curr_tb < $for_max; $curr_tb++) {
  249. /* match anything resembling php tags */
  250. if (preg_match_all('~(<\?(?:\w+|=)?|\?>|language\s*=\s*[\"\']?php[\"\']?)~is', $text_blocks[$curr_tb], $sp_match)) {
  251. /* replace tags with placeholders to prevent recursive replacements */
  252. $sp_match[1] = array_unique($sp_match[1]);
  253. usort($sp_match[1], '_smarty_sort_length');
  254. for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
  255. $text_blocks[$curr_tb] = str_replace($sp_match[1][$curr_sp],'%%%SMARTYSP'.$curr_sp.'%%%',$text_blocks[$curr_tb]);
  256. }
  257. /* process each one */
  258. for ($curr_sp = 0, $for_max2 = count($sp_match[1]); $curr_sp < $for_max2; $curr_sp++) {
  259. if ($this->php_handling == SMARTY_PHP_PASSTHRU) {
  260. /* echo php contents */
  261. $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '<?php echo \''.str_replace("'", "\'", $sp_match[1][$curr_sp]).'\'; ?>'."\n", $text_blocks[$curr_tb]);
  262. } else if ($this->php_handling == SMARTY_PHP_QUOTE) {
  263. /* quote php tags */
  264. $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', htmlspecialchars($sp_match[1][$curr_sp]), $text_blocks[$curr_tb]);
  265. } else if ($this->php_handling == SMARTY_PHP_REMOVE) {
  266. /* remove php tags */
  267. $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', '', $text_blocks[$curr_tb]);
  268. } else {
  269. /* SMARTY_PHP_ALLOW, but echo non php starting tags */
  270. $sp_match[1][$curr_sp] = preg_replace('~(<\?(?!php|=|$))~i', '<?php echo \'\\1\'?>'."\n", $sp_match[1][$curr_sp]);
  271. $text_blocks[$curr_tb] = str_replace('%%%SMARTYSP'.$curr_sp.'%%%', $sp_match[1][$curr_sp], $text_blocks[$curr_tb]);
  272. }
  273. }
  274. }
  275. }
  276. /* Compile the template tags into PHP code. */
  277. $compiled_tags = array();
  278. for ($i = 0, $for_max = count($template_tags); $i < $for_max; $i++) {
  279. $this->_current_line_no += substr_count($text_blocks[$i], "\n");
  280. $compiled_tags[] = $this->_compile_tag($template_tags[$i]);
  281. $this->_current_line_no += substr_count($template_tags[$i], "\n");
  282. }
  283. if (count($this->_tag_stack)>0) {
  284. list($_open_tag, $_line_no) = end($this->_tag_stack);
  285. $this->_syntax_error("unclosed tag \{$_open_tag} (opened line $_line_no).", E_USER_ERROR, __FILE__, __LINE__);
  286. return;
  287. }
  288. /* Reformat $text_blocks between 'strip' and '/strip' tags,
  289. removing spaces, tabs and newlines. */
  290. $strip = false;
  291. for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
  292. if ($compiled_tags[$i] == '{strip}') {
  293. $compiled_tags[$i] = '';
  294. $strip = true;
  295. /* remove leading whitespaces */
  296. $text_blocks[$i + 1] = ltrim($text_blocks[$i + 1]);
  297. }
  298. if ($strip) {
  299. /* strip all $text_blocks before the next '/strip' */
  300. for ($j = $i + 1; $j < $for_max; $j++) {
  301. /* remove leading and trailing whitespaces of each line */
  302. $text_blocks[$j] = preg_replace('![\t ]*[\r\n]+[\t ]*!', '', $text_blocks[$j]);
  303. if ($compiled_tags[$j] == '{/strip}') {
  304. /* remove trailing whitespaces from the last text_block */
  305. $text_blocks[$j] = rtrim($text_blocks[$j]);
  306. }
  307. $text_blocks[$j] = "<?php echo '" . strtr($text_blocks[$j], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>";
  308. if ($compiled_tags[$j] == '{/strip}') {
  309. $compiled_tags[$j] = "\n"; /* slurped by php, but necessary
  310. if a newline is following the closing strip-tag */
  311. $strip = false;
  312. $i = $j;
  313. break;
  314. }
  315. }
  316. }
  317. }
  318. $compiled_content = '';
  319. /* Interleave the compiled contents and text blocks to get the final result. */
  320. for ($i = 0, $for_max = count($compiled_tags); $i < $for_max; $i++) {
  321. if ($compiled_tags[$i] == '') {
  322. // tag result empty, remove first newline from following text block
  323. $text_blocks[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text_blocks[$i+1]);
  324. }
  325. $compiled_content .= $text_blocks[$i].$compiled_tags[$i];
  326. }
  327. $compiled_content .= $text_blocks[$i];
  328. // remove \n from the end of the file, if any
  329. if (strlen($compiled_content) && (substr($compiled_content, -1) == "\n") ) {
  330. $compiled_content = substr($compiled_content, 0, -1);
  331. }
  332. if (!empty($this->_cache_serial)) {
  333. $compiled_content = "<?php \$this->_cache_serials['".$this->_cache_include."'] = '".$this->_cache_serial."'; ?>" . $compiled_content;
  334. }
  335. // remove unnecessary close/open tags
  336. $compiled_content = preg_replace('~\?>\n?<\?php~', '', $compiled_content);
  337. // run compiled template through postfilter functions
  338. if (count($this->_plugins['postfilter']) > 0) {
  339. foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter) {
  340. if ($postfilter === false) continue;
  341. if ($postfilter[3] || is_callable($postfilter[0])) {
  342. $compiled_content = call_user_func_array($postfilter[0],
  343. array($compiled_content, &$this));
  344. $this->_plugins['postfilter'][$filter_name][3] = true;
  345. } else {
  346. $this->_trigger_fatal_error("Smarty plugin error: postfilter '$filter_name' is not implemented");
  347. }
  348. }
  349. }
  350. // put header at the top of the compiled template
  351. $template_header = "<?php /* Smarty version ".$this->_version.", created on ".strftime("%Y-%m-%d %H:%M:%S")."\n";
  352. $template_header .= " compiled from ".strtr(urlencode($resource_name), array('%2F'=>'/', '%3A'=>':'))." */ ?>\n";
  353. /* Emit code to load needed plugins. */
  354. $this->_plugins_code = '';
  355. if (count($this->_plugin_info)) {
  356. $_plugins_params = "array('plugins' => array(";
  357. foreach ($this->_plugin_info as $plugin_type => $plugins) {
  358. foreach ($plugins as $plugin_name => $plugin_info) {
  359. $_plugins_params .= "array('$plugin_type', '$plugin_name', '" . strtr($plugin_info[0], array("'" => "\\'", "\\" => "\\\\")) . "', $plugin_info[1], ";
  360. $_plugins_params .= $plugin_info[2] ? 'true),' : 'false),';
  361. }
  362. }
  363. $_plugins_params .= '))';
  364. $plugins_code = "<?php require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');\nsmarty_core_load_plugins($_plugins_params, \$this); ?>\n";
  365. $template_header .= $plugins_code;
  366. $this->_plugin_info = array();
  367. $this->_plugins_code = $plugins_code;
  368. }
  369. if ($this->_init_smarty_vars) {
  370. $template_header .= "<?php require_once(SMARTY_CORE_DIR . 'core.assign_smarty_interface.php');\nsmarty_core_assign_smarty_interface(null, \$this); ?>\n";
  371. $this->_init_smarty_vars = false;
  372. }
  373. $compiled_content = $template_header . $compiled_content;
  374. return true;
  375. }
  376. /**
  377. * Compile a template tag
  378. *
  379. * @param string $template_tag
  380. * @return string
  381. */
  382. function _compile_tag($template_tag)
  383. {
  384. /* Matched comment. */
  385. if (substr($template_tag, 0, 1) == '*' && substr($template_tag, -1) == '*')
  386. return '';
  387. // PETER / TJEERD HACK: replace &nbsp; which is directly behind a funciton call
  388. $template_tag = preg_replace("/^({$this->_func_regexp})&nbsp;/", "\\1 ", $template_tag);
  389. /* Split tag into two three parts: command, command modifiers and the arguments. */
  390. if(! preg_match('~^(?:(' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp
  391. . '|\/?' . $this->_reg_obj_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*))
  392. (?:\s+(.*))?$
  393. ~xs', $template_tag, $match)) {
  394. $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
  395. }
  396. $tag_command = $match[1];
  397. $tag_modifier = isset($match[2]) ? $match[2] : null;
  398. $tag_args = isset($match[3]) ? $match[3] : null;
  399. if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {
  400. /* tag name is a variable or object */
  401. $_return = $this->_parse_var_props($tag_command . $tag_modifier);
  402. return "<?php echo $_return; ?>" . $this->_additional_newline;
  403. }
  404. /* If the tag name is a registered object, we process it. */
  405. if (preg_match('~^\/?' . $this->_reg_obj_regexp . '$~', $tag_command)) {
  406. return $this->_compile_registered_object_tag($tag_command, $this->_parse_attrs($tag_args), $tag_modifier);
  407. }
  408. switch ($tag_command) {
  409. case 'include':
  410. return $this->_compile_include_tag($tag_args);
  411. case 'include_php':
  412. return $this->_compile_include_php_tag($tag_args);
  413. case 'if':
  414. $this->_push_tag('if');
  415. return $this->_compile_if_tag($tag_args);
  416. case 'else':
  417. list($_open_tag) = end($this->_tag_stack);
  418. if ($_open_tag != 'if' && $_open_tag != 'elseif')
  419. $this->_syntax_error('unexpected {else}', E_USER_ERROR, __FILE__, __LINE__);
  420. else
  421. $this->_push_tag('else');
  422. return '<?php else: ?>';
  423. case 'elseif':
  424. list($_open_tag) = end($this->_tag_stack);
  425. if ($_open_tag != 'if' && $_open_tag != 'elseif')
  426. $this->_syntax_error('unexpected {elseif}', E_USER_ERROR, __FILE__, __LINE__);
  427. if ($_open_tag == 'if')
  428. $this->_push_tag('elseif');
  429. return $this->_compile_if_tag($tag_args, true);
  430. case '/if':
  431. $this->_pop_tag('if');
  432. return '<?php endif; ?>';
  433. case 'capture':
  434. return $this->_compile_capture_tag(true, $tag_args);
  435. case '/capture':
  436. return $this->_compile_capture_tag(false);
  437. case 'ldelim':
  438. return $this->left_delimiter;
  439. case 'rdelim':
  440. return $this->right_delimiter;
  441. case 'section':
  442. $this->_push_tag('section');
  443. return $this->_compile_section_start($tag_args);
  444. case 'sectionelse':
  445. $this->_push_tag('sectionelse');
  446. return "<?php endfor; else: ?>";
  447. break;
  448. case '/section':
  449. $_open_tag = $this->_pop_tag('section');
  450. if ($_open_tag == 'sectionelse')
  451. return "<?php endif; ?>";
  452. else
  453. return "<?php endfor; endif; ?>";
  454. case 'foreach':
  455. $this->_push_tag('foreach');
  456. return $this->_compile_foreach_start($tag_args);
  457. break;
  458. case 'foreachelse':
  459. $this->_push_tag('foreachelse');
  460. return "<?php endforeach; else: ?>";
  461. case '/foreach':
  462. $_open_tag = $this->_pop_tag('foreach');
  463. if ($_open_tag == 'foreachelse')
  464. return "<?php endif; unset(\$_from); ?>";
  465. else
  466. return "<?php endforeach; endif; unset(\$_from); ?>";
  467. break;
  468. case 'strip':
  469. case '/strip':
  470. if (substr($tag_command, 0, 1)=='/') {
  471. $this->_pop_tag('strip');
  472. if (--$this->_strip_depth==0) { /* outermost closing {/strip} */
  473. $this->_additional_newline = "\n";
  474. return '{' . $tag_command . '}';
  475. }
  476. } else {
  477. $this->_push_tag('strip');
  478. if ($this->_strip_depth++==0) { /* outermost opening {strip} */
  479. $this->_additional_newline = "";
  480. return '{' . $tag_command . '}';
  481. }
  482. }
  483. return '';
  484. case 'php':
  485. /* handle folded tags replaced by {php} */
  486. list(, $block) = each($this->_folded_blocks);
  487. $this->_current_line_no += substr_count($block[0], "\n");
  488. /* the number of matched elements in the regexp in _compile_file()
  489. determins the type of folded tag that was found */
  490. switch (count($block)) {
  491. case 2: /* comment */
  492. return '';
  493. case 3: /* literal */
  494. return "<?php echo '" . strtr($block[2], array("'"=>"\'", "\\"=>"\\\\")) . "'; ?>" . $this->_additional_newline;
  495. case 4: /* php */
  496. if ($this->security && !$this->security_settings['PHP_TAGS']) {
  497. $this->_syntax_error("(secure mode) php tags not permitted", E_USER_WARNING, __FILE__, __LINE__);
  498. return;
  499. }
  500. return '<?php ' . $block[3] .' ?>';
  501. }
  502. break;
  503. case 'insert':
  504. return $this->_compile_insert_tag($tag_args);
  505. default:
  506. if ($this->_compile_compiler_tag($tag_command, $tag_args, $output)) {
  507. return $output;
  508. } else if ($this->_compile_block_tag($tag_command, $tag_args, $tag_modifier, $output)) {
  509. return $output;
  510. } else if ($this->_compile_custom_tag($tag_command, $tag_args, $tag_modifier, $output)) {
  511. return $output;
  512. } else {
  513. $this->_syntax_error("unrecognized tag '$tag_command'", E_USER_ERROR, __FILE__, __LINE__);
  514. }
  515. }
  516. }
  517. /**
  518. * compile the custom compiler tag
  519. *
  520. * sets $output to the compiled custom compiler tag
  521. * @param string $tag_command
  522. * @param string $tag_args
  523. * @param string $output
  524. * @return boolean
  525. */
  526. function _compile_compiler_tag($tag_command, $tag_args, &$output)
  527. {
  528. $found = false;
  529. $have_function = true;
  530. /*
  531. * First we check if the compiler function has already been registered
  532. * or loaded from a plugin file.
  533. */
  534. if (isset($this->_plugins['compiler'][$tag_command])) {
  535. $found = true;
  536. $plugin_func = $this->_plugins['compiler'][$tag_command][0];
  537. if (!is_callable($plugin_func)) {
  538. $message = "compiler function '$tag_command' is not implemented";
  539. $have_function = false;
  540. }
  541. }
  542. /*
  543. * Otherwise we need to load plugin file and look for the function
  544. * inside it.
  545. */
  546. else if ($plugin_file = $this->_get_plugin_filepath('compiler', $tag_command)) {
  547. $found = true;
  548. include_once $plugin_file;
  549. $plugin_func = 'smarty_compiler_' . $tag_command;
  550. if (!is_callable($plugin_func)) {
  551. $message = "plugin function $plugin_func() not found in $plugin_file\n";
  552. $have_function = false;
  553. } else {
  554. $this->_plugins['compiler'][$tag_command] = array($plugin_func, null, null, null, true);
  555. }
  556. }
  557. /*
  558. * True return value means that we either found a plugin or a
  559. * dynamically registered function. False means that we didn't and the
  560. * compiler should now emit code to load custom function plugin for this
  561. * tag.
  562. */
  563. if ($found) {
  564. if ($have_function) {
  565. $output = call_user_func_array($plugin_func, array($tag_args, &$this));
  566. if($output != '') {
  567. $output = '<?php ' . $this->_push_cacheable_state('compiler', $tag_command)
  568. . $output
  569. . $this->_pop_cacheable_state('compiler', $tag_command) . ' ?>';
  570. }
  571. } else {
  572. $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
  573. }
  574. return true;
  575. } else {
  576. return false;
  577. }
  578. }
  579. /**
  580. * compile block function tag
  581. *
  582. * sets $output to compiled block function tag
  583. * @param string $tag_command
  584. * @param string $tag_args
  585. * @param string $tag_modifier
  586. * @param string $output
  587. * @return boolean
  588. */
  589. function _compile_block_tag($tag_command, $tag_args, $tag_modifier, &$output)
  590. {
  591. if (substr($tag_command, 0, 1) == '/') {
  592. $start_tag = false;
  593. $tag_command = substr($tag_command, 1);
  594. } else
  595. $start_tag = true;
  596. $found = false;
  597. $have_function = true;
  598. /*
  599. * First we check if the block function has already been registered
  600. * or loaded from a plugin file.
  601. */
  602. if (isset($this->_plugins['block'][$tag_command])) {
  603. $found = true;
  604. $plugin_func = $this->_plugins['block'][$tag_command][0];
  605. if (!is_callable($plugin_func)) {
  606. $message = "block function '$tag_command' is not implemented";
  607. $have_function = false;
  608. }
  609. }
  610. /*
  611. * Otherwise we need to load plugin file and look for the function
  612. * inside it.
  613. */
  614. else if ($plugin_file = $this->_get_plugin_filepath('block', $tag_command)) {
  615. $found = true;
  616. include_once $plugin_file;
  617. $plugin_func = 'smarty_block_' . $tag_command;
  618. if (!function_exists($plugin_func)) {
  619. $message = "plugin function $plugin_func() not found in $plugin_file\n";
  620. $have_function = false;
  621. } else {
  622. $this->_plugins['block'][$tag_command] = array($plugin_func, null, null, null, true);
  623. }
  624. }
  625. if (!$found) {
  626. return false;
  627. } else if (!$have_function) {
  628. $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
  629. return true;
  630. }
  631. /*
  632. * Even though we've located the plugin function, compilation
  633. * happens only once, so the plugin will still need to be loaded
  634. * at runtime for future requests.
  635. */
  636. $this->_add_plugin('block', $tag_command);
  637. if ($start_tag)
  638. {
  639. $this->_push_tag($tag_command);
  640. }
  641. else
  642. {
  643. // PETER / TJEERD HACK, because _pop_tag doesn't kill the entire script anymore
  644. // we must somehow prevent the close tag from being parsed because it can lead to
  645. // parse errors (blocks of codes that get closed which were never opened etc.)
  646. if ($this->_pop_tag($tag_command) === false) return true;
  647. }
  648. if ($start_tag) {
  649. $output = '<?php ' . $this->_push_cacheable_state('block', $tag_command);
  650. $attrs = $this->_parse_attrs($tag_args);
  651. $_cache_attrs='';
  652. $arg_list = $this->_compile_arg_list('block', $tag_command, $attrs, $_cache_attrs);
  653. $output .= "$_cache_attrs\$this->_tag_stack[] = array('$tag_command', array(".implode(',', $arg_list).')); ';
  654. $output .= '$_block_repeat=true;' . $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], null, $this, $_block_repeat);';
  655. $output .= 'while ($_block_repeat) { ob_start(); ?>';
  656. } else {
  657. $output = '<?php $_block_content = ob_get_contents(); ob_end_clean(); ';
  658. $_out_tag_text = $this->_compile_plugin_call('block', $tag_command).'($this->_tag_stack[count($this->_tag_stack)-1][1], $_block_content, $this, $_block_repeat)';
  659. if ($tag_modifier != '') {
  660. $this->_parse_modifiers($_out_tag_text, $tag_modifier);
  661. }
  662. $output .= '$_block_repeat=false;echo ' . $_out_tag_text . '; } ';
  663. $output .= " array_pop(\$this->_tag_stack); " . $this->_pop_cacheable_state('block', $tag_command) . '?>';
  664. }
  665. return true;
  666. }
  667. /**
  668. * compile custom function tag
  669. *
  670. * @param string $tag_command
  671. * @param string $tag_args
  672. * @param string $tag_modifier
  673. * @return string
  674. */
  675. function _compile_custom_tag($tag_command, $tag_args, $tag_modifier, &$output)
  676. {
  677. $found = false;
  678. $have_function = true;
  679. /*
  680. * First we check if the custom function has already been registered
  681. * or loaded from a plugin file.
  682. */
  683. if (isset($this->_plugins['function'][$tag_command])) {
  684. $found = true;
  685. $plugin_func = $this->_plugins['function'][$tag_command][0];
  686. if (!is_callable($plugin_func)) {
  687. $message = "custom function '$tag_command' is not implemented";
  688. $have_function = false;
  689. }
  690. }
  691. /*
  692. * Otherwise we need to load plugin file and look for the function
  693. * inside it.
  694. */
  695. else if ($plugin_file = $this->_get_plugin_filepath('function', $tag_command)) {
  696. $found = true;
  697. include_once $plugin_file;
  698. $plugin_func = 'smarty_function_' . $tag_command;
  699. if (!function_exists($plugin_func)) {
  700. $message = "plugin function $plugin_func() not found in $plugin_file\n";
  701. $have_function = false;
  702. } else {
  703. $this->_plugins['function'][$tag_command] = array($plugin_func, null, null, null, true);
  704. }
  705. }
  706. if (!$found) {
  707. return false;
  708. } else if (!$have_function) {
  709. $this->_syntax_error($message, E_USER_WARNING, __FILE__, __LINE__);
  710. return true;
  711. }
  712. /* declare plugin to be loaded on display of the template that
  713. we compile right now */
  714. $this->_add_plugin('function', $tag_command);
  715. $_cacheable_state = $this->_push_cacheable_state('function', $tag_command);
  716. $attrs = $this->_parse_attrs($tag_args);
  717. $arg_list = $this->_compile_arg_list('function', $tag_command, $attrs, $_cache_attrs='');
  718. $output = $this->_compile_plugin_call('function', $tag_command).'(array('.implode(',', $arg_list)."), \$this)";
  719. if($tag_modifier != '') {
  720. $this->_parse_modifiers($output, $tag_modifier);
  721. }
  722. if($output != '') {
  723. $output = '<?php ' . $_cacheable_state . $_cache_attrs . 'echo ' . $output . ';'
  724. . $this->_pop_cacheable_state('function', $tag_command) . "?>" . $this->_additional_newline;
  725. }
  726. return true;
  727. }
  728. /**
  729. * compile a registered object tag
  730. *
  731. * @param string $tag_command
  732. * @param array $attrs
  733. * @param string $tag_modifier
  734. * @return string
  735. */
  736. function _compile_registered_object_tag($tag_command, $attrs, $tag_modifier)
  737. {
  738. if (substr($tag_command, 0, 1) == '/') {
  739. $start_tag = false;
  740. $tag_command = substr($tag_command, 1);
  741. } else {
  742. $start_tag = true;
  743. }
  744. list($object, $obj_comp) = explode('->', $tag_command);
  745. $arg_list = array();
  746. if(count($attrs)) {
  747. $_assign_var = false;
  748. foreach ($attrs as $arg_name => $arg_value) {
  749. if($arg_name == 'assign') {
  750. $_assign_var = $arg_value;
  751. unset($attrs['assign']);
  752. continue;
  753. }
  754. if (is_bool($arg_value))
  755. $arg_value = $arg_value ? 'true' : 'false';
  756. $arg_list[] = "'$arg_name' => $arg_value";
  757. }
  758. }
  759. if($this->_reg_objects[$object][2]) {
  760. // smarty object argument format
  761. $args = "array(".implode(',', (array)$arg_list)."), \$this";
  762. } else {
  763. // traditional argument format
  764. $args = implode(',', array_values($attrs));
  765. if (empty($args)) {
  766. $args = 'null';
  767. }
  768. }
  769. $prefix = '';
  770. $postfix = '';
  771. $newline = '';
  772. if(!is_object($this->_reg_objects[$object][0])) {
  773. $this->_trigger_fatal_error("registered '$object' is not an object" , $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
  774. } elseif(!empty($this->_reg_objects[$object][1]) && !in_array($obj_comp, $this->_reg_objects[$object][1])) {
  775. $this->_trigger_fatal_error("'$obj_comp' is not a registered component of object '$object'", $this->_current_file, $this->_current_line_no, __FILE__, __LINE__);
  776. } elseif(method_exists($this->_reg_objects[$object][0], $obj_comp)) {
  777. // method
  778. if(in_array($obj_comp, $this->_reg_objects[$object][3])) {
  779. // block method
  780. if ($start_tag) {
  781. $prefix = "\$this->_tag_stack[] = array('$obj_comp', $args); ";
  782. $prefix .= "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], null, \$this, \$_block_repeat=true); ";
  783. $prefix .= "while (\$_block_repeat) { ob_start();";
  784. $return = null;
  785. $postfix = '';
  786. } else {
  787. $prefix = "\$_obj_block_content = ob_get_contents(); ob_end_clean(); ";
  788. $return = "\$this->_reg_objects['$object'][0]->$obj_comp(\$this->_tag_stack[count(\$this->_tag_stack)-1][1], \$_obj_block_content, \$this, \$_block_repeat=false)";
  789. $postfix = "} array_pop(\$this->_tag_stack);";
  790. }
  791. } else {
  792. // non-block method
  793. $return = "\$this->_reg_objects['$object'][0]->$obj_comp($args)";
  794. }
  795. } else {
  796. // property
  797. $return = "\$this->_reg_objects['$object'][0]->$obj_comp";
  798. }
  799. if($return != null) {
  800. if($tag_modifier != '') {
  801. $this->_parse_modifiers($return, $tag_modifier);
  802. }
  803. if(!empty($_assign_var)) {
  804. $output = "\$this->assign('" . $this->_dequote($_assign_var) ."', $return);";
  805. } else {
  806. $output = 'echo ' . $return . ';';
  807. $newline = $this->_additional_newline;
  808. }
  809. } else {
  810. $output = '';
  811. }
  812. return '<?php ' . $prefix . $output . $postfix . "?>" . $newline;
  813. }
  814. /**
  815. * Compile {insert ...} tag
  816. *
  817. * @param string $tag_args
  818. * @return string
  819. */
  820. function _compile_insert_tag($tag_args)
  821. {
  822. $attrs = $this->_parse_attrs($tag_args);
  823. $name = $this->_dequote($attrs['name']);
  824. if (empty($name)) {
  825. $this->_syntax_error("missing insert name", E_USER_ERROR, __FILE__, __LINE__);
  826. }
  827. if (!empty($attrs['script'])) {
  828. $delayed_loading = true;
  829. } else {
  830. $delayed_loading = false;
  831. }
  832. foreach ($attrs as $arg_name => $arg_value) {
  833. if (is_bool($arg_value))
  834. $arg_value = $arg_value ? 'true' : 'false';
  835. $arg_list[] = "'$arg_name' => $arg_value";
  836. }
  837. $this->_add_plugin('insert', $name, $delayed_loading);
  838. $_params = "array('args' => array(".implode(', ', (array)$arg_list)."))";
  839. return "<?php require_once(SMARTY_CORE_DIR . 'core.run_insert_handler.php');\necho smarty_core_run_insert_handler($_params, \$this); ?>" . $this->_additional_newline;
  840. }
  841. /**
  842. * Compile {include ...} tag
  843. *
  844. * @param string $tag_args
  845. * @return string
  846. */
  847. function _compile_include_tag($tag_args)
  848. {
  849. $attrs = $this->_parse_attrs($tag_args);
  850. $arg_list = array();
  851. if (empty($attrs['file'])) {
  852. $this->_syntax_error("missing 'file' attribute in include tag", E_USER_ERROR, __FILE__, __LINE__);
  853. }
  854. foreach ($attrs as $arg_name => $arg_value) {
  855. if ($arg_name == 'file') {
  856. $include_file = $arg_value;
  857. continue;
  858. } else if ($arg_name == 'assign') {
  859. $assign_var = $arg_value;
  860. continue;
  861. }
  862. if (is_bool($arg_value))
  863. $arg_value = $arg_value ? 'true' : 'false';
  864. $arg_list[] = "'$arg_name' => $arg_value";
  865. }
  866. $output = '<?php ';
  867. if (isset($assign_var)) {
  868. $output .= "ob_start();\n";
  869. }
  870. $output .=
  871. "\$_smarty_tpl_vars = \$this->_tpl_vars;\n";
  872. $_params = "array('smarty_include_tpl_file' => " . $include_file . ", 'smarty_include_vars' => array(".implode(',', (array)$arg_list)."))";
  873. $output .= "\$this->_smarty_include($_params);\n" .
  874. "\$this->_tpl_vars = \$_smarty_tpl_vars;\n" .
  875. "unset(\$_smarty_tpl_vars);\n";
  876. if (isset($assign_var)) {
  877. $output .= "\$this->assign(" . $assign_var . ", ob_get_contents()); ob_end_clean();\n";
  878. }
  879. $output .= ' ?>';
  880. return $output;
  881. }
  882. /**
  883. * Compile {include ...} tag
  884. *
  885. * @param string $tag_args
  886. * @return string
  887. */
  888. function _compile_include_php_tag($tag_args)
  889. {
  890. $attrs = $this->_parse_attrs($tag_args);
  891. if (empty($attrs['file'])) {
  892. $this->_syntax_error("missing 'file' attribute in include_php tag", E_USER_ERROR, __FILE__, __LINE__);
  893. }
  894. $assign_var = (empty($attrs['assign'])) ? '' : $this->_dequote($attrs['assign']);
  895. $once_var = (empty($attrs['once']) || $attrs['once']=='false') ? 'false' : 'true';
  896. $arg_list = array();
  897. foreach($attrs as $arg_name => $arg_value) {
  898. if($arg_name != 'file' AND $arg_name != 'once' AND $arg_name != 'assign') {
  899. if(is_bool($arg_value))
  900. $arg_value = $arg_value ? 'true' : 'false';
  901. $arg_list[] = "'$arg_name' => $arg_value";
  902. }
  903. }
  904. $_params = "array('smarty_file' => " . $attrs['file'] . ", 'smarty_assign' => '$assign_var', 'smarty_once' => $once_var, 'smarty_include_vars' => array(".implode(',', $arg_list)."))";
  905. return "<?php require_once(SMARTY_CORE_DIR . 'core.smarty_include_php.php');\nsmarty_core_smarty_include_php($_params, \$this); ?>" . $this->_additional_newline;
  906. }
  907. /**
  908. * Compile {section ...} tag
  909. *
  910. * @param string $tag_args
  911. * @return string
  912. */
  913. function _compile_section_start($tag_args)
  914. {
  915. $attrs = $this->_parse_attrs($tag_args);
  916. $arg_list = array();
  917. $output = '<?php ';
  918. $section_name = $attrs['name'];
  919. if (empty($section_name)) {
  920. $this->_syntax_error("missing section name", E_USER_ERROR, __FILE__, __LINE__);
  921. }
  922. $output .= "unset(\$this->_sections[$section_name]);\n";
  923. $section_props = "\$this->_sections[$section_name]";
  924. foreach ($attrs as $attr_name => $attr_value) {
  925. switch ($attr_name) {
  926. case 'loop':
  927. $output .= "{$section_props}['loop'] = is_array(\$_loop=$attr_value) ? count(\$_loop) : max(0, (int)\$_loop); unset(\$_loop);\n";
  928. break;
  929. case 'show':
  930. if (is_bool($attr_value))
  931. $show_attr_value = $attr_value ? 'true' : 'false';
  932. else
  933. $show_attr_value = "(bool)$attr_value";
  934. $output .= "{$section_props}['show'] = $show_attr_value;\n";
  935. break;
  936. case 'name':
  937. $output .= "{$section_props}['$attr_name'] = $attr_value;\n";
  938. break;
  939. case 'max':
  940. case 'start':
  941. $output .= "{$section_props}['$attr_name'] = (int)$attr_value;\n";
  942. break;
  943. case 'step':
  944. $output .= "{$section_props}['$attr_name'] = ((int)$attr_value) == 0 ? 1 : (int)$attr_value;\n";
  945. break;
  946. default:
  947. $this->_syntax_error("unknown section attribute - '$attr_name'", E_USER_ERROR, __FILE__, __LINE__);
  948. break;
  949. }
  950. }
  951. if (!isset($attrs['show']))
  952. $output .= "{$section_props}['show'] = true;\n";
  953. if (!isset($attrs['loop']))
  954. $output .= "{$section_props}['loop'] = 1;\n";
  955. if (!isset($attrs['max']))
  956. $output .= "{$section_props}['max'] = {$section_props}['loop'];\n";
  957. else
  958. $output .= "if ({$section_props}['max'] < 0)\n" .
  959. " {$section_props}['max'] = {$section_props}['loop'];\n";
  960. if (!isset($attrs['step']))
  961. $output .= "{$section_props}['step'] = 1;\n";
  962. if (!isset($attrs['start']))
  963. $output .= "{$section_props}['start'] = {$section_props}['step'] > 0 ? 0 : {$section_props}['loop']-1;\n";
  964. else {
  965. $output .= "if ({$section_props}['start'] < 0)\n" .
  966. " {$section_props}['start'] = max({$section_props}['step'] > 0 ? 0 : -1, {$section_props}['loop'] + {$section_props}['start']);\n" .
  967. "else\n" .
  968. " {$section_props}['start'] = min({$section_props}['start'], {$section_props}['step'] > 0 ? {$section_props}['loop'] : {$section_props}['loop']-1);\n";
  969. }
  970. $output .= "if ({$section_props}['show']) {\n";
  971. if (!isset($attrs['start']) && !isset($attrs['step']) && !isset($attrs['max'])) {
  972. $output .= " {$section_props}['total'] = {$section_props}['loop'];\n";
  973. } else {
  974. $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";
  975. }
  976. $output .= " if ({$section_props}['total'] == 0)\n" .
  977. " {$section_props}['show'] = false;\n" .
  978. "} else\n" .
  979. " {$section_props}['total'] = 0;\n";
  980. $output .= "if ({$section_props}['show']):\n";
  981. $output .= "
  982. for ({$section_props}['index'] = {$section_props}['start'], {$section_props}['iteration'] = 1;
  983. {$section_props}['iteration'] <= {$section_props}['total'];
  984. {$section_props}['index'] += {$section_props}['step'], {$section_props}['iteration']++):\n";
  985. $output .= "{$section_props}['rownum'] = {$section_props}['iteration'];\n";
  986. $output .= "{$section_props}['index_prev'] = {$section_props}['index'] - {$section_props}['step'];\n";
  987. $output .= "{$section_props}['index_next'] = {$section_props}['index'] + {$section_props}['step'];\n";
  988. $output .= "{$section_props}['first'] = ({$section_props}['iteration'] == 1);\n";
  989. $output .= "{$section_props}['last'] = ({$section_props}['iteration'] == {$section_props}['total']);\n";
  990. $output .= "?>";
  991. return $output;
  992. }
  993. /**
  994. * Compile {foreach ...} tag.
  995. *
  996. * @param string $tag_args
  997. * @return string
  998. */
  999. function _compile_foreach_start($tag_args)
  1000. {
  1001. $attrs = $this->_parse_attrs($tag_args);
  1002. $arg_list = array();
  1003. if (empty($attrs['from'])) {
  1004. return $this->_syntax_error("foreach: missing 'from' attribute", E_USER_ERROR, __FILE__, __LINE__);
  1005. }
  1006. $from = $attrs['from'];
  1007. if (empty($attrs['item'])) {
  1008. return $this->_syntax_error("foreach: missing 'item' attribute", E_USER_ERROR, __FILE__, __LINE__);
  1009. }
  1010. $item = $this->_dequote($attrs['item']);
  1011. if (!preg_match('~^\w+$~', $item)) {
  1012. return $this->_syntax_error("'foreach: item' must be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
  1013. }
  1014. if (isset($attrs['key'])) {
  1015. $key = $this->_dequote($attrs['key']);
  1016. if (!preg_match('~^\w+$~', $key)) {
  1017. return $this->_syntax_error("foreach: 'key' must to be a variable name (literal string)", E_USER_ERROR, __FILE__, __LINE__);
  1018. }
  1019. $key_part = "\$this->_tpl_vars['$key'] => ";
  1020. } else {
  1021. $key = null;
  1022. $key_part = '';
  1023. }
  1024. if (isset($attrs['name'])) {
  1025. $name = $attrs['name'];
  1026. } else {
  1027. $name = null;
  1028. }
  1029. $output = '<?php ';
  1030. $output .= "\$_from = $from; if (!is_array(\$_from) && !is_object(\$_from)) { settype(\$_from, 'array'); }";
  1031. if (isset($name)) {
  1032. $foreach_props = "\$this->_foreach[$name]";
  1033. $output .= "{$foreach_props} = array('total' => count(\$_from), 'iteration' => 0);\n";
  1034. $output .= "if ({$foreach_props}['total'] > 0):\n";
  1035. $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
  1036. $output .= " {$foreach_props}['iteration']++;\n";
  1037. } else {
  1038. $output .= "if (count(\$_from)):\n";
  1039. $output .= " foreach (\$_from as $key_part\$this->_tpl_vars['$item']):\n";
  1040. }
  1041. $output .= '?>';
  1042. return $output;
  1043. }
  1044. /**
  1045. * Compile {capture} .. {/capture} tags
  1046. *
  1047. * @param boolean $start true if this is the {capture} tag
  1048. * @param string $tag_args
  1049. * @return string
  1050. */
  1051. function _compile_capture_tag($start, $tag_args = '')
  1052. {
  1053. $attrs = $this->_parse_attrs($tag_args);
  1054. if

Large files files are truncated, but you can click here to view the full file