PageRenderTime 60ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/library/template.php

https://bitbucket.org/calrek/kadoo-api
PHP | 1206 lines | 1096 code | 31 blank | 79 comment | 37 complexity | 3381495f7a9b961d4a5bf9ee38866556 MD5 | raw file

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

  1. <?php
  2. // BEGIN DEBUG
  3. ################################################################################
  4. # PHP-HTML::Template #
  5. # http://phphtmltemplate.sourceforge.net/ #
  6. ################################################################################
  7. # A template system for PHP based on HTML::Template Perl Module #
  8. # Version 0.3.3 #
  9. # 14-NOV-2002 #
  10. # See file README for details #
  11. ################################################################################
  12. # Author: Juan R. Pozo, jrpozo@conclase.net #
  13. # License: GNU GPL (included in file "LICENSE") #
  14. # (c) 2002 by Juan R. Pozo #
  15. # http://html.conclase.net/ #
  16. # http://www.planhost.net/ #
  17. ################################################################################
  18. # HTML::Template Perl module copyright by Sam Tregar #
  19. ################################################################################
  20. # Please consider making a donation today. Visit my amazon.com wishlist at: #
  21. # http://html.conclase.net/link/wishlist #
  22. # Thank you :) #
  23. ################################################################################
  24. /* ************** *
  25. * TEMPLATE CLASS *
  26. * ************** */
  27. // END DEBUG
  28. class Template {
  29. // The array of options
  30. var $options;
  31. // The tags that need a NAME attribute
  32. var $need_names = array(
  33. "TMPL_VAR" => 1,
  34. "TMPL_LOOP" => 1,
  35. "TMPL_IF" => 1,
  36. "TMPL_UNLESS" => 1,
  37. "TMPL_INCLUDE" => 1
  38. );
  39. // Vars for Parse phase
  40. var $template = NULL; // the template file in memory
  41. var $nodes = array(); // the linearized parse tree
  42. var $names = array(); // the names of the variables that this template needs
  43. var $depth = 0; // the inclusion depth of this template
  44. // Vars for AddParam phase
  45. var $paramScope = array(); // enclosing scopes of variable value as we add parameters
  46. var $param = NULL; // the variables values assigned by the user
  47. // Vars for Output phase
  48. var $output = NULL; // the output string
  49. var $totalPass = array(); // Stack for loops: total passes of current loop
  50. var $curPass = array(); // Stack for loops: current pass of current loop
  51. var $version = "0.3.3";
  52. // The class constructor
  53. function Template($options)
  54. {
  55. // if the argument is a scalar, it is taken as the template file name
  56. // otherwise we take it as an associative array of option=>value pairs
  57. if (is_scalar($options)) {
  58. $filename = $options;
  59. unset($options);
  60. $options = array('filename' => $filename);
  61. } else if (!is_array($options)) {
  62. trigger_error("Template->Template() : Trying to create template without providing a template filename or an options array", E_USER_ERROR);
  63. }
  64. if (isset($options['_parent'])) {
  65. $this->nodes =& $options['_parent']->nodes;
  66. $this->names =& $options['_parent']->names;
  67. $this->depth = $options['_parent']->depth + 1;
  68. }
  69. $this->SetOptions($options);
  70. // BEGIN DEBUG
  71. // We can start showing debug information from now on
  72. if (!isset($options['parent']) && $this->options['debug']) {
  73. echo("<h1>Template::New()</h1>\n");
  74. echo("<p>Debug mode on.</p>");
  75. echo("<p>Thanks for using PHP-HTML::Template.</p>");
  76. echo("<p>If you find any bugs, please report them to <a href='mailto:jrpozo@conclase.net'>jrpozo@conclase.net</a> along with an example and the version number of this library.</p>");
  77. echo("<p>Current version number is " . $this->version . "</p>");
  78. }
  79. // END DEBUG
  80. $filename = $this->options['filename'];
  81. if (!is_readable($filename)) {
  82. trigger_error("Template->Template() : Template file \"".$filename."\" not found", E_USER_ERROR);
  83. }
  84. // BEGIN DEBUG
  85. if ($this->options['debug']) {
  86. echo("<p>Opening file ".$filename."</p>");
  87. }
  88. // END DEBUG
  89. $f = fopen($filename, "r");
  90. $this->template = fread($f, filesize($filename));
  91. fclose($f);
  92. // BEGIN DEBUG
  93. if ($this->options['debug']) {
  94. echo("<p>File closed. ".filesize($filename)." bytes read into memory.</p>");
  95. }
  96. // END DEBUG
  97. if ($this->options['parse']) {
  98. // BEGIN DEBUG
  99. if (!isset($options['parent']) && $this->options['debug']) {
  100. echo("<p>Going to parse template now... good luck!</p>");
  101. }
  102. // END DEBUG
  103. $this->Parse();
  104. $this->defScope[] =& $this->names;
  105. $this->paramScope[] =& $this->param;
  106. }
  107. }
  108. // Fill in the $options array
  109. function SetOptions($options)
  110. {
  111. // We first set the default values for all options
  112. $this->options = array(
  113. "debug" => 0,
  114. "die_on_bad_params" => 1,
  115. "strict" => 1,
  116. "loop_context_vars" => 0,
  117. "max_includes" => 10,
  118. "global_vars" => 0,
  119. "no_includes" => 0,
  120. "case_sensitive" => 0,
  121. "hash_comments" => 0,
  122. "parse" => 1,
  123. "imark" => '<',
  124. "emark" => '>',
  125. "parse_html_comments" => 1,
  126. "vanguard_compatibility_mode" => 0
  127. );
  128. // and then the values provided by the user override those values
  129. foreach ($options as $key => $value) {
  130. $this->options[strtolower($key)] = $value;
  131. }
  132. // vanguard compatibility mode enforces die_on_bad_params = 0
  133. if ($this->options["vanguard_compatibility_mode"]) {
  134. $this->options["die_on_bad_params"] = 0;
  135. }
  136. // initial and end tags cannot take the same value
  137. if ($this->options["imark"] == $this->options["emark"]) {
  138. // trigger_error("Template::SetOptions() - Error, imark and emark options cannot take the same values", E_USER_ERROR);
  139. }
  140. }
  141. // This functions escapes regex metacharacters (outside square brackets)
  142. function EscapePREGText($text)
  143. {
  144. return strtr($text, array('\\'=>'\\\\', '/'=>'\/', '^'=>'\^', '$'=>'\$', '.'=>'\.', '['=>'\[', ']'=>'\]', '|'=>'\|', '('=>'\(', ')'=>'\)', '?'=>'\?', '*'=>'\*', '+'=>'\+', '{'=>'\{', '}'=>'\}', '%'=>'\%'));
  145. }
  146. function Parse()
  147. {
  148. // BEGIN DEBUG
  149. if ($this->options['debug']) {
  150. echo("<h1>Template::Parse()</h1>");
  151. }
  152. // END DEBUG
  153. $lineNumber = 1; // we reset in case the template is parsed succesive times
  154. // Stacks to keep track of where we are.
  155. // These contain the number of the corresponding initial node
  156. $inLoop = array();
  157. $inIf = array();
  158. $inUnless = array();
  159. // This contains the type of the last opened node type
  160. $curType = array();
  161. // Handle the old vanguard format
  162. if ($this->options['vanguard_compatibility_mode']) {
  163. $expr = $this->options['imark']."TMPL_VAR NAME=\\1".$this->options['emark'];
  164. $this->template = preg_replace("/%([-\w\/\.+]+)%/", $expr, $this->template);
  165. // BEGIN DEBUG
  166. if ($this->options['debug']) {
  167. echo("Vanguard style tags converted to normal tags<br>\n");
  168. }
  169. // END DEBUG
  170. }
  171. // Strip hash comments (### comment)
  172. if ($this->options['hash_comments']) {
  173. $this->template = preg_replace("/### .*/", "", $this->template);
  174. // BEGIN DEBUG
  175. if ($this->options['debug']) {
  176. echo("Hash comments stripped<br>\n");
  177. }
  178. // END DEBUG
  179. }
  180. // Now split up the template
  181. // BEGIN DEBUG
  182. if ($this->options['debug']) {
  183. echo("Splitting up template in chunks<br>\n");
  184. }
  185. // END DEBUG
  186. // Two possible delimiters, depending on initial mark shape. Examples:
  187. // [[ => <!--\s*[[ and [[
  188. // <[ => <!--\s*[ and <[
  189. // < => <!--\s* and <
  190. $imark = $this->EscapePREGText($this->options['imark']);
  191. $emark = $this->EscapePREGText($this->options['emark']);
  192. $emark0 = $this->EscapePREGText($this->options['emark'][0]);
  193. if ($this->options['parse_html_comments']) {
  194. if ($this->options['imark'][0] == '<') {
  195. $delim = "<(?:!--\\s*)?".substr($imark, 1);
  196. } else {
  197. $delim = "(?:<!--\\s*)?".$imark;
  198. }
  199. if ($this->options['emark'][0] == '>') {
  200. $delim2 = substr($emark, 0, strlen($emark)-1)."(?:\s*--)?>";
  201. } else {
  202. $delim2 = $emark."(?:\s*-->)?";
  203. }
  204. } else {
  205. $delim = $imark;
  206. $delim2 = $emark;
  207. }
  208. // One Regex to rule them all
  209. $regex = "/(" . $delim . "\/?[Tt][Mm][Pp][Ll]_\w+(?:(?:\s+(?:(?:\"[^\"]*\")|(?:\'[\']*\')|(?:[^=\s".$emark0."]+))(?:=(?:\"[^\"]*\"|\'[^\']*\'|(?:[^\s".$emark0."]*)))?)(?:\s+[^=\s]+(?:=(?:\"[^\"]+\"|\'[^\']\'|(?:[^\s".$emark0."]*)))?)*)?".$delim2.")(?m)/";
  210. // One Regex to find them
  211. $regex2 = "/" . $delim . "(\/?[Tt][Mm][Pp][Ll]_\w+)((?:\s+(?:(?:\"[^\"]*\")|(?:\'[\']*\')|(?:[^=\s".$emark0."]+))(?:=(?:\"[^\"]*\"|\'[^\']*\'|(?:[^\s".$emark0."]*)))?)(?:\s+[^=\s]+(?:=(?:\"[^\"]+\"|\'[^\']\'|(?:[^\s".$emark0."]*)))?)*)?".$delim2."(?m)/";
  212. // One Regex to bring them all and in the darkness bind them
  213. // In the Land of Mordor where the Shadows lie.
  214. // Oh, never mind...
  215. $chunks = preg_split($regex, $this->template, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
  216. // BEGIN DEBUG
  217. if ($this->options['debug']) {
  218. echo("Template splitted, ".count($chunks)." chunks obtained<br>\n");
  219. echo("<pre>");
  220. foreach($chunks as $k=>$v) {
  221. echo("<b>[$k]</b> ".htmlentities($v)."\n");
  222. }
  223. echo("</pre>");
  224. }
  225. // END DEBUG
  226. // All done with template
  227. unset ($this->template);
  228. // Loop through chunks, filling up the linearized parse tree
  229. for ($i = 0; $i < count($chunks); $i++) {
  230. if (preg_match($regex2, $chunks[$i], $tag)) {
  231. $which = strtoupper($tag[1]);
  232. // This seems to be a template tag
  233. // BEGIN DEBUG
  234. if ($this->options['debug']) {
  235. echo("<hr>Template tag found: <code>".htmlentities($which)."</code><br>\n");
  236. echo("<pre>tag\n");
  237. print_r($tag);
  238. echo("</pre>");
  239. }
  240. // END DEBUG
  241. $var = array("name"=>NULL, "escape"=>NULL, "global"=>NULL, "default"=>NULL);
  242. if (isset($tag[2])) {
  243. $token = preg_split("/((?:[^\s=]+=)?(?:(?:\"[^\"]+\")|(?:\'[^\']+\')|(?:\S*)))/", trim($tag[2]), -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
  244. foreach($token as $tok) {
  245. if (preg_match("/=/", $tok)) {
  246. $t = preg_split("/\s*=\s*/", $tok, 3);
  247. preg_match("/(?:\"([^\"]*)\")|(?:\'([^\']*)\')|(\S*)/", $t[1], $match);
  248. $var[strtolower($t[0])] = max(isset($match[1])?$match[1]:"", isset($match[2])?$match[2]:"", isset($match[3])?$match[3]:"");
  249. } else if (!preg_match("/^\s*$/", $tok)) {
  250. preg_match("/(?:\"([^\"]*)\")|(?:\'([^\']*)\')|(\S*)/", $tok, $match);
  251. $var["name"] = max(isset($match[1])?$match[1]:"", isset($match[2])?$match[2]:"", isset($match[3])?$match[3]:"");
  252. }
  253. }
  254. }
  255. $name = $var["name"];
  256. $escape = $var["escape"];
  257. $global = $var["global"];
  258. $default = $var["default"];
  259. // BEGIN DEBUG
  260. if ($this->options['debug']) {
  261. echo("<b>Found values:</b><br>\n");
  262. echo("<code>name....: ".htmlentities($name)."</code><br>\n");
  263. echo("<code>escape..: ".htmlentities($escape)."</code><br>\n");
  264. echo("<code>global..: ".htmlentities($global)."</code><br>\n");
  265. echo("<code>default.: ".htmlentities($default)."</code><br>\n");
  266. echo("Normalizing...<br><br>\n");
  267. }
  268. // END DEBUG
  269. // ESCAPE
  270. if ($escape == "1") {
  271. $escape = "HTML";
  272. } else if (empty($escape) || !strcmp($escape, "NONE")) {
  273. $escape = 0;
  274. } else {
  275. $escape = strtoupper($escape);
  276. }
  277. // GLOBAL
  278. $global = ($global) ? 1 : 0;
  279. // NAME
  280. // Allow mixed case in filenames, otherwise flatten
  281. if ($which != 'TMPL_INCLUDE' && !$this->options['case_sensitive']) {
  282. $name = strtolower($name);
  283. }
  284. // BEGIN DEBUG
  285. if ($this->options['debug']) {
  286. echo("<b>Final values:</b><br>\n");
  287. echo("<code><b>Which...:</b> ".$which."</code><br>\n");
  288. echo("<code><b>Name....:</b> ".$name."</code><br>\n");
  289. echo("<code><b>Escape..:</b> ".$escape."</code><br>\n");
  290. echo("<code><b>Global..:</b> ".$global."</code><br>\n");
  291. echo("<code><b>Default.:</b> ".$default."</code><br><br>\n");
  292. }
  293. // END DEBUG
  294. // Die if name contains invalid characters
  295. if (!preg_match("/^[-\w\/+_\.]*$/", $name)) {
  296. trigger_error("Template::Parse() : Invalid character(s) in NAME attribute (".htmlentities($name).") for ".$which." tag, found at ".$this->options['filename'].", line ".$lineNumber, E_USER_ERROR);
  297. }
  298. // Die if we need a name and didn't get one
  299. if (empty($name) && isset($this->need_names[$which])) {
  300. trigger_error("Template::Parse() : No NAME given to a ".$which." tag at ".$this->options['filename']." : line ".$lineNumber, E_USER_ERROR);
  301. }
  302. // Die if we got an escape but can't use one
  303. if ($escape and ($which != 'TMPL_VAR')) {
  304. trigger_error("Template::Parse() : ESCAPE option invalid in a ".$which." tag at ".$this->options['filename']." : line ".$lineNumber, E_USER_ERROR);
  305. }
  306. // Die if we got a default but can't use one
  307. if ($default and ($which != 'TMPL_VAR')) {
  308. trigger_error("Template::Parse() : DEFAULT option invalid in a ".$which." tag at ".$this->options['filename']." : line ".$lineNumber, E_USER_ERROR);
  309. }
  310. // Wow, it doesn't die!
  311. // Take actions depending on which tag found
  312. switch ($which) { // ...said the witch
  313. case 'TMPL_VAR':
  314. // BEGIN DEBUG
  315. if ($this->options['debug']) {
  316. echo("Adding VAR node<br>\n");
  317. }
  318. // END DEBUG
  319. if (in_array($name, array('__pass__', '__passtotal__', '__counter__'))) {
  320. if (count($inLoop)) {
  321. $this->nodes[] =& new Node("ContextVAR", $name);
  322. } else {
  323. trigger_error("Template::Parse() : Found context VAR tag outside of LOOP, at ".$this->options['filename']." : line ".$lineNumber, E_USER_ERROR);
  324. }
  325. } else {
  326. $this->nodes[] =& new Node("VAR", $name, $escape, $global, $default);
  327. $this->names[$name] = 1;
  328. }
  329. // BEGIN DEBUG
  330. if ($this->options['debug']) {
  331. echo("VAR Node added.<br>\n");
  332. $this->ListNodes();
  333. }
  334. // END DEBUG
  335. break;
  336. case 'TMPL_LOOP':
  337. // BEGIN DEBUG
  338. if ($this->options['debug']) {
  339. echo("Adding LOOP node<br>\n");
  340. }
  341. // END DEBUG
  342. $this->nodes[] =& new Node("LOOP", $name, NULL, $global);
  343. $inLoop[] = count($this->nodes)-1;
  344. $curType[] = "LOOP";
  345. $this->names[$name] = 1;
  346. // BEGIN DEBUG
  347. if ($this->options['debug']) {
  348. echo("LOOP node added<br>\n");
  349. $this->ListNodes();
  350. }
  351. // END DEBUG
  352. break;
  353. case '/TMPL_LOOP':
  354. if (!strcmp(end($curType), "LOOP")) {
  355. // BEGIN DEBUG
  356. if ($this->options['debug']) {
  357. echo("Ending LOOP ".end($inLoop)."<br>\n");
  358. }
  359. // END DEBUG
  360. $this->nodes[end($inLoop)]->jumpTo = count($this->nodes);
  361. array_pop($inLoop);
  362. array_pop($curType);
  363. // BEGIN DEBUG
  364. if ($this->options['debug']) {
  365. echo("LOOP ended<br>\n");
  366. $this->ListNodes();
  367. }
  368. // END DEBUG
  369. } else {
  370. trigger_error("Template::Parse() : Nesting error: found end /TMPL_LOOP tag without its corresponding initial tag, at ".$this->options['filename']." : line ".$lineNumber." (last opened tag is of type \"".end($curType)."\")", E_USER_ERROR);
  371. }
  372. break;
  373. case 'TMPL_IF':
  374. // BEGIN DEBUG
  375. if ($this->options['debug']) {
  376. echo("Adding IF node<br>\n");
  377. }
  378. // END DEBUG
  379. if (in_array($name, array('__first__', '__odd__', '__inner__', '__last__'))) {
  380. if (count($inLoop)) {
  381. $this->nodes[] =& new Node("ContextIF", $name);
  382. } else {
  383. trigger_error("Template::Parse() : Found context IF/UNLESS tag outside of LOOP, at ".$this->options['filename']." : line ".$lineNumber, E_USER_ERROR);
  384. }
  385. } else {
  386. $this->nodes[] =& new Node("IF", $name, NULL, $global);
  387. $this->names[$name] = 1;
  388. }
  389. $inIf[] = count($this->nodes)-1;
  390. $curType[] = "IF";
  391. // BEGIN DEBUG
  392. if ($this->options['debug']) {
  393. echo("IF node added<br>\n");
  394. $this->ListNodes();
  395. }
  396. // END DEBUG
  397. break;
  398. case '/TMPL_IF':
  399. if (!strcmp(end($curType), "IF")) {
  400. // BEGIN DEBUG
  401. if ($this->options['debug']) {
  402. echo("Ending IF<br>\n");
  403. }
  404. // END DEBUG
  405. $this->nodes[end($inIf)]->jumpTo = count($this->nodes);
  406. array_pop($inIf);
  407. array_pop($curType);
  408. // BEGIN DEBUG
  409. if ($this->options['debug']) {
  410. echo("IF ended<br>\n");
  411. $this->ListNodes();
  412. }
  413. // END DEBUG
  414. } else {
  415. trigger_error("Template::Parse() : Nesting error: found end /TMPL_IF tag without its corresponding initial tag, at ".$this->options['filename']." : line ".$lineNumber, E_USER_ERROR);
  416. }
  417. break;
  418. case 'TMPL_UNLESS':
  419. // BEGIN DEBUG
  420. if ($this->options['debug']) {
  421. echo("Adding UNLESS node<br>\n");
  422. }
  423. // END DEBUG
  424. if (in_array($name, array('__first__', '__odd__', '__inner__', '__last__'))) {
  425. $this->nodes[] =& new Node("ContextUNLESS", $name);
  426. } else {
  427. $this->nodes[] =& new Node("UNLESS", $name, NULL, $global);
  428. $this->names[$name] = 1;
  429. }
  430. $inUnless[] = count($this->nodes)-1;
  431. $curType[] = "UNLESS";
  432. // BEGIN DEBUG
  433. if ($this->options['debug']) {
  434. echo("UNLESS node added<br>\n");
  435. $this->ListNodes();
  436. }
  437. // END DEBUG
  438. break;
  439. case '/TMPL_UNLESS':
  440. if (!strcmp(end($curType), "UNLESS")) {
  441. // BEGIN DEBUG
  442. if ($this->options['debug']) {
  443. echo("Ending UNLESS<br>\n");
  444. }
  445. // END DEBUG
  446. $this->nodes[end($inUnless)]->jumpTo = count($this->nodes);
  447. array_pop($inUnless);
  448. array_pop($curType);
  449. // BEGIN DEBUG
  450. if ($this->options['debug']) {
  451. echo("UNLESS ended<br>\n");
  452. $this->ListNodes();
  453. }
  454. // END DEBUG
  455. } else {
  456. trigger_error("Template::Parse() : Nesting error: found end /TMPL_UNLESS tag without its corresponding initial tag, at ".$this->options['filename']." : line ".$lineNumber, E_USER_ERROR);
  457. }
  458. break;
  459. case 'TMPL_ELSE':
  460. if (!strcmp(end($curType), "IF") || !strcmp(end($curType), "UNLESS")) {
  461. // BEGIN DEBUG
  462. if ($this->options['debug']) {
  463. echo("Starting ELSE<br>\n");
  464. }
  465. // END DEBUG
  466. if (!strcmp(end($curType), "IF")) {
  467. $this->nodes[end($inIf)]->else = count($this->nodes);
  468. } else {
  469. $this->nodes[end($inUnless)]->else = count($this->nodes);
  470. }
  471. // BEGIN DEBUG
  472. if ($this->options['debug']) {
  473. echo("ELSE started<br>\n");
  474. $this->ListNodes();
  475. }
  476. // END DEBUG
  477. } else {
  478. trigger_error("Template::Parse() : Nesting error: found end TMPL_ELSE tag without its corresponding initial tag, at ".$this->options['filename']." : line ".$lineNumber, E_USER_ERROR);
  479. }
  480. break;
  481. case 'TMPL_INCLUDE':
  482. if (!$this->options['no_includes'])
  483. {
  484. if ($this->depth >= $this->options['max_includes'] && $this->options['max_includes'] > 0) {
  485. trigger_error("Template::Parse() : Include error: Too many included templates, found at ".$this->options['filename']." : line ".$lineNumber, E_USER_ERROR);
  486. } else {
  487. // BEGIN DEBUG
  488. if ($this->options['debug']) {
  489. echo("Including template ".$name."<br>\n");
  490. }
  491. // END DEBUG
  492. $newOptions = $this->options;
  493. $newOptions['filename'] = $name;
  494. $newOptions['_parent'] =& $this;
  495. new Template($newOptions);
  496. // BEGIN DEBUG
  497. if ($this->options['debug']) {
  498. echo("<hr>Template included, returning to previous template<hr>\n");
  499. $this->ListNodes();
  500. }
  501. // END DEBUG
  502. }
  503. }
  504. break;
  505. default:
  506. trigger_error("Template::Parse() : Unknown or unmatched TMPL construct at ".$this->options['filename']." : line ".$lineNumber, E_USER_ERROR);
  507. break;
  508. }
  509. } else {
  510. // This is not a template tag. If it is not a delimiter, skip until next delimiter
  511. // and add all trailing chunks as a new markup node
  512. // BEGIN DEBUG
  513. if ($this->options['debug']) {
  514. echo("<hr>Markup node found<br>\n");
  515. }
  516. // END DEBUG
  517. // Make sure we didn't reject something TMPL_* but badly formed
  518. if ($this->options['strict'] && preg_match("/".$delim."\/?[Tt][Mm][Pp][Ll]_/", $chunks[$i])) {
  519. trigger_error("Template::Parse() : Syntax error in &lt;TMPL_*&gt; tag at " . $this->options['filename'] . " : line " . $lineNumber, E_USER_ERROR);
  520. }
  521. $this->nodes[] =& new Node("MARKUP", $chunks[$i]);
  522. // BEGIN DEBUG
  523. if ($this->options['debug']) {
  524. $this->ListNodes();
  525. }
  526. // END DEBUG
  527. }
  528. // Count newlines in chunk and advance line count
  529. $lineNumber += substr_count($chunks[$i], "\n");
  530. }
  531. // Check if there's some unfinished block
  532. if (count($curType)) {
  533. trigger_error("Template::Parse() : Template ".$this->options['filename']." incorrectly terminated. Found ".end($curType)." tag without corresponding end tag", E_USER_ERROR);
  534. }
  535. }
  536. function ListNodes()
  537. {
  538. echo("<b>Contents of linearized parse tree</b><br>");
  539. for ($i=0; $i<count($this->nodes); $i++) {
  540. echo("<b>[".$i."]</b> - ".$this->nodes[$i]->type." - <code>".htmlentities(addcslashes($this->nodes[$i]->name, "\n\r"))."</code>".(($this->nodes[$i]->else===NULL)?"":" <code>{ Next } else { ".$this->nodes[$i]->else." }</code>").(($this->nodes[$i]->jumpTo===NULL)?"":" - Jump to ".$this->nodes[$i]->jumpTo)."<br>\n");
  541. }
  542. echo("<b>Variables used in template</b><br>");
  543. ob_start();
  544. print_r($this->names);
  545. $b = ob_get_contents();
  546. ob_end_clean();
  547. echo("<pre>$b</pre>");
  548. }
  549. function AddParam($arg, $value=NULL)
  550. {
  551. // BEGIN DEBUG
  552. if ($this->options['debug']) {
  553. echo("<h1>Template::AddParam()</h1>");
  554. }
  555. // END DEBUG
  556. // We can call this with a two arguments (name value pair),
  557. // or one argument (an associative array) (see README for details).
  558. // If there are two arguments, the first must be a string, the second may a scalar or an array
  559. // if the second argument is an array, it must be an array of associative arrays for a loop node
  560. // If there's one argument, it must be an array, and its elements are name-value pairs.
  561. if (func_num_args() == 2) {
  562. if (is_scalar($value) || empty($value)) {
  563. // BEGIN DEBUG
  564. if ($this->options['debug']) {
  565. echo("<p>Attempting to set scalar value \"".htmlentities($value)."\" for variable \"$arg\".</p>");
  566. }
  567. // END DEBUG
  568. if (!$this->options['case_sensitive']) {
  569. $arg = strtolower($arg);
  570. // BEGIN DEBUG
  571. if ($this->options['debug']) {
  572. echo("<p>case_sensitive option is off. Converting variable name to lowercase (\"$arg\").</p>");
  573. }
  574. // END DEBUG
  575. }
  576. if (isset($this->names[$arg])) {
  577. $this->paramScope[count($this->paramScope)-1][$arg] = $value;
  578. // BEGIN DEBUG
  579. if ($this->options['debug']) {
  580. echo("<p>Value set: \"$arg\" = \"".htmlentities($value)."\".</p>");
  581. }
  582. // END DEBUG
  583. } else if ($this->options['die_on_bad_params']) {
  584. trigger_error("Template::AddParam() : Attempt to set value for non existent variable name '".$arg."' - this variable name doesn't match any declarations in the template file", E_USER_ERROR);
  585. }
  586. } else if (is_array($value)) {
  587. // BEGIN DEBUG
  588. if ($this->options['debug']) {
  589. echo("<p>Attempting to set array value for variable \"$arg\".</p>");
  590. }
  591. // END DEBUG
  592. if (!$this->options['case_sensitive']) {
  593. $arg = strtolower($arg);
  594. // BEGIN DEBUG
  595. if ($this->options['debug']) {
  596. echo("<p>case_sensitive option is off. Converting variable name to lowercase (\"$arg\").</p>");
  597. }
  598. // END DEBUG
  599. }
  600. if (isset($this->names[$arg])) {
  601. // BEGIN DEBUG
  602. if ($this->options['debug']) {
  603. echo("<p>The variable \"$arg\" exists in the template.</p>");
  604. }
  605. // END DEBUG
  606. $this->paramScope[count($this->paramScope)-1][$arg] = array();
  607. $this->paramScope[] =& $this->paramScope[count($this->paramScope)-1][$arg];
  608. foreach($value as $a) {
  609. if (!is_array($a)) {
  610. // error
  611. trigger_error("Template::AddParam() : Must use arrays of associative arrays for loop parameters", E_USER_ERROR);
  612. } else {
  613. $this->paramScope[count($this->paramScope)-1][] = array();
  614. $this->paramScope[] =& $this->paramScope[count($this->paramScope)-1][count($this->paramScope[count($this->paramScope)-1])-1];
  615. // BEGIN DEBUG
  616. if ($this->options['debug']) {
  617. echo("<p>Adding variables inside loop...</p><blockquote>");
  618. }
  619. // END DEBUG
  620. foreach ($a as $k=>$v) {
  621. if (is_scalar($v)) {
  622. $this->SetValue($k, $v);
  623. } else {
  624. $this->AddParam($k, $v);
  625. }
  626. }
  627. // BEGIN DEBUG
  628. if ($this->options['debug']) {
  629. echo("</blockquote>");
  630. }
  631. // END DEBUG
  632. array_pop($this->paramScope);
  633. }
  634. }
  635. array_pop($this->paramScope);
  636. } else if ($this->options['die_on_bad_params']) {
  637. trigger_error("Template::AddParam() : Attempt to set value for non existent variable name '".$arg."' - this variable name doesn't match any declarations in the template file", E_USER_ERROR);
  638. }
  639. } else {
  640. // error
  641. trigger_error("Template::AddParam() : Wrong value type", E_USER_ERROR);
  642. }
  643. } else if (func_num_args() == 1) {
  644. if (is_array($arg)) {
  645. foreach ($arg as $k => $v) {
  646. $this->AddParam($k, $v);
  647. }
  648. } else {
  649. // error
  650. trigger_error("Template::AddParam() : Only one argument set, but it's not an array", E_USER_ERROR);
  651. }
  652. } else {
  653. // error
  654. trigger_error("Template::AddParam() : Wrong argument count (".func_num_args().") arguments", E_USER_ERROR);
  655. }
  656. }
  657. function SetValue($arg, $value)
  658. {
  659. // Like AddParam but exclusively for setting scalar values
  660. if (is_scalar($value) || empty($value)) {
  661. // BEGIN DEBUG
  662. if ($this->options['debug']) {
  663. echo("<p>Attempting to set scalar value \"".htmlentities($value)."\" for variable \"$arg\".</p>");
  664. }
  665. // END DEBUG
  666. if (!$this->options['case_sensitive']) {
  667. // BEGIN DEBUG
  668. if ($this->options['debug']) {
  669. echo("<p>case_sensitive option is off. Converting variable name to lowercase (\"$arg\").</p>");
  670. }
  671. // END DEBUG
  672. $arg = strtolower($arg);
  673. }
  674. if (isset($this->names[$arg])) {
  675. $this->paramScope[count($this->paramScope)-1][$arg] = $value;
  676. // BEGIN DEBUG
  677. if ($this->options['debug']) {
  678. echo("<p>Value set: \"$arg\" = \"".htmlentities($value)."\".</p>");
  679. }
  680. // END DEBUG
  681. } else if ($this->options['die_on_bad_params']) {
  682. trigger_error("Template::SetValue() : Attempt to set value for non existent variable name '".$arg."' - this variable name doesn't match any declarations in the template file", E_USER_ERROR);
  683. }
  684. } else{
  685. // error
  686. trigger_error("Template::SetValue() : Value must be a scalar", E_USER_ERROR);
  687. }
  688. }
  689. function Output()
  690. {
  691. // BEGIN DEBUG
  692. if ($this->options['debug']) {
  693. echo("<h1>Template::Output()</h1>");
  694. }
  695. // END DEBUG
  696. if (!isset($this->output)) {
  697. $this->paramScope = array();
  698. $this->paramScope[] =& $this->param;
  699. $this->totalPass = array();
  700. $this->curPass = array();
  701. // BEGIN DEBUG
  702. if ($this->options['debug']) {
  703. echo("Initial variable scope is:<pre>\n");
  704. print_r($this->paramScope[0]);
  705. echo("</pre>");
  706. }
  707. // END DEBUG
  708. $n = 0;
  709. while (isset($this->nodes[$n])) {
  710. $n = $this->ProcessNode($n);
  711. };
  712. }
  713. return $this->output;
  714. }
  715. function ProcessNode($n)
  716. {
  717. // BEGIN DEBUG
  718. if ($this->options['debug']) {
  719. echo("Processing node $n of type ".$this->nodes[$n]->type." <code>[".htmlentities($this->nodes[$n]->name)."]</code><br>\n");
  720. }
  721. // END DEBUG
  722. $node = $this->nodes[$n];
  723. switch ($node->type) {
  724. case "MARKUP":
  725. $this->output .= $node->name;
  726. return $n+1;
  727. case "VAR":
  728. if (isset($this->paramScope[count($this->paramScope)-1][$node->name])) {
  729. if (is_scalar($this->paramScope[count($this->paramScope)-1][$node->name])) {
  730. $value = $this->paramScope[count($this->paramScope)-1][$node->name];
  731. } else if (is_array($this->paramScope[count($this->paramScope)-1][$node->name])) {
  732. $value = count($this->paramScope[count($this->paramScope)-1][$node->name]);
  733. }
  734. // BEGIN DEBUG
  735. else if ($this->options['debug']) {
  736. echo("Variable <code>".$node->name."</code> is defined in current scope with empty value.<br>\n");
  737. }
  738. // END DEBUG
  739. // BEGIN DEBUG
  740. if ($this->options['debug']) {
  741. echo("Variable <code>".$node->name."</code> is defined in current scope with value <code>".htmlentities($value)."</code>.<br>\n");
  742. }
  743. // END DEBUG
  744. if (!strcmp($node->escape, "HTML")) {
  745. $this->output .= htmlspecialchars($value);
  746. } else if (!strcmp($node->escape, "URL")) {
  747. $this->output .= htmlentities(urlencode($value));
  748. } else {
  749. $this->output .= $value;
  750. }
  751. } else if ($node->default !== NULL) {
  752. // BEGIN DEBUG
  753. if ($this->options['debug']) {
  754. echo("Variable <code>".$node->name."</code> is not defined in current scope but has a default value: <code>".htmlentities($node->default)."</code>.<br>\n");
  755. }
  756. // END DEBUG
  757. if (!strcmp($node->escape, "HTML")) {
  758. $this->output .= htmlspecialchars($node->default);
  759. } else if (!strcmp($node->escape, "URL")) {
  760. $this->output .= htmlentities(urlencode($node->default));
  761. } else {
  762. $this->output .= $node->default;
  763. }
  764. } else if ($this->options['global_vars'] || $this->nodes[$n]->global) {
  765. // BEGIN DEBUG
  766. if ($this->options['debug']) {
  767. echo("Variable <code>".$node->name."</code> is not defined in current scope, searching in enclosing scopes.<br>\n");
  768. }
  769. // END DEBUG
  770. for ($lvl = count($this->paramScope)-2; !isset($this->paramScope[$lvl][$node->name]) && $lvl>=0; $lvl--);
  771. if ($lvl>=0) {
  772. if (is_scalar($this->paramScope[$lvl][$node->name])) {
  773. $value = $this->paramScope[$lvl][$node->name];
  774. } else if (is_array($this->paramScope[$lvl][$node->name])) {
  775. $value = count($this->paramScope[$lvl][$node->name]);
  776. }
  777. // BEGIN DEBUG
  778. else if ($this->options['debug']) {
  779. echo("Variable <code>".$node->name."</code> is defined with empty value.<br>\n");
  780. }
  781. // END DEBUG
  782. // BEGIN DEBUG
  783. if ($this->options['debug']) {
  784. echo("Found variable in scope depth $lvl with value <code>".htmlentities($value)."</code><br>\n");
  785. }
  786. // END DEBUG
  787. if (!strcmp($node->escape, "HTML")) {
  788. $this->output .= htmlentities($value);
  789. } else if (!strcmp($node->escape, "URL")) {
  790. $this->output .= htmlentities(urlencode($value));
  791. } else {
  792. $this->output .= $value;
  793. }
  794. }
  795. // BEGIN DEBUG
  796. else if ($this->options['debug']) {
  797. echo("Variable not found<br>\n");
  798. }
  799. // END DEBUG
  800. }
  801. // BEGIN DEBUG
  802. else if ($this->options['debug']) {
  803. echo("Variable not found or it is empty/NULL<br>\n");
  804. }
  805. // END DEBUG
  806. return $n+1;
  807. case "ContextVAR":
  808. if ($this->options['loop_context_vars'] && count($this->totalPass)) {
  809. // BEGIN DEBUG
  810. if ($this->options['debug']) {
  811. echo("Variable <code>".$node->name."</code> is defined in current scope.<br>\n");
  812. }
  813. // END DEBUG
  814. switch ($node->name) {
  815. case "__pass__":
  816. case "__counter__":
  817. $this->output .= $this->curPass[count($this->curPass)-1];
  818. break;
  819. case "__passtotal__":
  820. $this->output .= $this->totalPass[count($this->totalPass)-1];
  821. break;
  822. }
  823. }
  824. return $n+1;
  825. case "LOOP":
  826. if (isset($this->paramScope[count($this->paramScope)-1][$node->name])) {
  827. // BEGIN DEBUG
  828. if ($this->options['debug']) {
  829. echo("Variable <code>".$node->name."</code> is defined in current scope.<br>\n");
  830. }
  831. // END DEBUG
  832. if (!is_array($this->paramScope[count($this->paramScope)-1][$node->name])) {
  833. trigger_error("Template->Output() : A scalar value was assigned to a LOOP var (".$node->name."), but LOOP vars only accept arrays of associative arrays as values,", E_USER_ERROR);
  834. }
  835. $this->paramScope[] =& $this->paramScope[count($this->paramScope)-1][$node->name];
  836. $this->totalPass[] = count($this->paramScope[count($this->paramScope)-1]);
  837. $this->curPass[] = 0;
  838. // BEGIN DEBUG
  839. if ($this->options['debug']) {
  840. echo("Entering loop. New variable scope is:<pre>\n");
  841. print_r($this->paramScope[count($this->paramScope)-1]);
  842. echo("</pre>");
  843. echo("Loop will be traversed ".count($this->paramScope[count($this->paramScope)-1])." times<hr>\n");
  844. }
  845. // END DEBUG
  846. for ($i=0; $i<$this->totalPass[count($this->totalPass)-1]; $i++) {
  847. $this->curPass[count($this->curPass)-1]++;
  848. $this->paramScope[] =& $this->paramScope[count($this->paramScope)-1][$i];
  849. // BEGIN DEBUG
  850. if ($this->options['debug']) {
  851. echo("Variable scope for this pass:<pre>\n");
  852. print_r($this->paramScope[count($this->paramScope)-1]);
  853. echo("</pre>");
  854. echo("</pre>Traversing from node ".($n+1)." to node ".($node->jumpTo-1)."<hr>\n");
  855. }
  856. // END DEBUG
  857. for($j=$n+1; $j<$node->jumpTo;) {
  858. $j = $this->ProcessNode($j);
  859. }
  860. array_pop($this->paramScope);
  861. }
  862. array_pop($this->curPass);
  863. array_pop($this->totalPass);
  864. array_pop($this->paramScope);
  865. return $node->jumpTo;
  866. } else if ($this->options['global_vars'] || $this->nodes[$n]->global) {
  867. // BEGIN DEBUG
  868. if ($this->options['debug']) {
  869. echo("Variable <code>".$node->name."</code> is not defined in current scope, searching in enclosing scopes.<br>\n");
  870. }
  871. // END DEBUG
  872. for ($lvl = count($this->paramScope)-2; !isset($this->paramScope[$lvl][$node->name]) && $lvl>=0; $lvl--);
  873. if ($lvl>=0) {
  874. // BEGIN DEBUG
  875. if ($this->options['debug']) {
  876. echo("Found variable in scope depth $lvl<br>\n");
  877. }
  878. // END DEBUG
  879. if (!is_array($this->paramScope[$lvl][$node->name])) {
  880. trigger_error("Template->Output() : A LOOP var (".$node->name.") was trying to use a scalar value, but LOOP vars only accept arrays of associative arrays as values,", E_USER_ERROR);
  881. }
  882. $this->paramScope[] =& $this->paramScope[$lvl][$node->name];
  883. $this->totalPass[] = count($this->paramScope[count($this->paramScope)-1]);
  884. $this->curPass[] = 0;
  885. // BEGIN DEBUG
  886. if ($this->options['debug']) {
  887. echo("Entering loop. New variable scope is:<pre>\n");
  888. print_r($this->paramScope[count($this->paramScope)-1]);
  889. echo("</pre>");
  890. echo("Loop will be traversed ".count($this->paramScope[count($this->paramScope)-1])." times<hr>\n");
  891. }
  892. // END DEBUG
  893. for ($i=0; $i<$this->totalPass[count($this->totalPass)-1]; $i++) {
  894. $this->curPass[count($this->curPass)-1]++;
  895. $this->paramScope[] =& $this->paramScope[count($this->paramScope)-1][$i];
  896. // BEGIN DEBUG
  897. if ($this->options['debug']) {
  898. echo("Variable scope for this pass:<pre>\n");
  899. print_r($this->paramScope[count($this->paramScope)-1]);
  900. echo("</pre>");
  901. echo("</pre>Traversing from node ".($n+1)." to node ".($node->jumpTo-1)."<hr>\n");
  902. }
  903. // END DEBUG
  904. for($j=$n+1; $j<$node->jumpTo;) {
  905. $j = $this->ProcessNode($j);
  906. }
  907. array_pop($this->paramScope);
  908. }
  909. array_pop($this->curPass);
  910. array_pop($this->totalPass);
  911. array_pop($this->paramScope);
  912. return $node->jumpTo;
  913. }
  914. // BEGIN DEBUG
  915. else if ($this->options['debug']) {
  916. echo("Variable not found<br>\n");
  917. }
  918. // END DEBUG
  919. }
  920. case "IF":
  921. case "UNLESS":
  922. $cond = 0; // by default, condition is false
  923. // BEGIN DEBUG
  924. if ($this->options['debug']) {
  925. echo("Entering IF/UNLESS branch<br>\n");
  926. }
  927. // END DEBUG
  928. $else = $node->else;
  929. if (isset($this->paramScope[count($this->paramScope)-1][$node->name])) {
  930. // BEGIN DEBUG
  931. if ($this->options['debug']) {
  932. echo("Variable <code>".$node->name."</code> is defined in current scope.<br>\n");
  933. }
  934. // END DEBUG
  935. if (is_scalar($this->paramScope[count($this->paramScope)-1][$node->name])) {
  936. $cond = $this->paramScope[count($this->paramScope)-1][$node->name];
  937. } else if (is_array($this->paramScope[count($this->paramScope)-1][$node->name])) {
  938. $cond = count($this->paramScope[count($this->paramScope)-1][$node->name]);
  939. }
  940. // BEGIN DEBUG
  941. if ($this->options['debug']) {
  942. echo("Variable found, value is <code>$cond</code>, condition is ".($cond!=false)."<br>\n");
  943. }
  944. // END DEBUG
  945. } else if ($this->options['global_vars'] || $this->nodes[$n]->global) {
  946. // BEGIN DEBUG
  947. if ($this->options['debug']) {
  948. echo("Variable <code>".$node->name."</code> is not defined in current scope, searching in enclosing scopes.<br>\n");
  949. }
  950. // END DEBUG
  951. for ($lvl = count($this->paramScope)-2; !isset($this->paramScope[$lvl][$node->name]) && $lvl>=0; $lvl--);
  952. if ($lvl>=0) {
  953. // BEGIN DEBUG
  954. if ($this->options['debug']) {
  955. echo("Found variable in scope depth $lvl<br>\n");
  956. }
  957. // END DEBUG
  958. if (is_scalar($this->paramScope[$lvl][$node->name])) {
  959. $cond = $this->paramScope[$lvl][$node->name];
  960. } else if (is_array($this->paramScope[$lvl][$node->name])) {
  961. $cond = count($this->paramScope[$lvl][$node->name]);
  962. } else { // empty var
  963. $cond = 0;
  964. }
  965. // BEGIN DEBUG
  966. if ($this->options['debug']) {
  967. echo("Variable found, value is <code>$cond</code>, condition is ".($cond!=false)."<br>\n");
  968. }
  969. // END DEBUG
  970. } else {
  971. $cond = 0;
  972. // BEGIN DEBUG
  973. if ($this->options['debug']) {
  974. echo("Variable not found, condition is false<br>\n");
  975. }
  976. // END DEBUG
  977. }
  978. } else {
  979. $cond = 0;
  980. // BEGIN DEBUG
  981. if ($this->options['debug']) {
  982. echo("Variable not found, condition is false<br>\n");
  983. }
  984. // END DEBUG
  985. }
  986. if (!strcmp($n

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