PageRenderTime 62ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lib/HTML/Template/IT.php

https://github.com/clayhinson/sgl2
PHP | 1127 lines | 461 code | 115 blank | 551 comment | 63 complexity | 2af8d7f10f7819293b83f55237fedf52 MD5 | raw file
  1. <?php
  2. /**
  3. * Integrated Template - IT
  4. *
  5. * PHP version 4
  6. *
  7. * Copyright (c) 1997-2007 Ulf Wendel, Pierre-Alain Joye,
  8. * David Soria Parra
  9. *
  10. * This source file is subject to the New BSD license, That is bundled
  11. * with this package in the file LICENSE, and is available through
  12. * the world-wide-web at
  13. * http://www.opensource.org/licenses/bsd-license.php
  14. * If you did not receive a copy of the new BSDlicense and are unable
  15. * to obtain it through the world-wide-web, please send a note to
  16. * pajoye@php.net so we can mail you a copy immediately.
  17. *
  18. * Author: Ulf Wendel <ulf.wendel@phpdoc.de>
  19. * Pierre-Alain Joye <pajoye@php.net>
  20. * David Soria Parra <dsp@php.net>
  21. *
  22. * @category HTML
  23. * @package HTML_Template_IT
  24. * @author Ulf Wendel <uw@netuse.de>
  25. * @license BSD http://www.opensource.org/licenses/bsd-license.php
  26. * @version CVS: $Id: IT.php,v 1.27 2008/11/14 23:57:17 kguest Exp $
  27. * @link http://pear.php.net/packages/HTML_Template_IT
  28. * @access public
  29. */
  30. require_once 'PEAR.php';
  31. define('IT_OK', 1);
  32. define('IT_ERROR', -1);
  33. define('IT_TPL_NOT_FOUND', -2);
  34. define('IT_BLOCK_NOT_FOUND', -3);
  35. define('IT_BLOCK_DUPLICATE', -4);
  36. define('IT_UNKNOWN_OPTION', -6);
  37. /**
  38. * Integrated Template - IT
  39. *
  40. * Well there's not much to say about it. I needed a template class that
  41. * supports a single template file with multiple (nested) blocks inside and
  42. * a simple block API.
  43. *
  44. * The Isotemplate API is somewhat tricky for a beginner although it is the best
  45. * one you can build. template::parse() [phplib template = Isotemplate] requests
  46. * you to name a source and a target where the current block gets parsed into.
  47. * Source and target can be block names or even handler names. This API gives you
  48. * a maximum of fexibility but you always have to know what you do which is
  49. * quite unusual for php skripter like me.
  50. *
  51. * I noticed that I do not any control on which block gets parsed into which one.
  52. * If all blocks are within one file, the script knows how they are nested and in
  53. * which way you have to parse them. IT knows that inner1 is a child of block2,
  54. * there's no need to tell him about this.
  55. *
  56. * <table border>
  57. * <tr>
  58. * <td colspan=2>
  59. * __global__
  60. * <p>
  61. * (hidden and automatically added)
  62. * </td>
  63. * </tr>
  64. * <tr>
  65. * <td>block1</td>
  66. * <td>
  67. * <table border>
  68. * <tr>
  69. * <td colspan=2>block2</td>
  70. * </tr>
  71. * <tr>
  72. * <td>inner1</td>
  73. * <td>inner2</td>
  74. * </tr>
  75. * </table>
  76. * </td>
  77. * </tr>
  78. * </table>
  79. *
  80. * To add content to block1 you simply type:
  81. * <code>$tpl->setCurrentBlock("block1");</code>
  82. * and repeat this as often as needed:
  83. * <code>
  84. * $tpl->setVariable(...);
  85. * $tpl->parseCurrentBlock();
  86. * </code>
  87. *
  88. * To add content to block2 you would type something like:
  89. * <code>
  90. * $tpl->setCurrentBlock("inner1");
  91. * $tpl->setVariable(...);
  92. * $tpl->parseCurrentBlock();
  93. *
  94. * $tpl->setVariable(...);
  95. * $tpl->parseCurrentBlock();
  96. *
  97. * $tpl->parse("block1");
  98. * </code>
  99. *
  100. * This will result in one repition of block1 which contains two repitions
  101. * of inner1. inner2 will be removed if $removeEmptyBlock is set to true
  102. * which is the default.
  103. *
  104. * Usage:
  105. * <code>
  106. * $tpl = new HTML_Template_IT( [string filerootdir] );
  107. *
  108. * // load a template or set it with setTemplate()
  109. * $tpl->loadTemplatefile( string filename [, boolean removeUnknownVariables, boolean removeEmptyBlocks] )
  110. *
  111. * // set "global" Variables meaning variables not beeing within a (inner) block
  112. * $tpl->setVariable( string variablename, mixed value );
  113. *
  114. * // like with the Isotemplates there's a second way to use setVariable()
  115. * $tpl->setVariable( array ( string varname => mixed value ) );
  116. *
  117. * // Let's use any block, even a deeply nested one
  118. * $tpl->setCurrentBlock( string blockname );
  119. *
  120. * // repeat this as often as you need it.
  121. * $tpl->setVariable( array ( string varname => mixed value ) );
  122. * $tpl->parseCurrentBlock();
  123. *
  124. * // get the parsed template or print it: $tpl->show()
  125. * $tpl->get();
  126. * </code>
  127. *
  128. * @category HTML
  129. * @package HTML_Template_IT
  130. * @author Ulf Wendel <uw@netuse.de>
  131. * @license BSD http://www.opensource.org/licenses/bsd-license.php
  132. * @link http://pear.php.net/packages/HTML_Template_IT
  133. * @access public
  134. */
  135. class HTML_Template_IT
  136. {
  137. /**
  138. * Contains the error objects
  139. * @var array
  140. * @access public
  141. * @see halt(), $printError, $haltOnError
  142. */
  143. var $err = array();
  144. /**
  145. * Clear cache on get()?
  146. * @var boolean
  147. * @acces public
  148. */
  149. var $clearCache = false;
  150. /**
  151. * First character of a variable placeholder ( _{_VARIABLE} ).
  152. * @var string
  153. * @access public
  154. * @see $closingDelimiter, $blocknameRegExp, $variablenameRegExp
  155. */
  156. var $openingDelimiter = '{';
  157. /**
  158. * Last character of a variable placeholder ( {VARIABLE_}_ ).
  159. * @var string
  160. * @access public
  161. * @see $openingDelimiter, $blocknameRegExp, $variablenameRegExp
  162. */
  163. var $closingDelimiter = '}';
  164. /**
  165. * RegExp matching a block in the template.
  166. * Per default "sm" is used as the regexp modifier, "i" is missing.
  167. * That means a case sensitive search is done.
  168. * @var string
  169. * @access public
  170. * @see $variablenameRegExp, $openingDelimiter, $closingDelimiter
  171. */
  172. var $blocknameRegExp = '[\.0-9A-Za-z_-]+';
  173. /**
  174. * RegExp matching a variable placeholder in the template.
  175. * Per default "sm" is used as the regexp modifier, "i" is missing.
  176. * That means a case sensitive search is done.
  177. * @var string
  178. * @access public
  179. * @see $blocknameRegExp, $openingDelimiter, $closingDelimiter
  180. */
  181. var $variablenameRegExp = '[\.0-9A-Za-z_-]+';
  182. /**
  183. * RegExp used to find variable placeholder, filled by the constructor.
  184. * @var string Looks somewhat like @(delimiter varname delimiter)@
  185. * @access private
  186. * @see IntegratedTemplate()
  187. */
  188. var $variablesRegExp = '';
  189. /**
  190. * RegExp used to strip unused variable placeholder.
  191. * @access private
  192. * @brother $variablesRegExp
  193. */
  194. var $removeVariablesRegExp = '';
  195. /**
  196. * Controls the handling of unknown variables, default is remove.
  197. * @var boolean
  198. * @access public
  199. */
  200. var $removeUnknownVariables = true;
  201. /**
  202. * Controls the handling of empty blocks, default is remove.
  203. * @var boolean
  204. * @access public
  205. */
  206. var $removeEmptyBlocks = true;
  207. /**
  208. * RegExp used to find blocks an their content, filled by the constructor.
  209. * @var string
  210. * @see IntegratedTemplate()
  211. * @access private
  212. */
  213. var $blockRegExp = '';
  214. /**
  215. * Name of the current block.
  216. * @var string
  217. * @access private
  218. */
  219. var $currentBlock = '__global__';
  220. /**
  221. * Content of the template.
  222. * @var string
  223. * @access private
  224. */
  225. var $template = '';
  226. /**
  227. * Array of all blocks and their content.
  228. *
  229. * @var array
  230. * @see findBlocks()
  231. * @access private
  232. */
  233. var $blocklist = array();
  234. /**
  235. * Array with the parsed content of a block.
  236. *
  237. * @var array
  238. * @access private
  239. */
  240. var $blockdata = array();
  241. /**
  242. * Array of variables in a block.
  243. * @var array
  244. * @access private
  245. */
  246. var $blockvariables = array();
  247. /**
  248. * Array of inner blocks of a block.
  249. * @var array
  250. * @access private
  251. */
  252. var $blockinner = array();
  253. /**
  254. * List of blocks to preverse even if they are "empty".
  255. *
  256. * This is something special. Sometimes you have blocks that
  257. * should be preserved although they are empty (no placeholder replaced).
  258. * Think of a shopping basket. If it's empty you have to drop a message to
  259. * the user. If it's filled you have to show the contents of
  260. * the shopping baseket. Now where do you place the message that the basket
  261. * is empty? It's no good idea to place it in you applications as customers
  262. * tend to like unecessary minor text changes. Having another template file
  263. * for an empty basket means that it's very likely that one fine day
  264. * the filled and empty basket templates have different layout. I decided
  265. * to introduce blocks that to not contain any placeholder but only
  266. * text such as the message "Your shopping basked is empty".
  267. *
  268. * Now if there is no replacement done in such a block the block will
  269. * be recognized as "empty" and by default ($removeEmptyBlocks = true) be
  270. * stripped off. To avoid thisyou can now call touchBlock() to avoid this.
  271. *
  272. * The array $touchedBlocks stores a list of touched block which must not
  273. * be removed even if they are empty.
  274. *
  275. * @var array $touchedBlocks
  276. * @see touchBlock(), $removeEmptyBlocks
  277. * @access private
  278. */
  279. var $touchedBlocks = array();
  280. /**
  281. * List of blocks which should not be shown even if not "empty"
  282. * @var array $_hiddenBlocks
  283. * @see hideBlock(), $removeEmptyBlocks
  284. * @access private
  285. */
  286. var $_hiddenBlocks = array();
  287. /**
  288. * Variable cache.
  289. *
  290. * Variables get cached before any replacement is done.
  291. * Advantage: empty blocks can be removed automatically.
  292. * Disadvantage: might take some more memory
  293. *
  294. * @var array
  295. * @see setVariable(), $clearCacheOnParse
  296. * @access private
  297. */
  298. var $variableCache = array();
  299. /**
  300. * Clear the variable cache on parse?
  301. *
  302. * If you're not an expert just leave the default false.
  303. * True reduces memory consumption somewhat if you tend to
  304. * add lots of values for unknown placeholder.
  305. *
  306. * @var boolean
  307. * @access public
  308. */
  309. var $clearCacheOnParse = false;
  310. /**
  311. * Root directory for all file operations.
  312. * The string gets prefixed to all filenames given.
  313. * @var string
  314. * @see HTML_Template_IT(), setRoot()
  315. * @access private
  316. */
  317. var $fileRoot = '';
  318. /**
  319. * Internal flag indicating that a blockname was used multiple times.
  320. * @var boolean
  321. * @access private
  322. */
  323. var $flagBlocktrouble = false;
  324. /**
  325. * Flag indicating that the global block was parsed.
  326. * @var boolean
  327. * @access private
  328. */
  329. var $flagGlobalParsed = false;
  330. /**
  331. * EXPERIMENTAL! FIXME!
  332. * Flag indication that a template gets cached.
  333. *
  334. * Complex templates require some times to be preparsed
  335. * before the replacement can take place. Often I use
  336. * one template file over and over again but I don't know
  337. * before that I will use the same template file again.
  338. * Now IT could notice this and skip the preparse.
  339. *
  340. * @var boolean
  341. * @access private
  342. */
  343. var $flagCacheTemplatefile = true;
  344. /**
  345. * EXPERIMENTAL! FIXME!
  346. * @access private
  347. */
  348. var $lastTemplatefile = '';
  349. /**
  350. * $_options['preserve_data'] Whether to substitute variables and remove
  351. * empty placeholders in data passed through setVariable
  352. * (see also bugs #20199, #21951).
  353. * $_options['use_preg'] Whether to use preg_replace instead of
  354. * str_replace in parse()
  355. * (this is a backwards compatibility feature, see also bugs #21951, #20392)
  356. *
  357. * @var array
  358. * @access private
  359. */
  360. var $_options = array(
  361. 'preserve_data' => false,
  362. 'use_preg' => true,
  363. 'preserve_input'=> true
  364. );
  365. /**
  366. * Builds some complex regular expressions and optinally sets the
  367. * file root directory.
  368. *
  369. * Make sure that you call this constructor if you derive your template
  370. * class from this one.
  371. *
  372. * @param string $root File root directory, prefix for all filenames
  373. * given to the object.
  374. * @param mixed $options Unknown
  375. *
  376. * @see setRoot()
  377. * @access public
  378. */
  379. function HTML_Template_IT($root = '', $options = null)
  380. {
  381. if (!is_null($options)) {
  382. $this->setOptions($options);
  383. }
  384. $this->variablesRegExp = '@' . $this->openingDelimiter .
  385. '(' . $this->variablenameRegExp . ')' .
  386. $this->closingDelimiter . '@sm';
  387. $this->removeVariablesRegExp = '@' . $this->openingDelimiter .
  388. "\s*(" . $this->variablenameRegExp .
  389. ")\s*" . $this->closingDelimiter .'@sm';
  390. $this->blockRegExp = '@<!--\s+BEGIN\s+(' . $this->blocknameRegExp .
  391. ')\s+-->(.*)<!--\s+END\s+\1\s+-->@sm';
  392. $this->setRoot($root);
  393. } // end constructor
  394. /**
  395. * Sets the option for the template class
  396. *
  397. * @param string $option option name
  398. * @param mixed $value option value
  399. *
  400. * @access public
  401. * @return mixed IT_OK on success, error object on failure
  402. */
  403. function setOption($option, $value)
  404. {
  405. if (array_key_exists($option, $this->_options)) {
  406. $this->_options[$option] = $value;
  407. return IT_OK;
  408. }
  409. return PEAR::raiseError(
  410. $this->errorMessage(IT_UNKNOWN_OPTION) . ": '{$option}'",
  411. IT_UNKNOWN_OPTION);
  412. }
  413. /**
  414. * Sets the options for the template class
  415. *
  416. * @param string[] $options options array of options
  417. * default value:
  418. * 'preserve_data' => false,
  419. * 'use_preg' => true
  420. *
  421. * @access public
  422. * @return mixed IT_OK on success, error object on failure
  423. * @see $options
  424. */
  425. function setOptions($options)
  426. {
  427. if (is_array($options)) {
  428. foreach ($options as $option => $value) {
  429. $error = $this->setOption($option, $value);
  430. if (PEAR::isError($error)) {
  431. return $error;
  432. }
  433. }
  434. }
  435. return IT_OK;
  436. }
  437. /**
  438. * Print a certain block with all replacements done.
  439. *
  440. * @param string $block block
  441. *
  442. * @brother get()
  443. * @access public
  444. * @return null
  445. */
  446. function show($block = '__global__')
  447. {
  448. print $this->get($block);
  449. } // end func show
  450. /**
  451. * Returns a block with all replacements done.
  452. *
  453. * @param string $block name of the block
  454. *
  455. * @return string
  456. * @throws PEAR_Error
  457. * @access public
  458. * @see show()
  459. */
  460. function get($block = '__global__')
  461. {
  462. if ($block == '__global__' && !$this->flagGlobalParsed) {
  463. $this->parse('__global__');
  464. }
  465. if (!isset($this->blocklist[$block])) {
  466. $this->err[] = PEAR::raiseError($this->errorMessage(IT_BLOCK_NOT_FOUND) .
  467. '"' . $block . "'",
  468. IT_BLOCK_NOT_FOUND);
  469. return '';
  470. }
  471. if (isset($this->blockdata[$block])) {
  472. $ret = $this->blockdata[$block];
  473. if ($this->clearCache) {
  474. unset($this->blockdata[$block]);
  475. if ($block == '__global__') {
  476. $this->flagGlobalParsed = false;
  477. }
  478. }
  479. if ($this->_options['preserve_data']) {
  480. $ret = str_replace($this->openingDelimiter .
  481. '%preserved%' . $this->closingDelimiter,
  482. $this->openingDelimiter,
  483. $ret);
  484. }
  485. return $ret;
  486. }
  487. return '';
  488. } // end func get()
  489. /**
  490. * Parses the given block.
  491. *
  492. * @param string $block name of the block to be parsed
  493. * @param bool $flag_recursion unknown
  494. *
  495. * @access public
  496. * @see parseCurrentBlock()
  497. * @throws PEAR_Error
  498. * @return null
  499. */
  500. function parse($block = '__global__', $flag_recursion = false)
  501. {
  502. static $regs, $values;
  503. if (!isset($this->blocklist[$block])) {
  504. return PEAR::raiseError($this->errorMessage(IT_BLOCK_NOT_FOUND)
  505. . '"' . $block . "'", IT_BLOCK_NOT_FOUND);
  506. }
  507. if ($block == '__global__') {
  508. $this->flagGlobalParsed = true;
  509. }
  510. if (!$flag_recursion) {
  511. $regs = array();
  512. $values = array();
  513. }
  514. $outer = $this->blocklist[$block];
  515. $empty = true;
  516. $variablelist = array();
  517. if ($this->clearCacheOnParse) {
  518. foreach ($this->variableCache as $name => $value) {
  519. $regs[] = $this->openingDelimiter .
  520. $name . $this->closingDelimiter;
  521. $values[] = $value;
  522. $empty = false;
  523. $variablelist[] = $name;
  524. }
  525. $this->variableCache = array();
  526. } else {
  527. foreach ($this->blockvariables[$block] as $allowedvar => $v) {
  528. if (isset($this->variableCache[$allowedvar])) {
  529. $regs[] = $this->openingDelimiter .
  530. $allowedvar . $this->closingDelimiter;
  531. $values[] = $this->variableCache[$allowedvar];
  532. unset($this->variableCache[$allowedvar]);
  533. $empty = false;
  534. $variablelist[] = $allowedvar;
  535. }
  536. }
  537. }
  538. if (isset($this->blockinner[$block])) {
  539. foreach ($this->blockinner[$block] as $k => $innerblock) {
  540. $this->parse($innerblock, true);
  541. if ($this->blockdata[$innerblock] != '') {
  542. $empty = false;
  543. }
  544. $placeholder = $this->openingDelimiter . "__" .
  545. $innerblock . "__" . $this->closingDelimiter;
  546. $outer = str_replace($placeholder,
  547. $this->blockdata[$innerblock], $outer);
  548. $this->blockdata[$innerblock] = "";
  549. }
  550. }
  551. if (!$flag_recursion && 0 != count($values)) {
  552. if ($this->_options['use_preg']) {
  553. $regs = array_map(array(&$this, '_addPregDelimiters'), $regs);
  554. $values = array_map(array(&$this, '_escapeBackreferences'), $values);
  555. $funcReplace = 'preg_replace';
  556. } else {
  557. $funcReplace = 'str_replace';
  558. }
  559. if ($this->_options['preserve_data']) {
  560. $values = array_map(array(&$this, '_preserveOpeningDelimiter'),
  561. $values);
  562. }
  563. $outer = $funcReplace($regs, $values, $outer);
  564. }
  565. if ($this->removeUnknownVariables) {
  566. $outer = $this->removeUnknownVariablesFromBlock($block,
  567. $outer,
  568. $variablelist);
  569. }
  570. if ($empty) {
  571. if (!$this->removeEmptyBlocks) {
  572. $this->blockdata[$block ] .= $outer;
  573. } else {
  574. if (isset($this->touchedBlocks[$block])) {
  575. $this->blockdata[$block] .= $outer;
  576. unset($this->touchedBlocks[$block]);
  577. }
  578. }
  579. } else {
  580. if (empty($this->blockdata[$block])) {
  581. $this->blockdata[$block] = $outer;
  582. } else {
  583. $this->blockdata[$block] .= $outer;
  584. }
  585. }
  586. return $empty;
  587. } // end func parse
  588. /**
  589. * Removes unknown variables from block. If preserve_input is set to true
  590. * only unknown variables that were present during setTemplate or
  591. * loadTemplatefile are removed. Thus you can set a variable to
  592. * "{SOMEINPUTDATA}" which is preserved.
  593. *
  594. * @param string $blockname block
  595. * @param string $blockinner unknown
  596. * @param string $variableList unknown
  597. *
  598. * @see parse()
  599. * @access private
  600. * @return null
  601. */
  602. function removeUnknownVariablesFromBlock ($blockname, $blockinner, $variableList)
  603. {
  604. if ($this->_options['preserve_input']) {
  605. foreach ($this->blockvariables[$blockname] as $var => $setted) {
  606. if (!in_array($var, $variableList)) {
  607. $blockinner = str_replace($this->openingDelimiter .
  608. $var . $this->closingDelimiter, '', $blockinner);
  609. }
  610. }
  611. } else {
  612. $blockinner = preg_replace($this->removeVariablesRegExp,
  613. '',
  614. $blockinner);
  615. }
  616. return $blockinner;
  617. }
  618. /**
  619. * Parses the current block
  620. *
  621. * @see parse(), setCurrentBlock(), $currentBlock
  622. * @access public
  623. * @return null
  624. */
  625. function parseCurrentBlock()
  626. {
  627. return $this->parse($this->currentBlock);
  628. } // end func parseCurrentBlock
  629. /**
  630. * Sets a variable value.
  631. *
  632. * The function can be used eighter like setVariable( "varname", "value")
  633. * or with one array $variables["varname"] = "value"
  634. * given setVariable($variables) quite like phplib templates set_var().
  635. *
  636. * @param mixed $variable string with the variable name or an array
  637. * %variables["varname"] = "value"
  638. * @param string $value value of the variable or empty if $variable
  639. * is an array.
  640. *
  641. * @access public
  642. * @return null
  643. */
  644. function setVariable($variable, $value = '')
  645. {
  646. if (is_array($variable)) {
  647. $this->variableCache = array_merge($this->variableCache, $variable);
  648. } else {
  649. $this->variableCache[$variable] = $value;
  650. }
  651. } // end func setVariable
  652. /**
  653. * Sets the name of the current block that is the block where variables
  654. * are added.
  655. *
  656. * @param string $block name of the block
  657. *
  658. * @return boolean false on failure, otherwise true
  659. * @throws PEAR_Error
  660. * @access public
  661. */
  662. function setCurrentBlock($block = '__global__')
  663. {
  664. if (!isset($this->blocklist[$block])) {
  665. return PEAR::raiseError($this->errorMessage(IT_BLOCK_NOT_FOUND)
  666. . '"' . $block . "'",
  667. IT_BLOCK_NOT_FOUND);
  668. }
  669. $this->currentBlock = $block;
  670. return true;
  671. } // end func setCurrentBlock
  672. /**
  673. * Preserves an empty block even if removeEmptyBlocks is true.
  674. *
  675. * @param string $block name of the block
  676. *
  677. * @return boolean false on false, otherwise true
  678. * @throws PEAR_Error
  679. * @access public
  680. * @see $removeEmptyBlocks
  681. */
  682. function touchBlock($block)
  683. {
  684. if (!isset($this->blocklist[$block])) {
  685. return PEAR::raiseError($this->errorMessage(IT_BLOCK_NOT_FOUND) .
  686. '"' . $block . "'", IT_BLOCK_NOT_FOUND);
  687. }
  688. $this->touchedBlocks[$block] = true;
  689. return true;
  690. } // end func touchBlock
  691. /**
  692. * Clears all datafields of the object and rebuild the internal blocklist
  693. *
  694. * LoadTemplatefile() and setTemplate() automatically call this function
  695. * when a new template is given. Don't use this function
  696. * unless you know what you're doing.
  697. *
  698. * @access private
  699. * @see free()
  700. * @return null
  701. */
  702. function init()
  703. {
  704. $this->free();
  705. $this->findBlocks($this->template);
  706. // we don't need it any more
  707. $this->template = '';
  708. $this->buildBlockvariablelist();
  709. } // end func init
  710. /**
  711. * Clears all datafields of the object.
  712. *
  713. * Don't use this function unless you know what you're doing.
  714. *
  715. * @access private
  716. * @see init()
  717. * @return null
  718. */
  719. function free()
  720. {
  721. $this->err = array();
  722. $this->currentBlock = '__global__';
  723. $this->variableCache = array();
  724. $this->blocklist = array();
  725. $this->touchedBlocks = array();
  726. $this->flagBlocktrouble = false;
  727. $this->flagGlobalParsed = false;
  728. } // end func free
  729. /**
  730. * Sets the template.
  731. *
  732. * You can eighter load a template file from disk with
  733. * LoadTemplatefile() or set the template manually using this function.
  734. *
  735. * @param string $template template content
  736. * @param bool $removeUnknownVariables how to handle unknown variables.
  737. * @param bool $removeEmptyBlocks how to handle empty blocks.
  738. *
  739. * @see LoadTemplatefile(), $template
  740. * @access public
  741. * @return boolean
  742. */
  743. function setTemplate( $template, $removeUnknownVariables = true,
  744. $removeEmptyBlocks = true)
  745. {
  746. $this->removeUnknownVariables = $removeUnknownVariables;
  747. $this->removeEmptyBlocks = $removeEmptyBlocks;
  748. if ($template == '' && $this->flagCacheTemplatefile) {
  749. $this->variableCache = array();
  750. $this->blockdata = array();
  751. $this->touchedBlocks = array();
  752. $this->currentBlock = '__global__';
  753. } else {
  754. $this->template = '<!-- BEGIN __global__ -->' . $template .
  755. '<!-- END __global__ -->';
  756. $this->init();
  757. }
  758. if ($this->flagBlocktrouble) {
  759. return false;
  760. }
  761. return true;
  762. } // end func setTemplate
  763. /**
  764. * Reads a template file from the disk.
  765. *
  766. * @param string $filename name of the template file
  767. * @param bool $removeUnknownVariables how to handle unknown variables.
  768. * @param bool $removeEmptyBlocks how to handle empty blocks.
  769. *
  770. * @access public
  771. * @return boolean false on failure, otherwise true
  772. * @see $template, setTemplate(), $removeUnknownVariables,
  773. * $removeEmptyBlocks
  774. */
  775. function loadTemplatefile( $filename,
  776. $removeUnknownVariables = true,
  777. $removeEmptyBlocks = true )
  778. {
  779. $template = '';
  780. if (!$this->flagCacheTemplatefile ||
  781. $this->lastTemplatefile != $filename
  782. ) {
  783. $template = $this->getFile($filename);
  784. }
  785. $this->lastTemplatefile = $filename;
  786. return $template != '' ?
  787. $this->setTemplate($template,
  788. $removeUnknownVariables,
  789. $removeEmptyBlocks) : false;
  790. } // end func LoadTemplatefile
  791. /**
  792. * Sets the file root. The file root gets prefixed to all filenames passed
  793. * to the object.
  794. *
  795. * Make sure that you override this function when using the class
  796. * on windows.
  797. *
  798. * @param string $root File root
  799. *
  800. * @see HTML_Template_IT()
  801. * @access public
  802. * @return null
  803. */
  804. function setRoot($root)
  805. {
  806. if ($root != '' && substr($root, -1) != '/') {
  807. $root .= '/';
  808. }
  809. $this->fileRoot = $root;
  810. } // end func setRoot
  811. /**
  812. * Build a list of all variables within of a block
  813. *
  814. * @access private
  815. * @return null
  816. */
  817. function buildBlockvariablelist()
  818. {
  819. foreach ($this->blocklist as $name => $content) {
  820. preg_match_all($this->variablesRegExp, $content, $regs);
  821. if (count($regs[1]) != 0) {
  822. foreach ($regs[1] as $k => $var) {
  823. $this->blockvariables[$name][$var] = true;
  824. }
  825. } else {
  826. $this->blockvariables[$name] = array();
  827. }
  828. }
  829. } // end func buildBlockvariablelist
  830. /**
  831. * Returns a list of all global variables
  832. *
  833. * @access public
  834. * @return array
  835. */
  836. function getGlobalvariables()
  837. {
  838. $regs = array();
  839. $values = array();
  840. foreach ($this->blockvariables['__global__'] as $allowedvar => $v) {
  841. if (isset($this->variableCache[$allowedvar])) {
  842. $regs[] = '@' . $this->openingDelimiter .
  843. $allowedvar . $this->closingDelimiter . '@';
  844. $values[] = $this->variableCache[$allowedvar];
  845. unset($this->variableCache[$allowedvar]);
  846. }
  847. }
  848. return array($regs, $values);
  849. } // end func getGlobalvariables
  850. /**
  851. * Recusively builds a list of all blocks within the template.
  852. *
  853. * @param string $string string that gets scanned
  854. *
  855. * @access private
  856. * @see $blocklist
  857. * @return array
  858. */
  859. function findBlocks($string)
  860. {
  861. $blocklist = array();
  862. if (preg_match_all($this->blockRegExp, $string, $regs, PREG_SET_ORDER)) {
  863. foreach ($regs as $k => $match) {
  864. $blockname = $match[1];
  865. $blockcontent = $match[2];
  866. if (isset($this->blocklist[$blockname])) {
  867. $msg = $this->errorMessage(IT_BLOCK_DUPLICATE, $blockname);
  868. $this->err[] = PEAR::raiseError($msg, IT_BLOCK_DUPLICATE);
  869. $this->flagBlocktrouble = true;
  870. }
  871. $this->blocklist[$blockname] = $blockcontent;
  872. $this->blockdata[$blockname] = "";
  873. $blocklist[] = $blockname;
  874. $inner = $this->findBlocks($blockcontent);
  875. $regex = '@<!--\s+BEGIN\s+%s\s+-->(.*)<!--\s+END\s+%s\s+-->@sm';
  876. foreach ($inner as $k => $name) {
  877. $pattern = sprintf($regex, preg_quote($name), preg_quote($name));
  878. $this->blocklist[$blockname] = preg_replace($pattern,
  879. $this->openingDelimiter .
  880. '__' . $name . '__' .
  881. $this->closingDelimiter,
  882. $this->blocklist[$blockname]);
  883. $this->blockinner[$blockname][] = $name;
  884. $this->blockparents[$name] = $blockname;
  885. }
  886. }
  887. }
  888. return $blocklist;
  889. } // end func findBlocks
  890. /**
  891. * Reads a file from disk and returns its content.
  892. *
  893. * @param string $filename Filename
  894. *
  895. * @return string Filecontent
  896. * @access private
  897. */
  898. function getFile($filename)
  899. {
  900. if ($filename{0} == '/' && substr($this->fileRoot, -1) == '/') {
  901. $filename = substr($filename, 1);
  902. }
  903. $filename = $this->fileRoot . $filename;
  904. if (!($fh = @fopen($filename, 'r'))) {
  905. $this->err[] = PEAR::raiseError($this->errorMessage(IT_TPL_NOT_FOUND) .
  906. ': "' .$filename .'"',
  907. IT_TPL_NOT_FOUND);
  908. return "";
  909. }
  910. $fsize = filesize($filename);
  911. if ($fsize < 1) {
  912. fclose($fh);
  913. return '';
  914. }
  915. $content = fread($fh, $fsize);
  916. fclose($fh);
  917. return preg_replace("#<!-- INCLUDE (.*) -->#ime",
  918. "\$this->getFile('\\1')",
  919. $content);
  920. } // end func getFile
  921. /**
  922. * Adds delimiters to a string, so it can be used as a pattern
  923. * in preg_* functions
  924. *
  925. * @param string $str input
  926. *
  927. * @return string
  928. * @access private
  929. */
  930. function _addPregDelimiters($str)
  931. {
  932. return '@' . preg_quote($str) . '@';
  933. }
  934. /**
  935. * Escapes $ and \ as preg_replace will treat
  936. * them as a backreference and not literal.
  937. * See bug #9501
  938. *
  939. * @param string $str String to escape
  940. *
  941. * @since 1.2.2
  942. * @return string
  943. * @access private
  944. */
  945. function _escapeBackreferences($str)
  946. {
  947. $str = str_replace('\\', '\\\\', $str);
  948. $str = preg_replace('@\$([0-9]{1,2})@', '\\\$${1}', $str);
  949. return $str;
  950. }
  951. /**
  952. * Replaces an opening delimiter by a special string
  953. *
  954. * @param string $str special string
  955. *
  956. * @return string
  957. * @access private
  958. */
  959. function _preserveOpeningDelimiter($str)
  960. {
  961. return (false === strpos($str, $this->openingDelimiter))?
  962. $str:
  963. str_replace($this->openingDelimiter,
  964. $this->openingDelimiter .
  965. '%preserved%' . $this->closingDelimiter,
  966. $str);
  967. }
  968. /**
  969. * Return a textual error message for a IT error code
  970. *
  971. * @param integer $value error code
  972. * @param string $blockname unknown
  973. *
  974. * @access private
  975. * @return string error message, or false if the error code was
  976. * not recognized
  977. */
  978. function errorMessage($value, $blockname = '')
  979. {
  980. static $errorMessages;
  981. if (!isset($errorMessages)) {
  982. $errorMessages = array(
  983. IT_OK => '',
  984. IT_ERROR => 'unknown error',
  985. IT_TPL_NOT_FOUND => 'Cannot read the template file',
  986. IT_BLOCK_NOT_FOUND => 'Cannot find this block',
  987. IT_BLOCK_DUPLICATE => 'The name of a block must be'.
  988. ' uniquewithin a template.'.
  989. ' Found "' . $blockname . '" twice.'.
  990. 'Unpredictable results '.
  991. 'may appear.',
  992. IT_UNKNOWN_OPTION => 'Unknown option'
  993. );
  994. }
  995. if (PEAR::isError($value)) {
  996. $value = $value->getCode();
  997. }
  998. return isset($errorMessages[$value]) ?
  999. $errorMessages[$value] : $errorMessages[IT_ERROR];
  1000. }
  1001. } // end class IntegratedTemplate
  1002. ?>