Source for file xtemplate.class.php

Documentation is available at xtemplate.class.php

  1. <?php
  2.  
  3. /* $Id: fsource_XTemplate__xtemplate.class.php.html,v 1.3 2005/04/11 10:00:49 cocomp Exp $
  4. // $Log: fsource_XTemplate__xtemplate.class.php.html,v $
  5. // Revision 1.3 2005/04/11 10:00:49 cocomp
  6. // Added restart() method sf:641407 feature request
  7. //
  8. // Revision 1.5 2005/04/08 09:17:37 cocomp
  9. // Fixed bug with backslashes sf:810773 & updated docs
  10. //
  11. // Revision 1.4 2005/04/07 12:02:52 cocomp
  12. // MAJOR UPDATE: E_ALL safe, better internal documentation, code readability ++, many bugfixes and new features - considered stable
  13. //
  14. */
  15.  
  16. /*
  17. XTemplate class - http://www.phpxtemplate.org/
  18.  
  19. Latest stable & CVS versions available @ http://sourceforge.net/projects/xtpl/
  20.  
  21. License: LGPL / BSD - see license.txt
  22.  
  23. html generation with templates - fast & easy
  24. Copyright (c) 2000-2001 Barnabas Debreceni [cranx@users.sourceforge.net], 2002-2005 Jeremy Coates [cocomp@users.sourceforge.net]
  25.  
  26. contributors:
  27. Ivar Smolin <okul@linux.ee> (14-march-2001)
  28. - made some code optimizations
  29. Bert Jandehoop <bert.jandehoop@users.info.wau.nl> (26-june-2001)
  30. - new feature to substitute template files by other templates
  31. - new method array_loop()
  32.  
  33. Various contributions over the years from:
  34. Code: Noel Walsh (NW), John Carter (JC)
  35. Bug reporting: SadGeezer
  36.  
  37. */
  38.  
  39. // When developing uncomment the line below, re-comment before making public
  40. //error_reporting(E_ALL);
  41.  
  42. class XTemplate {
  43.  
  44. /***[ variables ]***********************************************************/
  45.  
  46.  
  47. var $filecontents = ''; /* raw contents of template file */
  48. var $blocks = array(); /* unparsed blocks */
  49. var $parsed_blocks = array(); /* parsed blocks */
  50. var $preparsed_blocks = array(); /* preparsed blocks, for file includes */
  51. var $block_parse_order = array(); /* block parsing order for recursive parsing (sometimes reverse:) */
  52. var $sub_blocks = array(); /* store sub-block names for fast resetting */
  53. var $vars = array(); /* variables array */
  54. var $filevars = array(); /* file variables array */
  55. var $filevar_parent = array(); /* filevars' parent block */
  56. var $filecache = array(); /* file caching */
  57.  
  58. var $tpldir = ''; /* location of template files */
  59. var $files = null; /* file names lookup table */
  60. var $filename = '';
  61.  
  62. // moved to setup method so uses the tag_start & end_delims
  63. var $file_delim = '';//"/\{FILE\s*\"([^\"]+)\"\s*\}/m"; /* regexp for file includes */
  64. var $filevar_delim = '';//"/\{FILE\s*\{([A-Za-z0-9\._]+?)\}\s*\}/m"; /* regexp for file includes */
  65. var $filevar_delim_nl = '';//"/^\s*\{FILE\s*\{([A-Za-z0-9\._]+?)\}\s*\}\s*\n/m"; /* regexp for file includes w/ newlines */
  66. var $block_start_delim = '<!-- '; /* block start delimiter */
  67. var $block_end_delim = '-->'; /* block end delimiter */
  68. var $block_start_word = 'BEGIN:'; /* block start word */
  69. var $block_end_word = 'END:'; /* block end word */
  70.  
  71. /* this makes the delimiters look like: <!-- BEGIN: block_name --> if you use my syntax. */
  72.  
  73. var $tag_start_delim = '{';
  74. var $tag_end_delim = '}';
  75. /* this makes the delimiters look like: {tagname} if you use my syntax. */
  76.  
  77. var $mainblock = 'main';
  78.  
  79. var $output_type = 'HTML';
  80.  
  81. var $_null_string = array('' => ''); /* null string for unassigned vars */
  82. var $_null_block = array('' => ''); /* null string for unassigned blocks */
  83. var $_error = '';
  84. var $_autoreset = true; /* auto-reset sub blocks */
  85.  
  86. var $_ignore_missing_blocks = true ; // NW 17 oct 2002 - Set to FALSE to
  87. // generate errors if a non-existant blocks is referenced
  88.  
  89. // JC 20/11/02 for echoing the template filename if in development
  90. var $_file_name_full_path = '';
  91. /**
  92. * Constructor - Instantiate the object
  93. *
  94. * @param string $file Template file to work on
  95. * @param string $tpldir Location of template files (useful for keeping files outside web server root)
  96. * @param array $files Filenames lookup
  97. * @param string $mainblock Name of main block in the template
  98. * @param boolean $autosetup If true, run setup() as part of constuctor
  99. * @return XTemplate
  100. */
  101. function XTemplate ($file, $tpldir = '', $files = null, $mainblock = 'main', $autosetup = true) {
  102.  
  103. $this->filename = $file;
  104.  
  105. // JC 20/11/02 for echoing the template filename if in development
  106. $this->_file_name_full_path = realpath($file);
  107. $this->tpldir = $tpldir;
  108.  
  109. if (is_array($files)) {
  110. $this->files = $files;
  111. }
  112.  
  113. $this->mainblock = $mainblock;
  114.  
  115. if ($autosetup) {
  116. // setup the rest of the preprocess elements
  117. $this->setup();
  118. }
  119. }
  120.  
  121.  
  122. /***************************************************************************/
  123.  
  124. /***[ public stuff ]********************************************************/
  125.  
  126. /***************************************************************************/
  127.  
  128.  
  129. /**
  130. * Restart the class - allows one instantiation with several files processed by restarting
  131. * e.g. $xtpl = new XTemplate('file1.xtpl');
  132. * $xtpl->parse('main');
  133. * $xtpl->out('main');
  134. * $xtpl->restart('file2.xtpl');
  135. * $xtpl->parse('main');
  136. * $xtpl->out('main');
  137. * (Added in response to sf:641407 feature request)
  138. *
  139. * @param string $file Template file to work on
  140. * @param string $tpldir Location of template files
  141. * @param array $files Filenames lookup
  142. * @param string $mainblock Name of main block in the template
  143. * @param boolean $autosetup If true, run setup() as part of restarting
  144. * @param string $tag_start {
  145. * @param string $tag_end }
  146. */
  147. function restart ($file, $tpldir = '', $files = null, $mainblock = 'main', $autosetup = true, $tag_start = '{', $tag_end = '}') {
  148. $this->filename = $file;
  149. $this->_file_name_full_path = realpath($file);
  150. $this->tpldir = $tpldir;
  151. if (is_array($files)) {
  152. $this->files = $files;
  153. }
  154. $this->mainblock = $mainblock;
  155. $this->tag_start_delim = $tag_start;
  156. $this->tag_end_delim = $tag_end;
  157.  
  158. // Start with fresh file contents
  159. $this->filecontents = '';
  160. // Reset the template arrays
  161. $this->blocks = array();
  162. $this->parsed_blocks = array();
  163. $this->preparsed_blocks = array();
  164. $this->block_parse_order = array();
  165. $this->sub_blocks = array();
  166. $this->vars = array();
  167. $this->filevars = array();
  168. $this->filevar_parent = array();
  169. $this->filecache = array();
  170.  
  171. if ($autosetup) {
  172. $this->setup();
  173. }
  174. }
  175.  
  176. /**
  177. * setup - the elements that were previously in the constructor
  178. *
  179. * @access public
  180. * @param boolean $add_outer If true is passed when called, it adds an outer main block to the file
  181. */
  182. function setup ($add_outer = false) {
  183. $this->tag_start_delim = preg_quote($this->tag_start_delim);
  184. $this->tag_end_delim = preg_quote($this->tag_end_delim);
  185.  
  186. // Setup the file delimiters
  187.  
  188. // regexp for file includes
  189. $this->file_delim = "/" . $this->tag_start_delim . "FILE\s*\"([^\"]+)\"\s*" . $this->tag_end_delim . "/m";
  190.  
  191. // regexp for file includes
  192. $this->filevar_delim = "/" . $this->tag_start_delim . "FILE\s*" . $this->tag_start_delim . "([A-Za-z0-9\._]+?)" . $this->tag_end_delim . "\s*" . $this->tag_end_delim . "/m";
  193.  
  194. // regexp for file includes w/ newlines
  195. $this->filevar_delim_nl = "/^\s*" . $this->tag_start_delim . "FILE\s*" . $this->tag_start_delim . "([A-Za-z0-9\._]+?)" . $this->tag_end_delim . "\s*" . $this->tag_end_delim . "\s*\n/m";
  196.  
  197. if (empty($this->filecontents)) {
  198. // read in template file
  199. $this->filecontents = $this->_r_getfile($this->filename);
  200. }
  201.  
  202. if ($add_outer) {
  203. $this->_add_outer_block();
  204. }
  205.  
  206. // preprocess some stuff
  207. $this->blocks = $this->_maketree($this->filecontents, '');
  208. $this->filevar_parent = $this->_store_filevar_parents($this->blocks);
  209. $this->scan_globals();
  210. }
  211.  
  212. /**
  213. * assign a variable
  214. *
  215. * @access public
  216. * @param string $name Variable to assign $val to
  217. * @param string / array $val Value to assign to $name
  218. */
  219. function assign ($name, $val = '') {
  220.  
  221. if (is_array($name)) {
  222.  
  223. foreach ($name as $k => $v) {
  224.  
  225. $this->vars[$k] = $v;
  226. }
  227. } else {
  228.  
  229. $this->vars[$name] = $val;
  230. }
  231. }
  232.  
  233. /**
  234. * assign a file variable
  235. *
  236. * @access public
  237. * @param string $name Variable to assign $val to
  238. * @param string / array $val Values to assign to $name
  239. */
  240. function assign_file ($name, $val = '') {
  241.  
  242. if (is_array($name)) {
  243.  
  244. foreach ($name as $k => $v) {
  245.  
  246. $this->_assign_file_sub($k, $v);
  247. }
  248. } else {
  249.  
  250. $this->_assign_file_sub($name, $val);
  251. }
  252. }
  253.  
  254. /**
  255. * parse a block
  256. *
  257. * @access public
  258. * @param string $bname Block name to parse
  259. */
  260. function parse ($bname) {
  261.  
  262. if (isset($this->preparsed_blocks[$bname])) {
  263.  
  264. $copy = $this->preparsed_blocks[$bname];
  265.  
  266. } elseif (isset($this->blocks[$bname])) {
  267.  
  268. $copy = $this->blocks[$bname];
  269.  
  270. } elseif ($this->_ignore_missing_blocks) {
  271. // ------------------------------------------------------
  272. // NW : 17 Oct 2002. Added default of ignore_missing_blocks
  273. // to allow for generalised processing where some
  274. // blocks may be removed from the HTML without the
  275. // processing code needing to be altered.
  276. // ------------------------------------------------------
  277. // JRC: 3/1/2003 added set error to ignore missing functionality
  278. $this->_set_error("parse: blockname [$bname] does not exist");
  279. return;
  280.  
  281. } else {
  282.  
  283. $this->_set_error("parse: blockname [$bname] does not exist");
  284. }
  285.  
  286. /* from there we should have no more {FILE } directives */
  287. if (!isset($copy)) {
  288. die('Block: ' . $bname);
  289. }
  290.  
  291. $copy = preg_replace($this->filevar_delim_nl, '', $copy);
  292.  
  293. $var_array = array();
  294.  
  295. /* find & replace variables+blocks */
  296. preg_match_all("/" . $this->tag_start_delim . "([A-Za-z0-9\._]+? ?#?.*?)" . $this->tag_end_delim. "/", $copy, $var_array);
  297. $var_array = $var_array[1];
  298.  
  299. foreach ($var_array as $k => $v) {
  300.  
  301. // Are there any comments in the tags {tag#a comment for documenting the template}
  302. $any_comments = explode('#', $v);
  303. $v = rtrim($any_comments[0]);
  304.  
  305. if (sizeof($any_comments) > 1) {
  306.  
  307. $comments = $any_comments[1];
  308. } else {
  309.  
  310. $comments = '';
  311. }
  312.  
  313. $sub = explode('.', $v);
  314.  
  315. if ($sub[0] == '_BLOCK_') {
  316.  
  317. unset($sub[0]);
  318.  
  319. $bname2 = implode('.', $sub);
  320.  
  321. // trinary operator eliminates assign error in E_ALL reporting
  322. $var = isset($this->parsed_blocks[$bname2]) ? $this->parsed_blocks[$bname2] : null;
  323. $nul = (!isset($this->_null_block[$bname2])) ? $this->_null_block[''] : $this->_null_block[$bname2];
  324.  
  325. if ($var == '') {
  326.  
  327. if ($nul == '') {
  328. // -----------------------------------------------------------
  329. // Removed requriement for blocks to be at the start of string
  330. // -----------------------------------------------------------
  331. // $copy=preg_replace("/^\s*\{".$v."\}\s*\n*/m","",$copy);
  332. // Now blocks don't need to be at the beginning of a line,
  333. //$copy=preg_replace("/\s*" . $this->tag_start_delim . $v . $this->tag_end_delim . "\s*\n*/m","",$copy);
  334. $copy = preg_replace("/" . $this->tag_start_delim . $v . $this->tag_end_delim . "/m", '', $copy);
  335.  
  336. } else {
  337.  
  338. $copy = preg_replace("/" . $this->tag_start_delim . $v . $this->tag_end_delim . "/", "$nul", $copy);
  339. }
  340. } else {
  341.  
  342. $var = trim($var);
  343. // SF Bug no. 810773 - thanks anonymous
  344. //$var = str_replace('\\', '\\\\', $var);
  345. // Ensure dollars in strings are not evaluated reported by SadGeezer 31/3/04
  346. //$var = str_replace('$', '\\$', $var);
  347. // Replaced str_replaces with preg_quote
  348. $var = preg_quote($var);
  349. $var = str_replace('\\|', '|', $var);
  350. $copy = preg_replace("|" . $this->tag_start_delim . $v . $this->tag_end_delim . "|", "$var", $copy);
  351. }
  352. } else {
  353.  
  354. $var = $this->vars;
  355.  
  356. foreach ($sub as $v1) {
  357.  
  358. // NW 4 Oct 2002 - Added isset and is_array check to avoid NOTICE messages
  359. // JC 17 Oct 2002 - Changed EMPTY to stlen=0
  360. // if (empty($var[$v1])) { // this line would think that zeros(0) were empty - which is not true
  361. if (!isset($var[$v1]) || (!is_array($var[$v1]) && strlen($var[$v1]) == 0)) {
  362.  
  363. // Check for constant, when variable not assigned
  364. if (defined($v1)) {
  365.  
  366. $var[$v1] = constant($v1);
  367.  
  368. } else {
  369.  
  370. $var[$v1] = null;
  371. }
  372. }
  373.  
  374. $var = $var[$v1];
  375. }
  376.  
  377. $nul = (!isset($this->_null_string[$v])) ? ($this->_null_string[""]) : ($this->_null_string[$v]);
  378. $var = (!isset($var)) ? $nul : $var;
  379.  
  380. if ($var == '') {
  381. // -----------------------------------------------------------
  382. // Removed requriement for blocks to be at the start of string
  383. // -----------------------------------------------------------
  384. // $copy=preg_replace("|^\s*\{".$v." ?#?".$comments."\}\s*\n|m","",$copy);
  385. $copy=preg_replace("|\s*" . $this->tag_start_delim . $v . " ?#?" . $comments . $this->tag_end_delim . "\s*\n|m", '', $copy);
  386. }
  387.  
  388. $var = trim($var);
  389. // SF Bug no. 810773 - thanks anonymous
  390. //$var = str_replace('\\', '\\\\', $var);
  391. // Ensure dollars in strings are not evaluated reported by SadGeezer 31/3/04
  392. //$var = str_replace('$', '\\$', $var);
  393. // Replace str_replaces with preg_quote
  394. $var = preg_quote($var);
  395. $var = str_replace('\\|', '|', $var);
  396. $copy=preg_replace("|" . $this->tag_start_delim . $v . " ?#?" . $comments . $this->tag_end_delim . "|", "$var", $copy);
  397. }
  398. }
  399.  
  400. if (isset($this->parsed_blocks[$bname])) {
  401. $this->parsed_blocks[$bname] .= $copy;
  402. } else {
  403. $this->parsed_blocks[$bname] = $copy;
  404. }
  405.  
  406. /* reset sub-blocks */
  407. if ($this->_autoreset && (!empty($this->sub_blocks[$bname]))) {
  408.  
  409. reset($this->sub_blocks[$bname]);
  410.  
  411. foreach ($this->sub_blocks[$bname] as $k => $v) {
  412. $this->reset($v);
  413. }
  414. }
  415. }
  416.  
  417. /**
  418. * returns the parsed text for a block, including all sub-blocks.
  419. *
  420. * @access public
  421. * @param string $bname Block name to parse
  422. */
  423. function rparse ($bname) {
  424.  
  425. if (!empty($this->sub_blocks[$bname])) {
  426.  
  427. reset($this->sub_blocks[$bname]);
  428.  
  429. foreach ($this->sub_blocks[$bname] as $k => $v) {
  430.  
  431. if (!empty($v)) {
  432. $this->rparse($v);
  433. }
  434. }
  435. }
  436.  
  437. $this->parse($bname);
  438. }
  439.  
  440. /**
  441. * inserts a loop ( call assign & parse )
  442. *
  443. * @access public
  444. * @param string $bname Block name to assign
  445. * @param string $var Variable to assign values to
  446. * @param string / array $value Value to assign to $var
  447. */
  448. function insert_loop ($bname, $var, $value = '') {
  449.  
  450. $this->assign($var, $value);
  451. $this->parse($bname);
  452. }
  453.  
  454. /**
  455. * parses a block for every set of data in the values array
  456. *
  457. * @access public
  458. * @param string $bname Block name to loop
  459. * @param string $var Variable to assign values to
  460. * @param array $values Values to assign to $var
  461. */
  462. function array_loop ($bname, $var, &$values) {
  463.  
  464. if (is_array($values)) {
  465.  
  466. foreach($values as $v) {
  467.  
  468. $this->assign($var, $v);
  469. $this->parse($bname);
  470. }
  471. }
  472. }
  473.  
  474. /**
  475. * returns the parsed text for a block
  476. *
  477. * @access public
  478. * @param string $bname Block name to return
  479. * @return string
  480. */
  481. function text ($bname = '') {
  482.  
  483. // JC 20/11/02 moved from ::out()
  484. $text = '';
  485. /*if (SYSTEM_TYPE == 'development' && $this->output_type == "HTML") {
  486. $Text = "<!-- Template: " . $this->_file_name_full_path . " -->\n";
  487. } else {
  488. $Text = "";
  489. }*/
  490.  
  491. $bname = !empty($bname) ? $bname : $this->mainblock;
  492.  
  493. $text .= isset($this->parsed_blocks[$bname]) ? $this->parsed_blocks[$bname] : $this->get_error();
  494.  
  495. return $text;
  496. }
  497.  
  498. /**
  499. * prints the parsed text
  500. *
  501. * @access public
  502. * @param string $bname Block name to echo out
  503. */
  504. function out ($bname) {
  505.  
  506. $out = $this->text($bname);
  507. // $length=strlen($out);
  508. //header("Content-Length: ".$length); // TODO: Comment this back in later
  509.  
  510. // JC 20/11/02 echo the template filename if in development as
  511. // html comment
  512. // note 4.3.0 and ZE2 have new function debug_backtrace() that show a
  513. // function call list - it may be nice to dump that here too
  514. //if (SYSTEM_TYPE == 'development') {
  515. // echo "<!-- Template: " . $this->_file_name_full_path . " -->\n";
  516. //}
  517. // moved to ::text() so parsing sub templates work
  518.  
  519. echo $out;
  520. }
  521.  
  522. /**
  523. * prints the parsed text to a specified file
  524. *
  525. * @access public
  526. * @param string $bname Block name to write out
  527. * @param string $fname File name to write to
  528. */
  529. function out_file ($bname, $fname) {
  530.  
  531. if (!empty($bname) && !empty($fname) && is_writeable($fname)) {
  532.  
  533. $fp = fopen($fname, 'w');
  534. fwrite($fp, $this->text($bname));
  535. fclose($fp);
  536. }
  537. }
  538.  
  539. /**
  540. * resets the parsed text
  541. *
  542. * @access public
  543. * @param string $bname Block to reset
  544. */
  545. function reset ($bname) {
  546.  
  547. $this->parsed_blocks[$bname] = '';
  548. }
  549.  
  550. /**
  551. * returns true if block was parsed, false if not
  552. *
  553. * @access public
  554. * @param string $bname Block name to test
  555. * @return boolean
  556. */
  557. function parsed ($bname) {
  558.  
  559. return (!empty($this->parsed_blocks[$bname]));
  560. }
  561.  
  562. /**
  563. * sets the string to replace in case the var was not assigned
  564. *
  565. * @access public
  566. * @param string $str Display string for null block
  567. * @param string $varname Variable name to apply $str to
  568. */
  569. function SetNullString ($str, $varname = '') {
  570.  
  571. $this->_null_string[$varname] = $str;
  572. }
  573.  
  574. /**
  575. * sets the string to replace in case the block was not parsed
  576. *
  577. * @access public
  578. * @param string $str Display string for null block
  579. * @param string $bname Block name to apply $str to
  580. */
  581. function SetNullBlock ($str, $bname = '') {
  582.  
  583. $this->_null_block[$bname] = $str;
  584. }
  585.  
  586. /**
  587. * sets AUTORESET to 1. (default is 1)
  588. * if set to 1, parse() automatically resets the parsed blocks' sub blocks
  589. * (for multiple level blocks)
  590. *
  591. * @access public
  592. */
  593. function set_autoreset () {
  594.  
  595. $this->_autoreset = true;
  596. }
  597.  
  598. /**
  599. * sets AUTORESET to 0. (default is 1)
  600. * if set to 1, parse() automatically resets the parsed blocks' sub blocks
  601. * (for multiple level blocks)
  602. *
  603. * @access public
  604. */
  605. function clear_autoreset () {
  606.  
  607. $this->_autoreset = false;
  608. }
  609.  
  610. /**
  611. * scans global variables and assigns to PHP array
  612. *
  613. * @access public
  614. */
  615. function scan_globals () {
  616.  
  617. reset($GLOBALS);
  618.  
  619. foreach ($GLOBALS as $k => $v) {
  620. $GLOB[$k] = $v;
  621. }
  622.  
  623. $this->assign('PHP', $GLOB); /* access global variables as {PHP.HTTP_SERVER_VARS.HTTP_HOST} in your template! */
  624. }
  625.  
  626. /**
  627. * gets error condition / string
  628. *
  629. * @access public
  630. * @return boolean / string
  631. */
  632. function get_error () {
  633.  
  634. // JRC: 3/1/2003 Added ouptut wrapper and detection of output type for error message output
  635. $retval = false;
  636.  
  637. if ($this->_error != '') {
  638. switch ($this->output_type) {
  639. case 'HTML':
  640. case 'html':
  641. $retval = '<b>[XTemplate]</b><ul>' . nl2br(str_replace('* ', '<li>', str_replace(" *\n", "</li>\n", $this->_error))) . '</ul>';
  642. break;
  643.  
  644. default:
  645. $retval = '[XTemplate] ' . str_replace(' *\n', "\n", $this->_error);
  646. break;
  647. }
  648. }
  649.  
  650. return $retval;
  651. }
  652.  
  653. /***************************************************************************/
  654.  
  655. /***[ private stuff ]*******************************************************/
  656.  
  657. /***************************************************************************/
  658.  
  659.  
  660. /**
  661. * generates the array containing to-be-parsed stuff: $blocks["main"],$blocks["main.table"],$blocks["main.table.row"], etc. also builds the reverse parse order.
  662. *
  663. * @access private
  664. * @param string $con content to be processed
  665. * @param string $parentblock name of the parent block in the block hierarchy
  666. */
  667. function _maketree ($con, $parentblock='') {
  668.  
  669. $blocks = array();
  670.  
  671. $con2 = explode($this->block_start_delim, $con);
  672.  
  673. if (!empty($parentblock)) {
  674.  
  675. $block_names = explode('.', $parentblock);
  676. $level = sizeof($block_names);
  677.  
  678. } else {
  679.  
  680. $block_names = array();
  681. $level = 0;
  682. }
  683.  
  684. foreach($con2 as $k => $v) {
  685.  
  686. // JRC 06/04/2005 Added block comments (on BEGIN or END) <!-- BEGIN: block_name#Comments placed here -->
  687. //$patt = "($this->block_start_word|$this->block_end_word)\s*(\w+)\s*$this->block_end_delim(.*)";
  688. $patt = "($this->block_start_word|$this->block_end_word)\s*(\w+) ?#?.*?\s*$this->block_end_delim(.*)";
  689.  
  690. $res = array();
  691.  
  692. if (preg_match_all("/$patt/ims", $v, $res, PREG_SET_ORDER)) {
  693. // $res[0][1] = BEGIN or END
  694. // $res[0][2] = block name
  695. // $res[0][3] = kinda content
  696. $block_word = $res[0][1];
  697. $block_name = $res[0][2];
  698. $content = $res[0][3];
  699. if (strtoupper($block_word) == $this->block_start_word) {
  700.  
  701. $parent_name = implode('.', $block_names);
  702.  
  703. // add one level - array("main","table","row")
  704. $block_names[++$level] = $block_name;
  705.  
  706. // make block name (main.table.row)
  707. $cur_block_name=implode('.', $block_names);
  708.  
  709. // build block parsing order (reverse)
  710. $this->block_parse_order[] = $cur_block_name;
  711.  
  712. //add contents. trinary operator eliminates assign error in E_ALL reporting
  713. $blocks[$cur_block_name] = isset($blocks[$cur_block_name]) ? $blocks[$cur_block_name] . $content : $content;
  714.  
  715. // add {_BLOCK_.blockname} string to parent block
  716. $blocks[$parent_name] .= str_replace('\\', '', $this->tag_start_delim) . '_BLOCK_.' . $cur_block_name . str_replace('\\', '', $this->tag_end_delim);
  717.  
  718. // store sub block names for autoresetting and recursive parsing
  719. $this->sub_blocks[$parent_name][] = $cur_block_name;
  720.  
  721. // store sub block names for autoresetting
  722. $this->sub_blocks[$cur_block_name][] = '';
  723.  
  724. } else if (strtoupper($block_word) == $this->block_end_word) {
  725.  
  726. unset($block_names[$level--]);
  727.  
  728. $parent_name = implode('.', $block_names);
  729.  
  730. // add rest of block to parent block
  731. $blocks[$parent_name] .= $res[0][3];
  732. }
  733. } else {
  734.  
  735. // no block delimiters found
  736. // Saves doing multiple implodes - less overhead
  737. $tmp = implode('.', $block_names);
  738.  
  739. if ($k) {
  740. $blocks[$tmp] .= $this->block_start_delim;
  741. }
  742.  
  743. // trinary operator eliminates assign error in E_ALL reporting
  744. $blocks[$tmp] = isset($blocks[$tmp]) ? $blocks[$tmp] . $v : $v;
  745. }
  746. }
  747.  
  748. return $blocks;
  749. }
  750.  
  751. /**
  752. * Sub processing for assign_file method
  753. *
  754. * @param string $name
  755. * @param string $val
  756. */
  757. function _assign_file_sub ($name, $val) {
  758.  
  759. if (isset($this->filevar_parent[$name])) {
  760.  
  761. if ($val != '') {
  762.  
  763. $val = $this->_r_getfile($val);
  764.  
  765. foreach($this->filevar_parent[$name] as $parent) {
  766.  
  767. if (isset($this->preparsed_blocks[$parent]) && !isset($this->filevars[$name])) {
  768.  
  769. $copy = $this->preparsed_blocks[$parent];
  770.  
  771. } elseif (isset($this->blocks[$parent])) {
  772.  
  773. $copy = $this->blocks[$parent];
  774. }
  775.  
  776. $res = array();
  777.  
  778. preg_match_all($this->filevar_delim, $copy, $res, PREG_SET_ORDER);
  779.  
  780. if (is_array($res) && isset($res[0])) {
  781.  
  782. foreach ($res[0] as $v) {
  783.  
  784. $copy = preg_replace("/" . preg_quote($v) . "/", "$val", $copy);
  785. $this->preparsed_blocks = array_merge($this->preparsed_blocks, $this->_maketree($copy, $parent));
  786. $this->filevar_parent = array_merge($this->filevar_parent, $this->_store_filevar_parents($this->preparsed_blocks));
  787. }
  788. }
  789. }
  790. }
  791. }
  792.  
  793. $this->filevars[$name] = $val;
  794. }
  795.  
  796. /**
  797. * store container block's name for file variables
  798. *
  799. * @access private
  800. * @param array $blocks
  801. * @return array
  802. */
  803. function _store_filevar_parents ($blocks){
  804.  
  805. $parents = array();
  806.  
  807. foreach ($blocks as $bname => $con) {
  808.  
  809. $res = array();
  810.  
  811. preg_match_all($this->filevar_delim, $con, $res);
  812.  
  813. foreach ($res[1] as $k => $v) {
  814.  
  815. $parents[$v][] = $bname;
  816. }
  817. }
  818. return $parents;
  819. }
  820.  
  821. /**
  822. * Set the error string
  823. *
  824. * @param string $str
  825. */
  826. function _set_error ($str) {
  827.  
  828. //$this->_error="<b>[XTemplate]</b>&nbsp;<i>".$str."</i>";
  829. // JRC: 3/1/2003 Made to append the error messages
  830. $this->_error .= '* ' . $str . " *\n";
  831. // JRC: 3/1/2003 Removed trigger error, use this externally if you want it eg. trigger_error($xtpl->get_error())
  832. //trigger_error($this->get_error());
  833. }
  834.  
  835. /**
  836. * returns the contents of a file
  837. *
  838. * @access private
  839. * @param string $file
  840. * @return string
  841. */
  842. function _getfile ($file) {
  843.  
  844. if (!isset($file)) {
  845. // JC 19/12/02 added $file to error message
  846. $this->_set_error('!isset file name!' . $file);
  847.  
  848. return '';
  849. }
  850.  
  851. // check if filename is mapped to other filename
  852. if (isset($this->files)) {
  853.  
  854. if (isset($this->files[$file])) {
  855.  
  856. $file = $this->files[$file];
  857. }
  858. }
  859.  
  860. // prepend template dir
  861. if (!empty($this->tpldir)) {
  862.  
  863. $file = $this->tpldir. '/' . $file;
  864. }
  865.  
  866. if (isset($this->filecache[$file])) {
  867.  
  868. $file_text=$this->filecache[$file];
  869.  
  870. } else {
  871.  
  872. if (is_file($file)) {
  873.  
  874. if (!($fh = fopen($file, 'r'))) {
  875.  
  876. $this->_set_error('Cannot open file: ' . $file);
  877. return '';
  878. }
  879.  
  880. $file_text = fread($fh,filesize($file));
  881. fclose($fh);
  882.  
  883. } else {
  884.  
  885. // NW 17Oct 2002 : Added realpath around the file name to identify where the code is searching.
  886. $this->_set_error("[" . realpath($file) . "] ($file) does not exist");
  887. $file_text = "<b>__XTemplate fatal error: file [$file] does not exist__</b>";
  888. }
  889.  
  890. $this->filecache[$file] = $file_text;
  891. }
  892.  
  893. return $file_text;
  894. }
  895.  
  896. /**
  897. * recursively gets the content of a file with {FILE "filename.tpl"} directives
  898. *
  899. * @access private
  900. * @param string $file
  901. * @return string
  902. */
  903. function _r_getfile ($file) {
  904.  
  905. $text = $this->_getfile($file);
  906.  
  907. $res = array();
  908.  
  909. while (preg_match($this->file_delim,$text,$res)) {
  910. $text2 = $this->_getfile($res[1]);
  911. $text = preg_replace("'".preg_quote($res[0])."'",$text2,$text);
  912. }
  913.  
  914. return $text;
  915. }
  916.  
  917.  
  918. /**
  919. * add an outer block delimiter set useful for rtfs etc - keeps them editable in word
  920. *
  921. * @access private
  922. */
  923. function _add_outer_block () {
  924.  
  925. $before = $this->block_start_delim . $this->block_start_word . ' ' . $this->mainblock . ' ' . $this->block_end_delim;
  926. $after = $this->block_start_delim . $this->block_end_word . ' ' . $this->mainblock . ' ' . $this->block_end_delim;
  927.  
  928. $this->filecontents = $before . "\n" . $this->filecontents . "\n" . $after;
  929. }
  930.  
  931. /**
  932. * Debug function - var_dump wrapped in '<pre></pre>' tags
  933. *
  934. * @access private
  935. * @param multiple Var_dumps all the supplied arguments
  936. */
  937. function _pre_var_dump () {
  938.  
  939. echo '<pre>';
  940. var_dump(func_get_args());
  941. echo '</pre>';
  942. }
  943. } /* end of XTemplate class. */
  944. /* Stuff from development outside sourceforge
  945.  
  946. // Revision 1.2 2003/12/05 22:22:17 jeremy
  947. // Removed duplicate function call in out method
  948. //
  949. // Revision 1.1.1.1 2003/10/29 20:22:43 jeremy
  950. // Initial Import
  951. //
  952. // Revision 1.1 2003/06/25 17:17:52 jeremy
  953. // Initial Import
  954. //
  955. // Revision 1.4 2001/08/17 18:25:45 jeremy
  956. // Sorted greedy matching regular expression in parse function preg_match_all line 166: added ? after .* when looking for comments
  957. //
  958. */
  959. /* Old log stuff
  960. Revision 1.2 2001/09/19 14:11:25 cranx
  961. fixed a bug in the whitespace-stripping block variable interpolating regexp.
  962.  
  963. Revision 1.1 2001/07/11 10:42:39 cranx
  964. added:
  965. - filename substitution, no nested arrays for the moment, sorry
  966. (including happens when assigning, so assign filevar in the outside blocks first!)
  967.  
  968. Revision 1.5 2001/07/11 10:39:08 cranx
  969. added:
  970. - we can now specify base dir
  971. - array_loop()
  972. - trigger_error in _set_error
  973.  
  974. modified:
  975. - newline bugs fixed (for XML)
  976. - in out(): content-length header added
  977. - whiles changed to foreach
  978. - from now on, the class is php4 only :P
  979.  
  980. */
  981. /* Old stuff from original releases
  982. xtemplate class 0.3pre
  983. !!! {FILE {VAR}} file variable interpolation may still be buggy !!!
  984. */
  985.  
  986. ?>

Documentation generated on Mon, 11 Apr 2005 10:59:12 +0100 by phpDocumentor 1.3.0RC3