class.compiler.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. <?php
  2. /*
  3. * Project: template_lite, a smarter template engine
  4. * File: class.compiler.php
  5. * Author: Paul Lockaby <paul@paullockaby.com>, Mark Dickenson <akapanamajack@sourceforge.net>
  6. * Copyright: 2003,2004,2005 by Paul Lockaby, 2005,2006 Mark Dickenson
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with this library; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. *
  22. * The latest version of template_lite can be obtained from:
  23. * http://templatelite.sourceforge.net
  24. *
  25. */
  26. class Template_Lite_Compiler extends Template_Lite {
  27. // public configuration variables
  28. var $left_delimiter = "";
  29. var $right_delimiter = "";
  30. var $plugins_dir = "";
  31. var $template_dir = "";
  32. var $reserved_template_varname = "";
  33. var $default_modifiers = array();
  34. var $php_extract_vars = true; // Set this to false if you do not want the $this->_tpl variables to be extracted for use by PHP code inside the template.
  35. // private internal variables
  36. var $_vars = array(); // stores all internal assigned variables
  37. var $_confs = array(); // stores all internal config variables
  38. var $_plugins = array(); // stores all internal plugins
  39. var $_linenum = 0; // the current line number in the file we are processing
  40. var $_file = ""; // the current file we are processing
  41. var $_literal = array(); // stores all literal blocks
  42. var $_foreachelse_stack = array();
  43. var $_for_stack = 0;
  44. var $_sectionelse_stack = array(); // keeps track of whether section had 'else' part
  45. var $_switch_stack = array();
  46. var $_tag_stack = array();
  47. var $_require_stack = array(); // stores all files that are "required" inside of the template
  48. var $_php_blocks = array(); // stores all of the php blocks
  49. var $_error_level = null;
  50. var $_sl_md5 = '39fc70570b8b60cbc1b85839bf242aff';
  51. var $_db_qstr_regexp = null; // regexps are setup in the constructor
  52. var $_si_qstr_regexp = null;
  53. var $_qstr_regexp = null;
  54. var $_func_regexp = null;
  55. var $_var_bracket_regexp = null;
  56. var $_dvar_regexp = null;
  57. var $_cvar_regexp = null;
  58. var $_svar_regexp = null;
  59. var $_mod_regexp = null;
  60. var $_var_regexp = null;
  61. var $_obj_params_regexp = null;
  62. var $_templatelite_vars = array();
  63. function Template_Lite_compiler()
  64. {
  65. // matches double quoted strings:
  66. // "foobar"
  67. // "foo\"bar"
  68. // "foobar" . "foo\"bar"
  69. $this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
  70. // matches single quoted strings:
  71. // 'foobar'
  72. // 'foo\'bar'
  73. $this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';
  74. // matches single or double quoted strings
  75. $this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';
  76. // matches bracket portion of vars
  77. // [0]
  78. // [foo]
  79. // [$bar]
  80. // [#bar#]
  81. $this->_var_bracket_regexp = '\[[\$|\#]?\w+\#?\]';
  82. // $this->_var_bracket_regexp = '\[\$?[\w\.]+\]';
  83. // matches section vars:
  84. // %foo.bar%
  85. $this->_svar_regexp = '\%\w+\.\w+\%';
  86. // matches $ vars (not objects):
  87. // $foo
  88. // $foo[0]
  89. // $foo[$bar]
  90. // $foo[5][blah]
  91. // $this->_dvar_regexp = '\$[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:' . $this->_var_bracket_regexp . ')*';
  92. $this->_dvar_regexp = '\$[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*';
  93. // matches config vars:
  94. // #foo#
  95. // #foobar123_foo#
  96. $this->_cvar_regexp = '\#[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:' . $this->_var_bracket_regexp . ')*\#';
  97. // matches valid variable syntax:
  98. // $foo
  99. // 'text'
  100. // "text"
  101. $this->_var_regexp = '(?:(?:' . $this->_dvar_regexp . '|' . $this->_cvar_regexp . ')|' . $this->_qstr_regexp . ')';
  102. // matches valid modifier syntax:
  103. // |foo
  104. // |@foo
  105. // |foo:"bar"
  106. // |foo:$bar
  107. // |foo:"bar":$foobar
  108. // |foo|bar
  109. $this->_mod_regexp = '(?:\|@?[0-9a-zA-Z_]+(?::(?>-?\w+|' . $this->_dvar_regexp . '|' . $this->_qstr_regexp .'))*)';
  110. // matches valid function name:
  111. // foo123
  112. // _foo_bar
  113. $this->_func_regexp = '[a-zA-Z_]+';
  114. // $this->_func_regexp = '[a-zA-Z_]\w*';
  115. }
  116. function _compile_file($file_contents)
  117. {
  118. $ldq = preg_quote($this->left_delimiter);
  119. $rdq = preg_quote($this->right_delimiter);
  120. $_match = array(); // a temp variable for the current regex match
  121. $tags = array(); // all original tags
  122. $text = array(); // all original text
  123. $compiled_text = '<?php /* '.$this->_version.' '.strftime("%Y-%m-%d %H:%M:%S %Z").' */ ?>'."\n\n"; // stores the compiled result
  124. $compiled_tags = array(); // all tags and stuff
  125. $this->_require_stack = array();
  126. $this->_load_filters();
  127. if (count($this->_plugins['prefilter']) > 0)
  128. {
  129. foreach ($this->_plugins['prefilter'] as $function)
  130. {
  131. if ($function === false)
  132. {
  133. continue;
  134. }
  135. $file_contents = $function($file_contents, $this);
  136. }
  137. }
  138. // remove all comments
  139. $file_contents = preg_replace("!{$ldq}\*.*?\*{$rdq}!se","",$file_contents);
  140. // replace all php start and end tags
  141. $file_contents = preg_replace('%(<\?(?!php|=|$))%i', '<?php echo \'\\1\'?>'."\n", $file_contents);
  142. // remove literal blocks
  143. preg_match_all("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", $file_contents, $_match);
  144. $this->_literal = $_match[1];
  145. $file_contents = preg_replace("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", stripslashes($ldq . "literal" . $rdq), $file_contents);
  146. // remove php blocks
  147. preg_match_all("!{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}!s", $file_contents, $_match);
  148. $this->_php_blocks = $_match[1];
  149. $file_contents = preg_replace("!{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}!s", stripslashes($ldq . "php" . $rdq), $file_contents);
  150. // gather all template tags
  151. preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $file_contents, $_match);
  152. $tags = $_match[1];
  153. // put all of the non-template tag text blocks into an array, using the template tags as delimiters
  154. $text = preg_split("!{$ldq}.*?{$rdq}!s", $file_contents);
  155. // compile template tags
  156. $count_tags = count($tags);
  157. for ($i = 0, $for_max = $count_tags; $i < $for_max; $i++)
  158. {
  159. $this->_linenum += substr_count($text[$i], "\n");
  160. $compiled_tags[] = $this->_compile_tag($tags[$i]);
  161. $this->_linenum += substr_count($tags[$i], "\n");
  162. }
  163. // build the compiled template by replacing and interleaving text blocks and compiled tags
  164. $count_compiled_tags = count($compiled_tags);
  165. for ($i = 0, $for_max = $count_compiled_tags; $i < $for_max; $i++)
  166. {
  167. if ($compiled_tags[$i] == '') {
  168. $text[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text[$i+1]);
  169. }
  170. $compiled_text .= $text[$i].$compiled_tags[$i];
  171. }
  172. $compiled_text .= $text[$i];
  173. foreach ($this->_require_stack as $key => $value)
  174. {
  175. $compiled_text = '<?php require_once(\''. $this->_get_plugin_dir($key) . $key . '\'); $this->register_' . $value[0] . '("' . $value[1] . '", "' . $value[2] . '"); ?>' . $compiled_text;
  176. }
  177. // remove unnecessary close/open tags
  178. $compiled_text = preg_replace('!\?>\n?<\?php!', '', $compiled_text);
  179. if (count($this->_plugins['postfilter']) > 0)
  180. {
  181. foreach ($this->_plugins['postfilter'] as $function)
  182. {
  183. if ($function === false)
  184. {
  185. continue;
  186. }
  187. $compiled_text = $function($compiled_text, $this);
  188. }
  189. }
  190. return $compiled_text;
  191. }
  192. function _compile_tag($tag)
  193. {
  194. $_match = array(); // stores the tags
  195. $_result = ""; // the compiled tag
  196. $_variable = ""; // the compiled variable
  197. // extract the tag command, modifier and arguments
  198. preg_match_all('/(?:(' . $this->_var_regexp . '|' . $this->_svar_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*)(?:\s*[,\.]\s*)?)(?:\s+(.*))?/xs', $tag, $_match);
  199. if ($_match[1][0]{0} == '$' || ($_match[1][0]{0} == '#' && $_match[1][0]{strlen($_match[1][0]) - 1} == '#') || $_match[1][0]{0} == "'" || $_match[1][0]{0} == '"' || $_match[1][0]{0} == '%')
  200. {
  201. $_result = $this->_parse_variables($_match[1], $_match[2]);
  202. return "<?php echo $_result; ?>\n";
  203. }
  204. // process a function
  205. $tag_command = $_match[1][0];
  206. $tag_modifiers = !empty($_match[2][0]) ? $_match[2][0] : null;
  207. $tag_arguments = !empty($_match[3][0]) ? $_match[3][0] : null;
  208. $_result = $this->_parse_function($tag_command, $tag_modifiers, $tag_arguments);
  209. return $_result;
  210. }
  211. function _parse_function($function, $modifiers, $arguments)
  212. {
  213. switch ($function) {
  214. case 'include':
  215. if (!function_exists('compile_include'))
  216. {
  217. require_once(TEMPLATE_LITE_DIR . "internal/compile.include.php");
  218. }
  219. return compile_include($arguments, $this);
  220. break;
  221. case 'insert':
  222. $_args = $this->_parse_arguments($arguments);
  223. if (!isset($_args['name']))
  224. {
  225. $this->trigger_error("missing 'name' attribute in 'insert'", E_USER_ERROR, __FILE__, __LINE__);
  226. }
  227. foreach ($_args as $key => $value)
  228. {
  229. if (is_bool($value))
  230. {
  231. $value = $value ? 'true' : 'false';
  232. }
  233. $arg_list[] = "'$key' => $value";
  234. }
  235. return '<?php echo $this->_run_insert(array(' . implode(', ', (array)$arg_list) . ')); ?>';
  236. break;
  237. case 'ldelim':
  238. return $this->left_delimiter;
  239. break;
  240. case 'rdelim':
  241. return $this->right_delimiter;
  242. break;
  243. case 'literal':
  244. list (,$literal) = each($this->_literal);
  245. $this->_linenum += substr_count($literal, "\n");
  246. return "<?php echo '" . str_replace("'", "\'", str_replace("\\", "\\\\", $literal)) . "'; ?>\n";
  247. break;
  248. case 'php':
  249. list (,$php_block) = each($this->_php_blocks);
  250. $this->_linenum += substr_count($php_block, "\n");
  251. $php_extract = '';
  252. if($this->php_extract_vars)
  253. {
  254. if (strnatcmp(PHP_VERSION, '4.3.0') >= 0)
  255. {
  256. $php_extract = '<?php extract($this->_vars, EXTR_REFS); ?>' . "\n";
  257. }
  258. else
  259. {
  260. $php_extract = '<?php extract($this->_vars); ?>' . "\n";
  261. }
  262. }
  263. return $php_extract . '<?php '.$php_block.' ?>';
  264. break;
  265. case 'foreach':
  266. array_push($this->_foreachelse_stack, false);
  267. $_args = $this->_parse_arguments($arguments);
  268. if (!isset($_args['from']))
  269. {
  270. $this->trigger_error("missing 'from' attribute in 'foreach'", E_USER_ERROR, __FILE__, __LINE__);
  271. }
  272. if (!isset($_args['value']) && !isset($_args['item']))
  273. {
  274. $this->trigger_error("missing 'value' attribute in 'foreach'", E_USER_ERROR, __FILE__, __LINE__);
  275. }
  276. if (isset($_args['value']))
  277. {
  278. $_args['value'] = $this->_dequote($_args['value']);
  279. }
  280. elseif (isset($_args['item']))
  281. {
  282. $_args['value'] = $this->_dequote($_args['item']);
  283. }
  284. isset($_args['key']) ? $_args['key'] = "\$this->_vars['".$this->_dequote($_args['key'])."'] => " : $_args['key'] = '';
  285. $_result = '<?php if (count((array)' . $_args['from'] . ')): foreach ((array)' . $_args['from'] . ' as ' . $_args['key'] . '$this->_vars[\'' . $_args['value'] . '\']): ?>';
  286. return $_result;
  287. break;
  288. case 'foreachelse':
  289. $this->_foreachelse_stack[count($this->_foreachelse_stack)-1] = true;
  290. return "<?php endforeach; else: ?>";
  291. break;
  292. case '/foreach':
  293. if (array_pop($this->_foreachelse_stack))
  294. {
  295. return "<?php endif; ?>";
  296. }
  297. else
  298. {
  299. return "<?php endforeach; endif; ?>";
  300. }
  301. break;
  302. case 'for':
  303. $this->_for_stack++;
  304. $_args = $this->_parse_arguments($arguments);
  305. if (!isset($_args['start']))
  306. {
  307. $this->trigger_error("missing 'start' attribute in 'for'", E_USER_ERROR, __FILE__, __LINE__);
  308. }
  309. if (!isset($_args['stop']))
  310. {
  311. $this->trigger_error("missing 'stop' attribute in 'for'", E_USER_ERROR, __FILE__, __LINE__);
  312. }
  313. if (!isset($_args['step']))
  314. {
  315. $_args['step'] = 1;
  316. }
  317. $_result = '<?php for($for' . $this->_for_stack . ' = ' . $_args['start'] . '; ((' . $_args['start'] . ' < ' . $_args['stop'] . ') ? ($for' . $this->_for_stack . ' < ' . $_args['stop'] . ') : ($for' . $this->_for_stack . ' > ' . $_args['stop'] . ')); $for' . $this->_for_stack . ' += ((' . $_args['start'] . ' < ' . $_args['stop'] . ') ? ' . $_args['step'] . ' : -' . $_args['step'] . ')): ?>';
  318. if (isset($_args['value']))
  319. {
  320. $_result .= '<?php $this->assign(\'' . $this->_dequote($_args['value']) . '\', $for' . $this->_for_stack . '); ?>';
  321. }
  322. return $_result;
  323. break;
  324. case '/for':
  325. $this->_for_stack--;
  326. return "<?php endfor; ?>";
  327. break;
  328. case 'section':
  329. array_push($this->_sectionelse_stack, false);
  330. if (!function_exists('compile_section_start'))
  331. {
  332. require_once(TEMPLATE_LITE_DIR . "internal/compile.section_start.php");
  333. }
  334. return compile_section_start($arguments, $this);
  335. break;
  336. case 'sectionelse':
  337. $this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;
  338. return "<?php endfor; else: ?>";
  339. break;
  340. case '/section':
  341. if (array_pop($this->_sectionelse_stack))
  342. {
  343. return "<?php endif; ?>";
  344. }
  345. else
  346. {
  347. return "<?php endfor; endif; ?>";
  348. }
  349. break;
  350. case 'while':
  351. $_args = $this->_compile_if($arguments, false, true);
  352. return '<?php while(' . $_args . '): ?>';
  353. break;
  354. case '/while':
  355. return "<?php endwhile; ?>";
  356. break;
  357. case 'if':
  358. return $this->_compile_if($arguments);
  359. break;
  360. case 'else':
  361. return "<?php else: ?>";
  362. break;
  363. case 'elseif':
  364. return $this->_compile_if($arguments, true);
  365. break;
  366. case '/if':
  367. return "<?php endif; ?>";
  368. break;
  369. case 'assign':
  370. $_args = $this->_parse_arguments($arguments);
  371. if (!isset($_args['var']))
  372. {
  373. $this->trigger_error("missing 'var' attribute in 'assign'", E_USER_ERROR, __FILE__, __LINE__);
  374. }
  375. if (!isset($_args['value']))
  376. {
  377. $this->trigger_error("missing 'value' attribute in 'assign'", E_USER_ERROR, __FILE__, __LINE__);
  378. }
  379. return '<?php $this->assign(\'' . $this->_dequote($_args['var']) . '\', ' . $_args['value'] . '); ?>';
  380. break;
  381. case 'switch':
  382. $_args = $this->_parse_arguments($arguments);
  383. if (!isset($_args['from']))
  384. {
  385. $this->trigger_error("missing 'from' attribute in 'switch'", E_USER_ERROR, __FILE__, __LINE__);
  386. }
  387. array_push($this->_switch_stack, array("matched" => false, "var" => $this->_dequote($_args['from'])));
  388. return;
  389. break;
  390. case '/switch':
  391. array_pop($this->_switch_stack);
  392. return '<?php break; endswitch; ?>';
  393. break;
  394. case 'case':
  395. if (count($this->_switch_stack) > 0)
  396. {
  397. $_result = "<?php ";
  398. $_args = $this->_parse_arguments($arguments);
  399. $_index = count($this->_switch_stack) - 1;
  400. if (!$this->_switch_stack[$_index]["matched"])
  401. {
  402. $_result .= 'switch(' . $this->_switch_stack[$_index]["var"] . '): ';
  403. $this->_switch_stack[$_index]["matched"] = true;
  404. }
  405. else
  406. {
  407. $_result .= 'break; ';
  408. }
  409. if (!empty($_args['value']))
  410. {
  411. $_result .= 'case '.$_args['value'].': ';
  412. }
  413. else
  414. {
  415. $_result .= 'default: ';
  416. }
  417. return $_result . ' ?>';
  418. }
  419. else
  420. {
  421. $this->trigger_error("unexpected 'case', 'case' can only be in a 'switch'", E_USER_ERROR, __FILE__, __LINE__);
  422. }
  423. break;
  424. case 'config_load':
  425. $_args = $this->_parse_arguments($arguments);
  426. if (empty($_args['file']))
  427. {
  428. $this->trigger_error("missing 'file' attribute in 'config_load' tag", E_USER_ERROR, __FILE__, __LINE__);
  429. }
  430. isset($_args['section']) ? null : $_args['section'] = 'null';
  431. isset($_args['var']) ? null : $_args['var'] = 'null';
  432. return '<?php $this->config_load(' . $_args['file'] . ', ' . $_args['section'] . ', ' . $_args['var'] . '); ?>';
  433. break;
  434. default:
  435. $_result = "";
  436. if ($this->_compile_compiler_function($function, $arguments, $_result))
  437. {
  438. return $_result;
  439. }
  440. else if ($this->_compile_custom_block($function, $modifiers, $arguments, $_result))
  441. {
  442. return $_result;
  443. }
  444. elseif ($this->_compile_custom_function($function, $modifiers, $arguments, $_result))
  445. {
  446. return $_result;
  447. }
  448. else
  449. {
  450. $this->trigger_error($function." function does not exist", E_USER_ERROR, __FILE__, __LINE__);
  451. }
  452. break;
  453. }
  454. }
  455. function _compile_compiler_function($function, $arguments, &$_result)
  456. {
  457. if ($function = $this->_plugin_exists($function, "compiler"))
  458. {
  459. $_args = $this->_parse_arguments($arguments);
  460. $_result = '<?php ' . $function($_args, $this) . ' ?>';
  461. return true;
  462. }
  463. else
  464. {
  465. return false;
  466. }
  467. }
  468. function _compile_custom_function($function, $modifiers, $arguments, &$_result)
  469. {
  470. if (!function_exists('compile_compile_custom_function'))
  471. {
  472. require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_custom_function.php");
  473. }
  474. return compile_compile_custom_function($function, $modifiers, $arguments, $_result, $this);
  475. }
  476. function _compile_custom_block($function, $modifiers, $arguments, &$_result)
  477. {
  478. if (!function_exists('compile_compile_custom_block'))
  479. {
  480. require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_custom_block.php");
  481. }
  482. return compile_compile_custom_block($function, $modifiers, $arguments, $_result, $this);
  483. }
  484. function _compile_if($arguments, $elseif = false, $while = false)
  485. {
  486. if (!function_exists('compile_compile_if'))
  487. {
  488. require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_if.php");
  489. }
  490. return compile_compile_if($arguments, $elseif, $while, $this);
  491. }
  492. function _parse_is_expr($is_arg, $_arg)
  493. {
  494. if (!function_exists('compile_parse_is_expr'))
  495. {
  496. require_once(TEMPLATE_LITE_DIR . "internal/compile.parse_is_expr.php");
  497. }
  498. return compile_parse_is_expr($is_arg, $_arg, $this);
  499. }
  500. function _compile_config($variable)
  501. {
  502. if (!function_exists('compile_compile_config'))
  503. {
  504. require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_config.php");
  505. }
  506. return compile_compile_config($variable, $this);
  507. }
  508. function _dequote($string)
  509. {
  510. if (($string{0} == "'" || $string{0} == '"') && $string{strlen($string)-1} == $string{0})
  511. {
  512. return substr($string, 1, -1);
  513. }
  514. else
  515. {
  516. return $string;
  517. }
  518. }
  519. function _parse_arguments($arguments)
  520. {
  521. $_match = array();
  522. $_result = array();
  523. $_variables = array();
  524. preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+))+|[=]/x', $arguments, $_match);
  525. /*
  526. Parse state:
  527. 0 - expecting attribute name
  528. 1 - expecting '='
  529. 2 - expecting attribute value (not '=')
  530. */
  531. $state = 0;
  532. foreach($_match[0] as $value)
  533. {
  534. switch($state) {
  535. case 0:
  536. // valid attribute name
  537. if (is_string($value))
  538. {
  539. $a_name = $value;
  540. $state = 1;
  541. }
  542. else
  543. {
  544. $this->trigger_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
  545. }
  546. break;
  547. case 1:
  548. if ($value == '=')
  549. {
  550. $state = 2;
  551. }
  552. else
  553. {
  554. $this->trigger_error("expecting '=' after '$last_value'", E_USER_ERROR, __FILE__, __LINE__);
  555. }
  556. break;
  557. case 2:
  558. if ($value != '=')
  559. {
  560. if ($value == 'yes' || $value == 'on' || $value == 'true')
  561. {
  562. $value = true;
  563. }
  564. elseif ($value == 'no' || $value == 'off' || $value == 'false')
  565. {
  566. $value = false;
  567. }
  568. elseif ($value == 'null')
  569. {
  570. $value = null;
  571. }
  572. if(!preg_match_all('/(?:(' . $this->_var_regexp . '|' . $this->_svar_regexp . ')(' . $this->_mod_regexp . '*))(?:\s+(.*))?/xs', $value, $_variables))
  573. {
  574. $_result[$a_name] = $value;
  575. }
  576. else
  577. {
  578. $_result[$a_name] = $this->_parse_variables($_variables[1], $_variables[2]);
  579. }
  580. $state = 0;
  581. }
  582. else
  583. {
  584. $this->trigger_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
  585. }
  586. break;
  587. }
  588. $last_value = $value;
  589. }
  590. if($state != 0)
  591. {
  592. if($state == 1)
  593. {
  594. $this->trigger_error("expecting '=' after attribute name '$last_value'", E_USER_ERROR, __FILE__, __LINE__);
  595. }
  596. else
  597. {
  598. $this->trigger_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);
  599. }
  600. }
  601. return $_result;
  602. }
  603. function _parse_variables($variables, $modifiers)
  604. {
  605. $_result = "";
  606. foreach($variables as $key => $value)
  607. {
  608. $tag_variable = trim($variables[$key]);
  609. if(!empty($this->default_modifiers) && !preg_match('!(^|\|)templatelite:nodefaults($|\|)!',$modifiers[$key]))
  610. {
  611. $_default_mod_string = implode('|',(array)$this->default_modifiers);
  612. $modifiers[$key] = empty($modifiers[$key]) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers[$key];
  613. }
  614. if (empty($modifiers[$key]))
  615. {
  616. $_result .= $this->_parse_variable($tag_variable).'.';
  617. }
  618. else
  619. {
  620. $_result .= $this->_parse_modifier($this->_parse_variable($tag_variable), $modifiers[$key]).'.';
  621. }
  622. }
  623. return substr($_result, 0, -1);
  624. }
  625. function _parse_variable($variable)
  626. {
  627. // replace variable with value
  628. if ($variable{0} == "\$")
  629. {
  630. // replace the variable
  631. return $this->_compile_variable($variable);
  632. }
  633. elseif ($variable{0} == '#')
  634. {
  635. // replace the config variable
  636. return $this->_compile_config($variable);
  637. }
  638. elseif ($variable{0} == '"')
  639. {
  640. // expand the quotes to pull any variables out of it
  641. // fortunately variables inside of a quote aren't fancy, no modifiers, no quotes
  642. // just get everything from the $ to the ending space and parse it
  643. // if the $ is escaped, then we won't expand it
  644. $_result = "";
  645. preg_match_all('/(?:[^\\\]' . $this->_dvar_regexp . ')/', substr($variable, 1, -1), $_expand); // old match
  646. // preg_match_all('/(?:[^\\\]' . $this->_dvar_regexp . '[^\\\])/', $variable, $_expand);
  647. $_expand = array_unique($_expand[0]);
  648. foreach($_expand as $key => $value)
  649. {
  650. $_expand[$key] = trim($value);
  651. if (strpos($_expand[$key], '$') > 0)
  652. {
  653. $_expand[$key] = substr($_expand[$key], strpos($_expand[$key], '$'));
  654. }
  655. }
  656. $_result = $variable;
  657. foreach($_expand as $value)
  658. {
  659. $value = trim($value);
  660. $_result = str_replace($value, '" . ' . $this->_parse_variable($value) . ' . "', $_result);
  661. }
  662. $_result = str_replace("`", "", $_result);
  663. return $_result;
  664. }
  665. elseif ($variable{0} == "'")
  666. {
  667. // return the value just as it is
  668. return $variable;
  669. }
  670. elseif ($variable{0} == "%")
  671. {
  672. return $this->_parse_section_prop($variable);
  673. }
  674. else
  675. {
  676. // return it as is; i believe that there was a reason before that i did not just return it as is,
  677. // but i forgot what that reason is ...
  678. // the reason i return the variable 'as is' right now is so that unquoted literals are allowed
  679. return $variable;
  680. }
  681. }
  682. function _parse_section_prop($section_prop_expr)
  683. {
  684. $parts = explode('|', $section_prop_expr, 2);
  685. $var_ref = $parts[0];
  686. $modifiers = isset($parts[1]) ? $parts[1] : '';
  687. preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);
  688. $section_name = $match[1];
  689. $prop_name = $match[2];
  690. $output = "\$this->_sections['$section_name']['$prop_name']";
  691. $this->_parse_modifier($output, $modifiers);
  692. return $output;
  693. }
  694. function _compile_variable($variable)
  695. {
  696. $_result = "";
  697. // remove the $
  698. $variable = substr($variable, 1);
  699. // get [foo] and .foo and (...) pieces
  700. preg_match_all('!(?:^\w+)|(?:' . $this->_var_bracket_regexp . ')|\.\$?\w+|\S+!', $variable, $_match);
  701. $variable = $_match[0];
  702. $var_name = array_shift($variable);
  703. if ($var_name == $this->reserved_template_varname)
  704. {
  705. if ($variable[0]{0} == '[' || $variable[0]{0} == '.')
  706. {
  707. $find = array("[", "]", ".");
  708. switch(strtoupper(str_replace($find, "", $variable[0])))
  709. {
  710. case 'GET':
  711. $_result = "\$_GET";
  712. break;
  713. case 'POST':
  714. $_result = "\$_POST";
  715. break;
  716. case 'COOKIE':
  717. $_result = "\$_COOKIE";
  718. break;
  719. case 'ENV':
  720. $_result = "\$_ENV";
  721. break;
  722. case 'SERVER':
  723. $_result = "\$_SERVER";
  724. break;
  725. case 'SESSION':
  726. $_result = "\$_SESSION";
  727. break;
  728. case 'NOW':
  729. $_result = "time()";
  730. break;
  731. case 'SECTION':
  732. $_result = "\$this->_sections";
  733. break;
  734. case 'LDELIM':
  735. $_result = "\$this->left_delimiter";
  736. break;
  737. case 'RDELIM':
  738. $_result = "\$this->right_delimiter";
  739. break;
  740. case 'VERSION':
  741. $_result = "\$this->_version";
  742. break;
  743. case 'CONFIG':
  744. $_result = "\$this->_confs";
  745. break;
  746. case 'TEMPLATE':
  747. $_result = "\$this->_file";
  748. break;
  749. case 'CONST':
  750. $constant = str_replace($find, "", $_match[0][2]);
  751. $_result = "constant('$constant')";
  752. $variable = array();
  753. break;
  754. default:
  755. $_var_name = str_replace($find, "", $variable[0]);
  756. $_result = "\$this->_templatelite_vars['$_var_name']";
  757. break;
  758. }
  759. array_shift($variable);
  760. }
  761. else
  762. {
  763. $this->trigger_error('$' . $var_name.implode('', $variable) . ' is an invalid $templatelite reference', E_USER_ERROR, __FILE__, __LINE__);
  764. }
  765. }
  766. else
  767. {
  768. $_result = "\$this->_vars['$var_name']";
  769. }
  770. foreach ($variable as $var)
  771. {
  772. if ($var{0} == '[')
  773. {
  774. $var = substr($var, 1, -1);
  775. if (is_numeric($var))
  776. {
  777. $_result .= "[$var]";
  778. }
  779. elseif ($var{0} == '$')
  780. {
  781. $_result .= "[" . $this->_compile_variable($var) . "]";
  782. }
  783. elseif ($var{0} == '#')
  784. {
  785. $_result .= "[" . $this->_compile_config($var) . "]";
  786. }
  787. else
  788. {
  789. // $_result .= "['$var']";
  790. $parts = explode('.', $var);
  791. $section = $parts[0];
  792. $section_prop = isset($parts[1]) ? $parts[1] : 'index';
  793. $_result .= "[\$this->_sections['$section']['$section_prop']]";
  794. }
  795. }
  796. else if ($var{0} == '.')
  797. {
  798. if ($var{1} == '$')
  799. {
  800. $_result .= "[\$this->_TPL['" . substr($var, 2) . "']]";
  801. }
  802. else
  803. {
  804. $_result .= "['" . substr($var, 1) . "']";
  805. }
  806. }
  807. else if (substr($var,0,2) == '->')
  808. {
  809. if(substr($var,2,2) == '__')
  810. {
  811. $this->trigger_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);
  812. }
  813. else if (substr($var, 2, 1) == '$')
  814. {
  815. $_output .= '->{(($var=$this->_TPL[\''.substr($var,3).'\']) && substr($var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$var\\"")}';
  816. }
  817. }
  818. else
  819. {
  820. $this->trigger_error('$' . $var_name.implode('', $variable) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);
  821. }
  822. }
  823. return $_result;
  824. }
  825. function _parse_modifier($variable, $modifiers)
  826. {
  827. $_match = array();
  828. $_mods = array(); // stores all modifiers
  829. $_args = array(); // modifier arguments
  830. preg_match_all('!\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)!', '|' . $modifiers, $_match);
  831. list(, $_mods, $_args) = $_match;
  832. $count_mods = count($_mods);
  833. for ($i = 0, $for_max = $count_mods; $i < $for_max; $i++)
  834. {
  835. preg_match_all('!:(' . $this->_qstr_regexp . '|[^:]+)!', $_args[$i], $_match);
  836. $_arg = $_match[1];
  837. if ($_mods[$i]{0} == '@')
  838. {
  839. $_mods[$i] = substr($_mods[$i], 1);
  840. $_map_array = 0;
  841. } else {
  842. $_map_array = 1;
  843. }
  844. foreach($_arg as $key => $value)
  845. {
  846. $_arg[$key] = $this->_parse_variable($value);
  847. }
  848. if ($this->_plugin_exists($_mods[$i], "modifier") || function_exists($_mods[$i]))
  849. {
  850. if (count($_arg) > 0)
  851. {
  852. $_arg = ', '.implode(', ', $_arg);
  853. }
  854. else
  855. {
  856. $_arg = '';
  857. }
  858. $php_function = "PHP";
  859. if ($this->_plugin_exists($_mods[$i], "modifier"))
  860. {
  861. $php_function = "plugin";
  862. }
  863. $variable = "\$this->_run_modifier($variable, '$_mods[$i]', '$php_function', $_map_array$_arg)";
  864. }
  865. else
  866. {
  867. $variable = "\$this->trigger_error(\"'" . $_mods[$i] . "' modifier does not exist\", E_USER_NOTICE, __FILE__, __LINE__);";
  868. }
  869. }
  870. return $variable;
  871. }
  872. function _plugin_exists($function, $type)
  873. {
  874. // check for object functions
  875. if (isset($this->_plugins[$type][$function]) && is_array($this->_plugins[$type][$function]) && is_object($this->_plugins[$type][$function][0]) && method_exists($this->_plugins[$type][$function][0], $this->_plugins[$type][$function][1]))
  876. {
  877. return '$this->_plugins[\'' . $type . '\'][\'' . $function . '\'][0]->' . $this->_plugins[$type][$function][1];
  878. }
  879. // check for standard functions
  880. if (isset($this->_plugins[$type][$function]) && function_exists($this->_plugins[$type][$function]))
  881. {
  882. return $this->_plugins[$type][$function];
  883. }
  884. // check for a plugin in the plugin directory
  885. if (file_exists($this->_get_plugin_dir($type . '.' . $function . '.php') . $type . '.' . $function . '.php'))
  886. {
  887. require_once($this->_get_plugin_dir($type . '.' . $function . '.php') . $type . '.' . $function . '.php');
  888. if (function_exists('tpl_' . $type . '_' . $function))
  889. {
  890. $this->_require_stack[$type . '.' . $function . '.php'] = array($type, $function, 'tpl_' . $type . '_' . $function);
  891. return ('tpl_' . $type . '_' . $function);
  892. }
  893. }
  894. return false;
  895. }
  896. function _load_filters()
  897. {
  898. if (count($this->_plugins['prefilter']) > 0)
  899. {
  900. foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter)
  901. {
  902. if (!function_exists($prefilter))
  903. {
  904. @include_once( $this->_get_plugin_dir("prefilter." . $filter_name . ".php") . "prefilter." . $filter_name . ".php");
  905. }
  906. }
  907. }
  908. if (count($this->_plugins['postfilter']) > 0)
  909. {
  910. foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter)
  911. {
  912. if (!function_exists($postfilter))
  913. {
  914. @include_once( $this->_get_plugin_dir("postfilter." . $filter_name . ".php") . "postfilter." . $filter_name . ".php");
  915. }
  916. }
  917. }
  918. }
  919. }
  920. ?>