Source for file xtemplate.class.php

Documentation is available at xtemplate.class.php

  1. <?php
  2.  
  3. // When developing uncomment the line below, re-comment before making public
  4. //error_reporting(E_ALL);
  5.  
  6.  
  7.  
  8. /**
  9. * XTemplate PHP templating engine
  10. *
  11. * @package XTemplate
  12. * @author Barnabas Debreceni [cranx@users.sourceforge.net]
  13. * @copyright Barnabas Debreceni 2000-2001
  14. * @author Jeremy Coates [cocomp@users.sourceforge.net]
  15. * @copyright Jeremy Coates 2002-2007
  16. * @see license.txt LGPL / BSD license
  17. * @since PHP 5
  18. * @link $HeadURL: https://xtpl.svn.sourceforge.net/svnroot/xtpl/trunk/xtemplate.class.php $
  19. * @version $Id: xtemplate.class.php 21 2007-05-29 18:01:15Z cocomp $
  20. *
  21. *
  22. * XTemplate class - http://www.phpxtemplate.org/ (x)html / xml generation with templates - fast & easy
  23. * Latest stable & Subversion versions available @ http://sourceforge.net/projects/xtpl/
  24. * License: LGPL / BSD - see license.txt
  25. * Changelog: see changelog.txt
  26. */
  27. class XTemplate {
  28.  
  29. /**
  30. * Properties
  31. */
  32.  
  33. /**
  34. * Raw contents of the template file
  35. *
  36. * @access public
  37. * @var string
  38. */
  39. public $filecontents = '';
  40.  
  41. /**
  42. * Unparsed blocks
  43. *
  44. * @access public
  45. * @var array
  46. */
  47. public $blocks = array();
  48.  
  49. /**
  50. * Parsed blocks
  51. *
  52. * @var unknown_type
  53. */
  54. public $parsed_blocks = array();
  55.  
  56. /**
  57. * Preparsed blocks (for file includes)
  58. *
  59. * @access public
  60. * @var array
  61. */
  62. public $preparsed_blocks = array();
  63.  
  64. /**
  65. * Block parsing order for recursive parsing
  66. * (Sometimes reverse :)
  67. *
  68. * @access public
  69. * @var array
  70. */
  71. public $block_parse_order = array();
  72.  
  73. /**
  74. * Store sub-block names
  75. * (For fast resetting)
  76. *
  77. * @access public
  78. * @var array
  79. */
  80. public $sub_blocks = array();
  81.  
  82. /**
  83. * Variables array
  84. *
  85. * @access public
  86. * @var array
  87. */
  88. public $vars = array();
  89.  
  90. /**
  91. * File variables array
  92. *
  93. * @access public
  94. * @var array
  95. */
  96. public $filevars = array();
  97.  
  98. /**
  99. * Filevars' parent block
  100. *
  101. * @access public
  102. * @var array
  103. */
  104. public $filevar_parent = array();
  105.  
  106. /**
  107. * File caching during duration of script
  108. * e.g. files only cached to speed {FILE "filename"} repeats
  109. *
  110. * @access public
  111. * @var array
  112. */
  113. public $filecache = array();
  114.  
  115. /**
  116. * Location of template files
  117. *
  118. * @access public
  119. * @var string
  120. */
  121. public $tpldir = '';
  122.  
  123. /**
  124. * Filenames lookup table
  125. *
  126. * @access public
  127. * @var null
  128. */
  129. public $files = null;
  130.  
  131. /**
  132. * Template filename
  133. *
  134. * @access public
  135. * @var string
  136. */
  137. public $filename = '';
  138.  
  139. // moved to setup method so uses the tag_start & end_delims
  140. /**
  141. * RegEx for file includes
  142. *
  143. * "/\{FILE\s*\"([^\"]+)\"\s*\}/m";
  144. *
  145. * @access public
  146. * @var string
  147. */
  148. public $file_delim = '';
  149.  
  150. /**
  151. * RegEx for file include variable
  152. *
  153. * "/\{FILE\s*\{([A-Za-z0-9\._]+?)\}\s*\}/m";
  154. *
  155. * @access public
  156. * @var string
  157. */
  158. public $filevar_delim = '';
  159.  
  160. /**
  161. * RegEx for file includes with newlines
  162. *
  163. * "/^\s*\{FILE\s*\{([A-Za-z0-9\._]+?)\}\s*\}\s*\n/m";
  164. *
  165. * @access public
  166. * @var string
  167. */
  168. public $filevar_delim_nl = '';
  169.  
  170. /**
  171. * Template block start delimiter
  172. *
  173. * @access public
  174. * @var string
  175. */
  176. public $block_start_delim = '<!-- ';
  177.  
  178. /**
  179. * Template block end delimiter
  180. *
  181. * @access public
  182. * @var string
  183. */
  184. public $block_end_delim = '-->';
  185.  
  186. /**
  187. * Template block start word
  188. *
  189. * @access public
  190. * @var string
  191. */
  192. public $block_start_word = 'BEGIN:';
  193.  
  194. /**
  195. * Template block end word
  196. *
  197. * The last 3 properties and this make the delimiters look like:
  198. * @example <!-- BEGIN: block_name -->
  199. * if you use the default syntax.
  200. *
  201. * @access public
  202. * @var string
  203. */
  204. public $block_end_word = 'END:';
  205.  
  206. /**
  207. * Template tag start delimiter
  208. *
  209. * This makes the delimiters look like:
  210. * @example {tagname}
  211. * if you use the default syntax.
  212. *
  213. * @access public
  214. * @var string
  215. */
  216. public $tag_start_delim = '{';
  217.  
  218. /**
  219. * Template tag end delimiter
  220. *
  221. * This makes the delimiters look like:
  222. * @example {tagname}
  223. * if you use the default syntax.
  224. *
  225. * @access public
  226. * @var string
  227. */
  228. public $tag_end_delim = '}';
  229. /* this makes the delimiters look like: {tagname} if you use my syntax. */
  230.  
  231. /**
  232. * Regular expression element for comments within tags and blocks
  233. *
  234. * @example {tagname#My Comment}
  235. * @example {tagname #My Comment}
  236. * @example <!-- BEGIN: blockname#My Comment -->
  237. * @example <!-- BEGIN: blockname #My Comment -->
  238. *
  239. * @access public
  240. * @var string
  241. */
  242. public $comment_preg = '( ?#.*?)?';
  243.  
  244. /**
  245. * Default main template block name
  246. *
  247. * @access public
  248. * @var string
  249. */
  250. public $mainblock = 'main';
  251.  
  252. /**
  253. * Script output type
  254. *
  255. * @access public
  256. * @var string
  257. */
  258. public $output_type = 'HTML';
  259.  
  260. /**
  261. * Debug mode
  262. *
  263. * @access public
  264. * @var boolean
  265. */
  266. public $debug = false;
  267.  
  268. /**
  269. * Null string for unassigned vars
  270. *
  271. * @access protected
  272. * @var array
  273. */
  274. protected $_null_string = array('' => '');
  275.  
  276. /**
  277. * Null string for unassigned blocks
  278. *
  279. * @access protected
  280. * @var array
  281. */
  282. protected $_null_block = array('' => '');
  283.  
  284. /**
  285. * Errors
  286. *
  287. * @access protected
  288. * @var string
  289. */
  290. protected $_error = '';
  291.  
  292. /**
  293. * Auto-reset sub blocks
  294. *
  295. * @access protected
  296. * @var boolean
  297. */
  298. protected $_autoreset = true;
  299.  
  300. /**
  301. * Set to FALSE to generate errors if a non-existant blocks is referenced
  302. *
  303. * @author NW
  304. * @since 2002/10/17
  305. * @access protected
  306. * @var boolean
  307. */
  308. protected $_ignore_missing_blocks = true;
  309.  
  310. /**
  311. * PHP 5 Constructor - Instantiate the object
  312. *
  313. * @param string $file Template file to work on
  314. * @param string/array $tpldir Location of template files (useful for keeping files outside web server root)
  315. * @param array $files Filenames lookup
  316. * @param string $mainblock Name of main block in the template
  317. * @param boolean $autosetup If true, run setup() as part of constuctor
  318. * @return XTemplate
  319. */
  320. public function __construct($file, $tpldir = '', $files = null, $mainblock = 'main', $autosetup = true) {
  321.  
  322. $this->restart($file, $tpldir, $files, $mainblock, $autosetup, $this->tag_start_delim, $this->tag_end_delim);
  323. }
  324.  
  325. /**
  326. * PHP 4 Constructor - Instantiate the object
  327. *
  328. * @deprecated Use PHP 5 constructor instead
  329. * @param string $file Template file to work on
  330. * @param string/array $tpldir Location of template files (useful for keeping files outside web server root)
  331. * @param array $files Filenames lookup
  332. * @param string $mainblock Name of main block in the template
  333. * @param boolean $autosetup If true, run setup() as part of constuctor
  334. * @return XTemplate
  335. */
  336. public function XTemplate ($file, $tpldir = '', $files = null, $mainblock = 'main', $autosetup = true) {
  337.  
  338. assert('Deprecated - use PHP 5 constructor');
  339. }
  340.  
  341.  
  342. /***************************************************************************/
  343.  
  344. /***[ public stuff ]********************************************************/
  345.  
  346. /***************************************************************************/
  347.  
  348.  
  349. /**
  350. * Restart the class - allows one instantiation with several files processed by restarting
  351. * e.g. $xtpl = new XTemplate('file1.xtpl');
  352. * $xtpl->parse('main');
  353. * $xtpl->out('main');
  354. * $xtpl->restart('file2.xtpl');
  355. * $xtpl->parse('main');
  356. * $xtpl->out('main');
  357. * (Added in response to sf:641407 feature request)
  358. *
  359. * @param string $file Template file to work on
  360. * @param string/array $tpldir Location of template files
  361. * @param array $files Filenames lookup
  362. * @param string $mainblock Name of main block in the template
  363. * @param boolean $autosetup If true, run setup() as part of restarting
  364. * @param string $tag_start {
  365. * @param string $tag_end }
  366. */
  367. public function restart ($file, $tpldir = '', $files = null, $mainblock = 'main', $autosetup = true, $tag_start = '{', $tag_end = '}') {
  368.  
  369. $this->filename = $file;
  370.  
  371. // From SF Feature request 1202027
  372. // Kenneth Kalmer
  373. $this->tpldir = $tpldir;
  374. if (defined('XTPL_DIR') && empty($this->tpldir)) {
  375. $this->tpldir = XTPL_DIR;
  376. }
  377.  
  378. if (is_array($files)) {
  379. $this->files = $files;
  380. }
  381.  
  382. $this->mainblock = $mainblock;
  383.  
  384. $this->tag_start_delim = $tag_start;
  385. $this->tag_end_delim = $tag_end;
  386.  
  387. // Start with fresh file contents
  388. $this->filecontents = '';
  389.  
  390. // Reset the template arrays
  391. $this->blocks = array();
  392. $this->parsed_blocks = array();
  393. $this->preparsed_blocks = array();
  394. $this->block_parse_order = array();
  395. $this->sub_blocks = array();
  396. $this->vars = array();
  397. $this->filevars = array();
  398. $this->filevar_parent = array();
  399. $this->filecache = array();
  400.  
  401. if ($autosetup) {
  402. $this->setup();
  403. }
  404. }
  405.  
  406. /**
  407. * setup - the elements that were previously in the constructor
  408. *
  409. * @access public
  410. * @param boolean $add_outer If true is passed when called, it adds an outer main block to the file
  411. */
  412. public function setup ($add_outer = false) {
  413.  
  414. $this->tag_start_delim = preg_quote($this->tag_start_delim);
  415. $this->tag_end_delim = preg_quote($this->tag_end_delim);
  416.  
  417. // Setup the file delimiters
  418.  
  419. // regexp for file includes
  420. $this->file_delim = "/" . $this->tag_start_delim . "FILE\s*\"([^\"]+)\"" . $this->comment_preg . $this->tag_end_delim . "/m";
  421.  
  422. // regexp for file includes
  423. $this->filevar_delim = "/" . $this->tag_start_delim . "FILE\s*" . $this->tag_start_delim . "([A-Za-z0-9\._]+?)" . $this->comment_preg . $this->tag_end_delim . $this->comment_preg . $this->tag_end_delim . "/m";
  424.  
  425. // regexp for file includes w/ newlines
  426. $this->filevar_delim_nl = "/^\s*" . $this->tag_start_delim . "FILE\s*" . $this->tag_start_delim . "([A-Za-z0-9\._]+?)" . $this->comment_preg . $this->tag_end_delim . $this->comment_preg . $this->tag_end_delim . "\s*\n/m";
  427.  
  428. if (empty($this->filecontents)) {
  429. // read in template file
  430. $this->filecontents = $this->_r_getfile($this->filename);
  431. }
  432.  
  433. if ($add_outer) {
  434. $this->_add_outer_block();
  435. }
  436.  
  437. // preprocess some stuff
  438. $this->blocks = $this->_maketree($this->filecontents, '');
  439. $this->filevar_parent = $this->_store_filevar_parents($this->blocks);
  440. $this->scan_globals();
  441. }
  442.  
  443. /**
  444. * assign a variable
  445. *
  446. * @example Simplest case:
  447. * @example $xtpl->assign('name', 'value');
  448. * @example {name} in template
  449. *
  450. * @example Array assign:
  451. * @example $xtpl->assign(array('name' => 'value', 'name2' => 'value2'));
  452. * @example {name} {name2} in template
  453. *
  454. * @example Value as array assign:
  455. * @example $xtpl->assign('name', array('key' => 'value', 'key2' => 'value2'));
  456. * @example {name.key} {name.key2} in template
  457. *
  458. * @example Reset array:
  459. * @example $xtpl->assign('name', array('key' => 'value', 'key2' => 'value2'));
  460. * @example // Other code then:
  461. * @example $xtpl->assign('name', array('key3' => 'value3'), false);
  462. * @example {name.key} {name.key2} {name.key3} in template
  463. *
  464. * @access public
  465. * @param string $name Variable to assign $val to
  466. * @param string / array $val Value to assign to $name
  467. * @param boolean $reset_array Reset the variable array if $val is an array
  468. */
  469. public function assign ($name, $val = '', $reset_array = true) {
  470.  
  471. if (is_array($name)) {
  472.  
  473. foreach ($name as $k => $v) {
  474.  
  475. $this->vars[$k] = $v;
  476. }
  477. } elseif (is_array($val)) {
  478.  
  479. // Clear the existing values
  480. if ($reset_array) {
  481. $this->vars[$name] = array();
  482. }
  483.  
  484. foreach ($val as $k => $v) {
  485.  
  486. $this->vars[$name][$k] = $v;
  487. }
  488.  
  489. } else {
  490.  
  491. $this->vars[$name] = $val;
  492. }
  493. }
  494.  
  495. /**
  496. * assign a file variable
  497. *
  498. * @access public
  499. * @param string $name Variable to assign $val to
  500. * @param string / array $val Values to assign to $name
  501. */
  502. public function assign_file ($name, $val = '') {
  503.  
  504. if (is_array($name)) {
  505.  
  506. foreach ($name as $k => $v) {
  507.  
  508. $this->_assign_file_sub($k, $v);
  509. }
  510. } else {
  511.  
  512. $this->_assign_file_sub($name, $val);
  513. }
  514. }
  515.  
  516. /**
  517. * parse a block
  518. *
  519. * @access public
  520. * @param string $bname Block name to parse
  521. */
  522. public function parse ($bname) {
  523.  
  524. if (isset($this->preparsed_blocks[$bname])) {
  525.  
  526. $copy = $this->preparsed_blocks[$bname];
  527.  
  528. } elseif (isset($this->blocks[$bname])) {
  529.  
  530. $copy = $this->blocks[$bname];
  531.  
  532. } elseif ($this->_ignore_missing_blocks) {
  533. // ------------------------------------------------------
  534. // NW : 17 Oct 2002. Added default of ignore_missing_blocks
  535. // to allow for generalised processing where some
  536. // blocks may be removed from the HTML without the
  537. // processing code needing to be altered.
  538. // ------------------------------------------------------
  539. // JRC: 3/1/2003 added set error to ignore missing functionality
  540. $this->_set_error("parse: blockname [$bname] does not exist");
  541. return;
  542.  
  543. } else {
  544.  
  545. $this->_set_error("parse: blockname [$bname] does not exist");
  546. }
  547.  
  548. /* from there we should have no more {FILE } directives */
  549. if (!isset($copy)) {
  550. die('Block: ' . $bname);
  551. }
  552.  
  553. $copy = preg_replace($this->filevar_delim_nl, '', $copy);
  554.  
  555. $var_array = array();
  556.  
  557. /* find & replace variables+blocks */
  558. preg_match_all("|" . $this->tag_start_delim . "([A-Za-z0-9\._]+?" . $this->comment_preg . ")" . $this->tag_end_delim. "|", $copy, $var_array);
  559.  
  560. $var_array = $var_array[1];
  561.  
  562. foreach ($var_array as $k => $v) {
  563.  
  564. // Are there any comments in the tags {tag#a comment for documenting the template}
  565. $any_comments = explode('#', $v);
  566. $v = rtrim($any_comments[0]);
  567.  
  568. if (sizeof($any_comments) > 1) {
  569.  
  570. $comments = $any_comments[1];
  571. } else {
  572.  
  573. $comments = '';
  574. }
  575.  
  576. $sub = explode('.', $v);
  577.  
  578. if ($sub[0] == '_BLOCK_') {
  579.  
  580. unset($sub[0]);
  581.  
  582. $bname2 = implode('.', $sub);
  583.  
  584. // trinary operator eliminates assign error in E_ALL reporting
  585. $var = isset($this->parsed_blocks[$bname2]) ? $this->parsed_blocks[$bname2] : null;
  586. $nul = (!isset($this->_null_block[$bname2])) ? $this->_null_block[''] : $this->_null_block[$bname2];
  587.  
  588. if ($var === '') {
  589.  
  590. if ($nul == '') {
  591. // -----------------------------------------------------------
  592. // Removed requirement for blocks to be at the start of string
  593. // -----------------------------------------------------------
  594. // $copy=preg_replace("/^\s*\{".$v."\}\s*\n*/m","",$copy);
  595. // Now blocks don't need to be at the beginning of a line,
  596. //$copy=preg_replace("/\s*" . $this->tag_start_delim . $v . $this->tag_end_delim . "\s*\n*/m","",$copy);
  597. $copy = preg_replace("|" . $this->tag_start_delim . $v . $this->tag_end_delim . "|m", '', $copy);
  598.  
  599. } else {
  600.  
  601. $copy = preg_replace("|" . $this->tag_start_delim . $v . $this->tag_end_delim . "|m", "$nul", $copy);
  602. }
  603. } else {
  604.  
  605. //$var = trim($var);
  606. switch (true) {
  607. case preg_match('/^\n/', $var) && preg_match('/\n$/', $var):
  608. $var = substr($var, 1, -1);
  609. break;
  610.  
  611. case preg_match('/^\n/', $var):
  612. $var = substr($var, 1);
  613. break;
  614.  
  615. case preg_match('/\n$/', $var):
  616. $var = substr($var, 0, -1);
  617. break;
  618. }
  619.  
  620. // SF Bug no. 810773 - thanks anonymous
  621. $var = str_replace('\\', '\\\\', $var);
  622. // Ensure dollars in strings are not evaluated reported by SadGeezer 31/3/04
  623. $var = str_replace('$', '\\$', $var);
  624. // Replaced str_replaces with preg_quote
  625. //$var = preg_quote($var);
  626. $var = str_replace('\\|', '|', $var);
  627. $copy = preg_replace("|" . $this->tag_start_delim . $v . $this->tag_end_delim . "|m", "$var", $copy);
  628.  
  629. if (preg_match('/^\n/', $copy) && preg_match('/\n$/', $copy)) {
  630. $copy = substr($copy, 1, -1);
  631. }
  632. }
  633. } else {
  634.  
  635. $var = $this->vars;
  636.  
  637. foreach ($sub as $v1) {
  638.  
  639. // NW 4 Oct 2002 - Added isset and is_array check to avoid NOTICE messages
  640. // JC 17 Oct 2002 - Changed EMPTY to stlen=0
  641. // if (empty($var[$v1])) { // this line would think that zeros(0) were empty - which is not true
  642. if (!isset($var[$v1]) || (!is_array($var[$v1]) && strlen($var[$v1]) == 0)) {
  643.  
  644. // Check for constant, when variable not assigned
  645. if (defined($v1)) {
  646.  
  647. $var[$v1] = constant($v1);
  648.  
  649. } else {
  650.  
  651. $var[$v1] = null;
  652. }
  653. }
  654.  
  655. $var = $var[$v1];
  656. }
  657.  
  658. $nul = (!isset($this->_null_string[$v])) ? ($this->_null_string[""]) : ($this->_null_string[$v]);
  659. $var = (!isset($var)) ? $nul : $var;
  660.  
  661. if ($var === '') {
  662. // -----------------------------------------------------------
  663. // Removed requriement for blocks to be at the start of string
  664. // -----------------------------------------------------------
  665. // $copy=preg_replace("|^\s*\{".$v." ?#?".$comments."\}\s*\n|m","",$copy);
  666. $copy = preg_replace("|" . $this->tag_start_delim . $v . "( ?#" . $comments . ")?" . $this->tag_end_delim . "|m", '', $copy);
  667. }
  668.  
  669. $var = trim($var);
  670. // SF Bug no. 810773 - thanks anonymous
  671. $var = str_replace('\\', '\\\\', $var);
  672. // Ensure dollars in strings are not evaluated reported by SadGeezer 31/3/04
  673. $var = str_replace('$', '\\$', $var);
  674. // Replace str_replaces with preg_quote
  675. //$var = preg_quote($var);
  676. $var = str_replace('\\|', '|', $var);
  677. $copy = preg_replace("|" . $this->tag_start_delim . $v . "( ?#" . $comments . ")?" . $this->tag_end_delim . "|m", "$var", $copy);
  678.  
  679. if (preg_match('/^\n/', $copy) && preg_match('/\n$/', $copy)) {
  680. $copy = substr($copy, 1);
  681. }
  682. }
  683. }
  684.  
  685. if (isset($this->parsed_blocks[$bname])) {
  686. $this->parsed_blocks[$bname] .= $copy;
  687. } else {
  688. $this->parsed_blocks[$bname] = $copy;
  689. }
  690.  
  691. /* reset sub-blocks */
  692. if ($this->_autoreset && (!empty($this->sub_blocks[$bname]))) {
  693.  
  694. reset($this->sub_blocks[$bname]);
  695.  
  696. foreach ($this->sub_blocks[$bname] as $k => $v) {
  697. $this->reset($v);
  698. }
  699. }
  700. }
  701.  
  702. /**
  703. * returns the parsed text for a block, including all sub-blocks.
  704. *
  705. * @access public
  706. * @param string $bname Block name to parse
  707. */
  708. public function rparse ($bname) {
  709.  
  710. if (!empty($this->sub_blocks[$bname])) {
  711.  
  712. reset($this->sub_blocks[$bname]);
  713.  
  714. foreach ($this->sub_blocks[$bname] as $k => $v) {
  715.  
  716. if (!empty($v)) {
  717. $this->rparse($v);
  718. }
  719. }
  720. }
  721.  
  722. $this->parse($bname);
  723. }
  724.  
  725. /**
  726. * inserts a loop ( call assign & parse )
  727. *
  728. * @access public
  729. * @param string $bname Block name to assign
  730. * @param string $var Variable to assign values to
  731. * @param string / array $value Value to assign to $var
  732. */
  733. public function insert_loop ($bname, $var, $value = '') {
  734.  
  735. $this->assign($var, $value);
  736. $this->parse($bname);
  737. }
  738.  
  739. /**
  740. * parses a block for every set of data in the values array
  741. *
  742. * @access public
  743. * @param string $bname Block name to loop
  744. * @param string $var Variable to assign values to
  745. * @param array $values Values to assign to $var
  746. */
  747. public function array_loop ($bname, $var, &$values) {
  748.  
  749. if (is_array($values)) {
  750.  
  751. foreach($values as $v) {
  752.  
  753. $this->insert_loop($bname, $var, $v);
  754. }
  755. }
  756. }
  757.  
  758. /**
  759. * returns the parsed text for a block
  760. *
  761. * @access public
  762. * @param string $bname Block name to return
  763. * @return string
  764. */
  765. public function text ($bname = '') {
  766.  
  767. $text = '';
  768.  
  769. if ($this->debug && $this->output_type == 'HTML') {
  770. // JC 20/11/02 echo the template filename if in development as
  771. // html comment
  772. $text .= '<!-- XTemplate: ' . realpath($this->filename) . " -->\n";
  773. }
  774.  
  775. $bname = !empty($bname) ? $bname : $this->mainblock;
  776.  
  777. $text .= isset($this->parsed_blocks[$bname]) ? $this->parsed_blocks[$bname] : $this->get_error();
  778.  
  779. return $text;
  780. }
  781.  
  782. /**
  783. * prints the parsed text
  784. *
  785. * @access public
  786. * @param string $bname Block name to echo out
  787. */
  788. public function out ($bname) {
  789.  
  790. $out = $this->text($bname);
  791. // $length=strlen($out);
  792. //header("Content-Length: ".$length); // TODO: Comment this back in later
  793.  
  794. echo $out;
  795. }
  796.  
  797. /**
  798. * prints the parsed text to a specified file
  799. *
  800. * @access public
  801. * @param string $bname Block name to write out
  802. * @param string $fname File name to write to
  803. */
  804. public function out_file ($bname, $fname) {
  805.  
  806. if (!empty($bname) && !empty($fname) && is_writeable($fname)) {
  807.  
  808. $fp = fopen($fname, 'w');
  809. fwrite($fp, $this->text($bname));
  810. fclose($fp);
  811. }
  812. }
  813.  
  814. /**
  815. * resets the parsed text
  816. *
  817. * @access public
  818. * @param string $bname Block to reset
  819. */
  820. public function reset ($bname) {
  821.  
  822. $this->parsed_blocks[$bname] = '';
  823. }
  824.  
  825. /**
  826. * returns true if block was parsed, false if not
  827. *
  828. * @access public
  829. * @param string $bname Block name to test
  830. * @return boolean
  831. */
  832. public function parsed ($bname) {
  833.  
  834. return (!empty($this->parsed_blocks[$bname]));
  835. }
  836.  
  837. /**
  838. * sets the string to replace in case the var was not assigned
  839. *
  840. * @access public
  841. * @param string $str Display string for null block
  842. * @param string $varname Variable name to apply $str to
  843. */
  844. public function set_null_string($str, $varname = '') {
  845.  
  846. $this->_null_string[$varname] = $str;
  847. }
  848.  
  849. /**
  850. * Backwards compatibility only
  851. *
  852. * @param string $str
  853. * @param string $varname
  854. * @deprecated Change to set_null_string to keep in with rest of naming convention
  855. */
  856. public function SetNullString ($str, $varname = '') {
  857. $this->set_null_string($str, $varname);
  858. }
  859.  
  860. /**
  861. * sets the string to replace in case the block was not parsed
  862. *
  863. * @access public
  864. * @param string $str Display string for null block
  865. * @param string $bname Block name to apply $str to
  866. */
  867. public function set_null_block ($str, $bname = '') {
  868.  
  869. $this->_null_block[$bname] = $str;
  870. }
  871.  
  872. /**
  873. * Backwards compatibility only
  874. *
  875. * @param string $str
  876. * @param string $bname
  877. * @deprecated Change to set_null_block to keep in with rest of naming convention
  878. */
  879. public function SetNullBlock ($str, $bname = '') {
  880. $this->set_null_block($str, $bname);
  881. }
  882.  
  883. /**
  884. * sets AUTORESET to 1. (default is 1)
  885. * if set to 1, parse() automatically resets the parsed blocks' sub blocks
  886. * (for multiple level blocks)
  887. *
  888. * @access public
  889. */
  890. public function set_autoreset () {
  891.  
  892. $this->_autoreset = true;
  893. }
  894.  
  895. /**
  896. * sets AUTORESET to 0. (default is 1)
  897. * if set to 1, parse() automatically resets the parsed blocks' sub blocks
  898. * (for multiple level blocks)
  899. *
  900. * @access public
  901. */
  902. public function clear_autoreset () {
  903.  
  904. $this->_autoreset = false;
  905. }
  906.  
  907. /**
  908. * scans global variables and assigns to PHP array
  909. *
  910. * @access public
  911. */
  912. public function scan_globals () {
  913.  
  914. reset($GLOBALS);
  915.  
  916. foreach ($GLOBALS as $k => $v) {
  917. $GLOB[$k] = $v;
  918. }
  919.  
  920. /**
  921. * Access global variables as:
  922. * @example {PHP._SERVER.HTTP_HOST}
  923. * in your template!
  924. */
  925. $this->assign('PHP', $GLOB);
  926. }
  927.  
  928. /**
  929. * gets error condition / string
  930. *
  931. * @access public
  932. * @return boolean / string
  933. */
  934. public function get_error () {
  935.  
  936. // JRC: 3/1/2003 Added ouptut wrapper and detection of output type for error message output
  937. $retval = false;
  938.  
  939. if ($this->_error != '') {
  940.  
  941. switch ($this->output_type) {
  942. case 'HTML':
  943. case 'html':
  944. $retval = '<b>[XTemplate]</b><ul>' . nl2br(str_replace('* ', '<li>', str_replace(" *\n", "</li>\n", $this->_error))) . '</ul>';
  945. break;
  946.  
  947. default:
  948. $retval = '[XTemplate] ' . str_replace(' *\n', "\n", $this->_error);
  949. break;
  950. }
  951. }
  952.  
  953. return $retval;
  954. }
  955.  
  956. /***************************************************************************/
  957.  
  958. /***[ private stuff ]*******************************************************/
  959.  
  960. /***************************************************************************/
  961.  
  962.  
  963. /**
  964. * generates the array containing to-be-parsed stuff:
  965. * $blocks["main"],$blocks["main.table"],$blocks["main.table.row"], etc.
  966. * also builds the reverse parse order.
  967. *
  968. * @access public - aiming for private
  969. * @param string $con content to be processed
  970. * @param string $parentblock name of the parent block in the block hierarchy
  971. */
  972. public function _maketree ($con, $parentblock='') {
  973.  
  974. $blocks = array();
  975.  
  976. $con2 = explode($this->block_start_delim, $con);
  977.  
  978. if (!empty($parentblock)) {
  979.  
  980. $block_names = explode('.', $parentblock);
  981. $level = sizeof($block_names);
  982.  
  983. } else {
  984.  
  985. $block_names = array();
  986. $level = 0;
  987. }
  988.  
  989. // JRC 06/04/2005 Added block comments (on BEGIN or END) <!-- BEGIN: block_name#Comments placed here -->
  990. //$patt = "($this->block_start_word|$this->block_end_word)\s*(\w+)\s*$this->block_end_delim(.*)";
  991. $patt = "(" . $this->block_start_word . "|" . $this->block_end_word . ")\s*(\w+)" . $this->comment_preg . "\s*" . $this->block_end_delim . "(.*)";
  992.  
  993. foreach($con2 as $k => $v) {
  994.  
  995. $res = array();
  996.  
  997. if (preg_match_all("/$patt/ims", $v, $res, PREG_SET_ORDER)) {
  998. // $res[0][1] = BEGIN or END
  999. // $res[0][2] = block name
  1000. // $res[0][3] = comment
  1001. // $res[0][4] = kinda content
  1002. $block_word = $res[0][1];
  1003. $block_name = $res[0][2];
  1004. $comment = $res[0][3];
  1005. $content = $res[0][4];
  1006.  
  1007. if (strtoupper($block_word) == $this->block_start_word) {
  1008.  
  1009. $parent_name = implode('.', $block_names);
  1010.  
  1011. // add one level - array("main","table","row")
  1012. $block_names[++$level] = $block_name;
  1013.  
  1014. // make block name (main.table.row)
  1015. $cur_block_name=implode('.', $block_names);
  1016.  
  1017. // build block parsing order (reverse)
  1018. $this->block_parse_order[] = $cur_block_name;
  1019.  
  1020. //add contents. trinary operator eliminates assign error in E_ALL reporting
  1021. $blocks[$cur_block_name] = isset($blocks[$cur_block_name]) ? $blocks[$cur_block_name] . $content : $content;
  1022.  
  1023. // add {_BLOCK_.blockname} string to parent block
  1024. $blocks[$parent_name] .= str_replace('\\', '', $this->tag_start_delim) . '_BLOCK_.' . $cur_block_name . str_replace('\\', '', $this->tag_end_delim);
  1025.  
  1026. // store sub block names for autoresetting and recursive parsing
  1027. $this->sub_blocks[$parent_name][] = $cur_block_name;
  1028.  
  1029. // store sub block names for autoresetting
  1030. $this->sub_blocks[$cur_block_name][] = '';
  1031.  
  1032. } else if (strtoupper($block_word) == $this->block_end_word) {
  1033.  
  1034. unset($block_names[$level--]);
  1035.  
  1036. $parent_name = implode('.', $block_names);
  1037.  
  1038. // add rest of block to parent block
  1039. $blocks[$parent_name] .= $content;
  1040. }
  1041. } else {
  1042.  
  1043. // no block delimiters found
  1044. // Saves doing multiple implodes - less overhead
  1045. $tmp = implode('.', $block_names);
  1046.  
  1047. if ($k) {
  1048. $blocks[$tmp] .= $this->block_start_delim;
  1049. }
  1050.  
  1051. // trinary operator eliminates assign error in E_ALL reporting
  1052. $blocks[$tmp] = isset($blocks[$tmp]) ? $blocks[$tmp] . $v : $v;
  1053. }
  1054. }
  1055.  
  1056. return $blocks;
  1057. }
  1058.  
  1059. /**
  1060. * Sub processing for assign_file method
  1061. *
  1062. * @access private
  1063. * @param string $name
  1064. * @param string $val
  1065. */
  1066. private function _assign_file_sub ($name, $val) {
  1067.  
  1068. if (isset($this->filevar_parent[$name])) {
  1069.  
  1070. if ($val != '') {
  1071.  
  1072. $val = $this->_r_getfile($val);
  1073.  
  1074. foreach($this->filevar_parent[$name] as $parent) {
  1075.  
  1076. if (isset($this->preparsed_blocks[$parent]) && !isset($this->filevars[$name])) {
  1077.  
  1078. $copy = $this->preparsed_blocks[$parent];
  1079.  
  1080. } elseif (isset($this->blocks[$parent])) {
  1081.  
  1082. $copy = $this->blocks[$parent];
  1083. }
  1084.  
  1085. $res = array();
  1086.  
  1087. preg_match_all($this->filevar_delim, $copy, $res, PREG_SET_ORDER);
  1088.  
  1089. if (is_array($res) && isset($res[0])) {
  1090.  
  1091. // Changed as per solution in SF bug ID #1261828
  1092. foreach ($res as $v) {
  1093.  
  1094. // Changed as per solution in SF bug ID #1261828
  1095. if ($v[1] == $name) {
  1096.  
  1097. // Changed as per solution in SF bug ID #1261828
  1098. $copy = preg_replace("/" . preg_quote($v[0]) . "/", "$val", $copy);
  1099. $this->preparsed_blocks = array_merge($this->preparsed_blocks, $this->_maketree($copy, $parent));
  1100. $this->filevar_parent = array_merge($this->filevar_parent, $this->_store_filevar_parents($this->preparsed_blocks));
  1101. }
  1102. }
  1103. }
  1104. }
  1105. }
  1106. }
  1107.  
  1108. $this->filevars[$name] = $val;
  1109. }
  1110.  
  1111. /**
  1112. * store container block's name for file variables
  1113. *
  1114. * @access public - aiming for private
  1115. * @param array $blocks
  1116. * @return array
  1117. */
  1118. public function _store_filevar_parents ($blocks){
  1119.  
  1120. $parents = array();
  1121.  
  1122. foreach ($blocks as $bname => $con) {
  1123.  
  1124. $res = array();
  1125.  
  1126. preg_match_all($this->filevar_delim, $con, $res);
  1127.  
  1128. foreach ($res[1] as $k => $v) {
  1129.  
  1130. $parents[$v][] = $bname;
  1131. }
  1132. }
  1133. return $parents;
  1134. }
  1135.  
  1136. /**
  1137. * Set the error string
  1138. *
  1139. * @access private
  1140. * @param string $str
  1141. */
  1142. private function _set_error ($str) {
  1143.  
  1144. // JRC: 3/1/2003 Made to append the error messages
  1145. $this->_error .= '* ' . $str . " *\n";
  1146. // JRC: 3/1/2003 Removed trigger error, use this externally if you want it eg. trigger_error($xtpl->get_error())
  1147. //trigger_error($this->get_error());
  1148. }
  1149.  
  1150. /**
  1151. * returns the contents of a file
  1152. *
  1153. * @access protected
  1154. * @param string $file
  1155. * @return string
  1156. */
  1157. protected function _getfile ($file) {
  1158.  
  1159. if (!isset($file)) {
  1160. // JC 19/12/02 added $file to error message
  1161. $this->_set_error('!isset file name!' . $file);
  1162.  
  1163. return '';
  1164. }
  1165.  
  1166. // check if filename is mapped to other filename
  1167. if (isset($this->files)) {
  1168.  
  1169. if (isset($this->files[$file])) {
  1170.  
  1171. $file = $this->files[$file];
  1172. }
  1173. }
  1174.  
  1175. // prepend template dir
  1176. if (!empty($this->tpldir)) {
  1177.  
  1178. /**
  1179. * Support hierarchy of file locations to search
  1180. *
  1181. * @example Supply array of filepaths when instantiating
  1182. * First path supplied that has the named file is prioritised
  1183. * $xtpl = new XTemplate('myfile.xtpl', array('.','/mypath', '/mypath2'));
  1184. * @since 29/05/2007
  1185. */
  1186. if (is_array($this->tpldir)) {
  1187.  
  1188. foreach ($this->tpldir as $dir) {
  1189.  
  1190. if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) {
  1191. $file = $dir . DIRECTORY_SEPARATOR . $file;
  1192. break;
  1193. }
  1194. }
  1195. } else {
  1196.  
  1197. $file = $this->tpldir. DIRECTORY_SEPARATOR . $file;
  1198. }
  1199. }
  1200.  
  1201. $file_text = '';
  1202.  
  1203. if (isset($this->filecache[$file])) {
  1204.  
  1205. $file_text .= $this->filecache[$file];
  1206.  
  1207. if ($this->debug) {
  1208. $file_text = '<!-- XTemplate debug cached: ' . realpath($file) . ' -->' . "\n" . $file_text;
  1209. }
  1210.  
  1211. } else {
  1212.  
  1213. if (is_file($file) && is_readable($file)) {
  1214.  
  1215. if (filesize($file)) {
  1216.  
  1217. if (!($fh = fopen($file, 'r'))) {
  1218.  
  1219. $this->_set_error('Cannot open file: ' . realpath($file));
  1220. return '';
  1221. }
  1222.  
  1223. $file_text .= fread($fh,filesize($file));
  1224. fclose($fh);
  1225.  
  1226. }
  1227.  
  1228. if ($this->debug) {
  1229. $file_text = '<!-- XTemplate debug: ' . realpath($file) . ' -->' . "\n" . $file_text;
  1230. }
  1231.  
  1232. } elseif (str_replace('.', '', phpversion()) >= '430' && $file_text = @file_get_contents($file, true)) {
  1233. // Enable use of include path by using file_get_contents
  1234. // Implemented at suggestion of SF Feature Request ID #1529478 michaelgroh
  1235. if ($file_text === false) {
  1236. $this->_set_error("[" . realpath($file) . "] ($file) does not exist");
  1237. $file_text = "<b>__XTemplate fatal error: file [$file] does not exist in the include path__</b>";
  1238. } elseif ($this->debug) {
  1239. $file_text = '<!-- XTemplate debug: ' . realpath($file) . ' (via include path) -->' . "\n" . $file_text;
  1240. }
  1241. } elseif (!is_file($file)) {
  1242.  
  1243. // NW 17 Oct 2002 : Added realpath around the file name to identify where the code is searching.
  1244. $this->_set_error("[" . realpath($file) . "] ($file) does not exist");
  1245. $file_text .= "<b>__XTemplate fatal error: file [$file] does not exist__</b>";
  1246.  
  1247. } elseif (!is_readable($file)) {
  1248.  
  1249. $this->_set_error("[" . realpath($file) . "] ($file) is not readable");
  1250. $file_text .= "<b>__XTemplate fatal error: file [$file] is not readable__</b>";
  1251. }
  1252.  
  1253. $this->filecache[$file] = $file_text;
  1254. }
  1255.  
  1256. return $file_text;
  1257. }
  1258.  
  1259. /**
  1260. * recursively gets the content of a file with {FILE "filename.tpl"} directives
  1261. *
  1262. * @access public - aiming for private
  1263. * @param string $file
  1264. * @return string
  1265. */
  1266. public function _r_getfile ($file) {
  1267.  
  1268. $text = $this->_getfile($file);
  1269.  
  1270. $res = array();
  1271.  
  1272. while (preg_match($this->file_delim,$text,$res)) {
  1273.  
  1274. $text2 = $this->_getfile($res[1]);
  1275. $text = preg_replace("'".preg_quote($res[0])."'",$text2,$text);
  1276. }
  1277.  
  1278. return $text;
  1279. }
  1280.  
  1281.  
  1282. /**
  1283. * add an outer block delimiter set useful for rtfs etc - keeps them editable in word
  1284. *
  1285. * @access private
  1286. */
  1287. private function _add_outer_block () {
  1288.  
  1289. $before = $this->block_start_delim . $this->block_start_word . ' ' . $this->mainblock . ' ' . $this->block_end_delim;
  1290. $after = $this->block_start_delim . $this->block_end_word . ' ' . $this->mainblock . ' ' . $this->block_end_delim;
  1291.  
  1292. $this->filecontents = $before . "\n" . $this->filecontents . "\n" . $after;
  1293. }
  1294.  
  1295. /**
  1296. * Debug function - var_dump wrapped in '<pre></pre>' tags
  1297. *
  1298. * @access private
  1299. * @param multiple var_dumps all the supplied arguments
  1300. */
  1301. private function _pre_var_dump ($args) {
  1302.  
  1303. if ($this->debug) {
  1304. echo '<pre>';
  1305. var_dump(func_get_args());
  1306. echo '</pre>';
  1307. }
  1308. }
  1309. } /* end of XTemplate class. */
  1310.  
  1311. ?>

Documentation generated on Tue, 29 May 2007 19:29:36 +0100 by phpDocumentor 1.3.0RC3