PageRenderTime 64ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/php-pear-HTML-Template-IT-1.3.0/HTML_Template_IT-1.3.0/HTML/Template/IT.php

#
PHP | 1186 lines | 511 code | 118 blank | 557 comment | 69 complexity | 886a6044e8186e3b1e1d8c5b004246af 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 295605 2010-02-28 22:48:07Z gregorycu $
  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. switch ($option) {
  406. case 'removeEmptyBlocks':
  407. $this->removeEmptyBlocks = $value;
  408. return IT_OK;
  409. }
  410. if (array_key_exists($option, $this->_options)) {
  411. $this->_options[$option] = $value;
  412. return IT_OK;
  413. }
  414. return PEAR::raiseError(
  415. $this->errorMessage(IT_UNKNOWN_OPTION) . ": '{$option}'",
  416. IT_UNKNOWN_OPTION
  417. );
  418. }
  419. /**
  420. * Sets the options for the template class
  421. *
  422. * @param string[] $options options array of options
  423. * default value:
  424. * 'preserve_data' => false,
  425. * 'use_preg' => true
  426. *
  427. * @access public
  428. * @return mixed IT_OK on success, error object on failure
  429. * @see $options
  430. */
  431. function setOptions($options)
  432. {
  433. if (is_array($options)) {
  434. foreach ($options as $option => $value) {
  435. $error = $this->setOption($option, $value);
  436. if (PEAR::isError($error)) {
  437. return $error;
  438. }
  439. }
  440. }
  441. return IT_OK;
  442. }
  443. /**
  444. * Print a certain block with all replacements done.
  445. *
  446. * @param string $block block
  447. *
  448. * @brother get()
  449. * @access public
  450. * @return null
  451. */
  452. function show($block = '__global__')
  453. {
  454. print $this->get($block);
  455. } // end func show
  456. /**
  457. * Returns a block with all replacements done.
  458. *
  459. * @param string $block name of the block
  460. *
  461. * @return string
  462. * @throws PEAR_Error
  463. * @access public
  464. * @see show()
  465. */
  466. function get($block = '__global__')
  467. {
  468. if ($block == '__global__' && !$this->flagGlobalParsed) {
  469. $this->parse('__global__');
  470. }
  471. if (!isset($this->blocklist[$block])) {
  472. $this->err[] = PEAR::raiseError(
  473. $this->errorMessage(IT_BLOCK_NOT_FOUND) . '"' . $block . "'",
  474. IT_BLOCK_NOT_FOUND
  475. );
  476. return '';
  477. }
  478. if (isset($this->blockdata[$block])) {
  479. $ret = $this->blockdata[$block];
  480. if ($this->clearCache) {
  481. unset($this->blockdata[$block]);
  482. if ($block == '__global__') {
  483. $this->flagGlobalParsed = false;
  484. }
  485. }
  486. if ($this->_options['preserve_data']) {
  487. $ret = str_replace(
  488. $this->openingDelimiter .
  489. '%preserved%' . $this->closingDelimiter,
  490. $this->openingDelimiter,
  491. $ret
  492. );
  493. }
  494. return $ret;
  495. }
  496. return '';
  497. } // end func get()
  498. /**
  499. * Parses the given block.
  500. *
  501. * @param string $block name of the block to be parsed
  502. * @param bool $flag_recursion unknown
  503. *
  504. * @access public
  505. * @see parseCurrentBlock()
  506. * @throws PEAR_Error
  507. * @return null
  508. */
  509. function parse($block = '__global__', $flag_recursion = false)
  510. {
  511. static $regs, $values;
  512. if (!isset($this->blocklist[$block])) {
  513. return PEAR::raiseError(
  514. $this->errorMessage(IT_BLOCK_NOT_FOUND) . '"' . $block . "'",
  515. IT_BLOCK_NOT_FOUND
  516. );
  517. }
  518. if ($block == '__global__') {
  519. $this->flagGlobalParsed = true;
  520. }
  521. if (!$flag_recursion) {
  522. $regs = array();
  523. $values = array();
  524. }
  525. $outer = $this->blocklist[$block];
  526. $empty = true;
  527. $variablelist = array();
  528. if ($this->clearCacheOnParse) {
  529. foreach ($this->variableCache as $name => $value) {
  530. $regs[] = $this->openingDelimiter .
  531. $name . $this->closingDelimiter;
  532. $values[] = $value;
  533. $empty = false;
  534. $variablelist[] = $name;
  535. }
  536. $this->variableCache = array();
  537. } else {
  538. foreach ($this->blockvariables[$block] as $allowedvar => $v) {
  539. if (isset($this->variableCache[$allowedvar])) {
  540. $regs[] = $this->openingDelimiter .
  541. $allowedvar . $this->closingDelimiter;
  542. $values[] = $this->variableCache[$allowedvar];
  543. unset($this->variableCache[$allowedvar]);
  544. $empty = false;
  545. $variablelist[] = $allowedvar;
  546. }
  547. }
  548. }
  549. if (isset($this->blockinner[$block])) {
  550. foreach ($this->blockinner[$block] as $k => $innerblock) {
  551. $this->parse($innerblock, true);
  552. if ($this->blockdata[$innerblock] != '') {
  553. $empty = false;
  554. }
  555. $placeholder = $this->openingDelimiter . "__" .
  556. $innerblock . "__" . $this->closingDelimiter;
  557. $outer = str_replace(
  558. $placeholder,
  559. $this->blockdata[$innerblock], $outer
  560. );
  561. $this->blockdata[$innerblock] = "";
  562. }
  563. }
  564. if (!$flag_recursion && 0 != count($values)) {
  565. if ($this->_options['use_preg']) {
  566. $regs = array_map(array(&$this, '_addPregDelimiters'), $regs);
  567. $values = array_map(array(&$this, '_escapeBackreferences'), $values);
  568. $funcReplace = 'preg_replace';
  569. } else {
  570. $funcReplace = 'str_replace';
  571. }
  572. if ($this->_options['preserve_data']) {
  573. $values = array_map(
  574. array(&$this, '_preserveOpeningDelimiter'),
  575. $values
  576. );
  577. }
  578. $outer = $funcReplace($regs, $values, $outer);
  579. }
  580. if ($this->removeUnknownVariables) {
  581. $outer = $this->removeUnknownVariablesFromBlock(
  582. $block,
  583. $outer,
  584. $variablelist
  585. );
  586. }
  587. if ($empty) {
  588. if (!$this->removeEmptyBlocks) {
  589. $this->blockdata[$block ] .= $outer;
  590. } else {
  591. if (isset($this->touchedBlocks[$block])) {
  592. $this->blockdata[$block] .= $outer;
  593. unset($this->touchedBlocks[$block]);
  594. }
  595. }
  596. } else {
  597. if (empty($this->blockdata[$block])) {
  598. $this->blockdata[$block] = $outer;
  599. } else {
  600. $this->blockdata[$block] .= $outer;
  601. }
  602. }
  603. return $empty;
  604. } // end func parse
  605. /**
  606. * Removes unknown variables from block. If preserve_input is set to true
  607. * only unknown variables that were present during setTemplate or
  608. * loadTemplatefile are removed. Thus you can set a variable to
  609. * "{SOMEINPUTDATA}" which is preserved.
  610. *
  611. * @param string $blockname block
  612. * @param string $blockinner unknown
  613. * @param string $variableList unknown
  614. *
  615. * @see parse()
  616. * @access private
  617. * @return null
  618. */
  619. function removeUnknownVariablesFromBlock ($blockname, $blockinner, $variableList)
  620. {
  621. if ($this->_options['preserve_input']) {
  622. foreach ($this->blockvariables[$blockname] as $var => $setted) {
  623. if (!in_array($var, $variableList)) {
  624. $blockinner = str_replace(
  625. $this->openingDelimiter . $var . $this->closingDelimiter,
  626. '',
  627. $blockinner
  628. );
  629. }
  630. }
  631. } else {
  632. $blockinner = preg_replace(
  633. $this->removeVariablesRegExp,
  634. '',
  635. $blockinner
  636. );
  637. }
  638. return $blockinner;
  639. }
  640. /**
  641. * Parses the current block
  642. *
  643. * @see parse(), setCurrentBlock(), $currentBlock
  644. * @access public
  645. * @return null
  646. */
  647. function parseCurrentBlock()
  648. {
  649. return $this->parse($this->currentBlock);
  650. } // end func parseCurrentBlock
  651. /**
  652. * Checks to see if a placeholder exists within a block (and its children)
  653. *
  654. * @access public
  655. * @return bool
  656. */
  657. function checkPlaceholderExists($blockname, $placeholder) {
  658. if (isset($this->blockvariables[$blockname][$placeholder])) {
  659. return true;
  660. }
  661. if (isset($this->blockinner[$blockname])) {
  662. foreach ($this->blockinner[$blockname] as $block) {
  663. if ($this->checkPlaceholderExists($block, $placeholder)) {
  664. return true;
  665. }
  666. }
  667. }
  668. return false;
  669. } // end func checkPlaceholderExists
  670. /**
  671. * Sets a variable value.
  672. *
  673. * The function can be used eighter like setVariable( "varname", "value")
  674. * or with one array $variables["varname"] = "value"
  675. * given setVariable($variables) quite like phplib templates set_var().
  676. *
  677. * @param mixed $variable string with the variable name or an array
  678. * %variables["varname"] = "value"
  679. * @param string $value value of the variable or empty if $variable
  680. * is an array.
  681. *
  682. * @access public
  683. * @return null
  684. */
  685. function setVariable($variable, $value = '')
  686. {
  687. if (is_array($variable)) {
  688. foreach ($variable as $key => $value) {
  689. $this->setVariable($key, $value);
  690. }
  691. } else {
  692. if ($this->checkPlaceholderExists($this->currentBlock, $variable)) {
  693. $this->variableCache[$variable] = $value;
  694. }
  695. }
  696. } // end func setVariable
  697. /**
  698. * Sets the name of the current block that is the block where variables
  699. * are added.
  700. *
  701. * @param string $block name of the block
  702. *
  703. * @return boolean false on failure, otherwise true
  704. * @throws PEAR_Error
  705. * @access public
  706. */
  707. function setCurrentBlock($block = '__global__')
  708. {
  709. if (!isset($this->blocklist[$block])) {
  710. return PEAR::raiseError(
  711. $this->errorMessage(IT_BLOCK_NOT_FOUND)
  712. . '"' . $block . "'",
  713. IT_BLOCK_NOT_FOUND
  714. );
  715. }
  716. $this->currentBlock = $block;
  717. return true;
  718. } // end func setCurrentBlock
  719. /**
  720. * Preserves an empty block even if removeEmptyBlocks is true.
  721. *
  722. * @param string $block name of the block
  723. *
  724. * @return boolean false on false, otherwise true
  725. * @throws PEAR_Error
  726. * @access public
  727. * @see $removeEmptyBlocks
  728. */
  729. function touchBlock($block)
  730. {
  731. if (!isset($this->blocklist[$block])) {
  732. return PEAR::raiseError(
  733. $this->errorMessage(IT_BLOCK_NOT_FOUND) . '"' . $block . "'",
  734. IT_BLOCK_NOT_FOUND
  735. );
  736. }
  737. $this->touchedBlocks[$block] = true;
  738. return true;
  739. } // end func touchBlock
  740. /**
  741. * Clears all datafields of the object and rebuild the internal blocklist
  742. *
  743. * LoadTemplatefile() and setTemplate() automatically call this function
  744. * when a new template is given. Don't use this function
  745. * unless you know what you're doing.
  746. *
  747. * @access private
  748. * @see free()
  749. * @return null
  750. */
  751. function init()
  752. {
  753. $this->free();
  754. $this->findBlocks($this->template);
  755. // we don't need it any more
  756. $this->template = '';
  757. $this->buildBlockvariablelist();
  758. } // end func init
  759. /**
  760. * Clears all datafields of the object.
  761. *
  762. * Don't use this function unless you know what you're doing.
  763. *
  764. * @access private
  765. * @see init()
  766. * @return null
  767. */
  768. function free()
  769. {
  770. $this->err = array();
  771. $this->currentBlock = '__global__';
  772. $this->variableCache = array();
  773. $this->blocklist = array();
  774. $this->touchedBlocks = array();
  775. $this->flagBlocktrouble = false;
  776. $this->flagGlobalParsed = false;
  777. } // end func free
  778. /**
  779. * Sets the template.
  780. *
  781. * You can eighter load a template file from disk with
  782. * LoadTemplatefile() or set the template manually using this function.
  783. *
  784. * @param string $template template content
  785. * @param bool $removeUnknownVariables how to handle unknown variables.
  786. * @param bool $removeEmptyBlocks how to handle empty blocks.
  787. *
  788. * @see LoadTemplatefile(), $template
  789. * @access public
  790. * @return boolean
  791. */
  792. function setTemplate( $template,
  793. $removeUnknownVariables = true,
  794. $removeEmptyBlocks = true) {
  795. $this->removeUnknownVariables = $removeUnknownVariables;
  796. $this->removeEmptyBlocks = $removeEmptyBlocks;
  797. if ($template == '' && $this->flagCacheTemplatefile) {
  798. $this->variableCache = array();
  799. $this->blockdata = array();
  800. $this->touchedBlocks = array();
  801. $this->currentBlock = '__global__';
  802. } else {
  803. $this->template = '<!-- BEGIN __global__ -->' . $template .
  804. '<!-- END __global__ -->';
  805. $this->init();
  806. }
  807. if ($this->flagBlocktrouble) {
  808. return false;
  809. }
  810. return true;
  811. } // end func setTemplate
  812. /**
  813. * Reads a template file from the disk.
  814. *
  815. * @param string $filename name of the template file
  816. * @param bool $removeUnknownVariables how to handle unknown variables.
  817. * @param bool $removeEmptyBlocks how to handle empty blocks.
  818. *
  819. * @access public
  820. * @return boolean false on failure, otherwise true
  821. * @see $template, setTemplate(), $removeUnknownVariables,
  822. * $removeEmptyBlocks
  823. */
  824. function loadTemplatefile( $filename,
  825. $removeUnknownVariables = true,
  826. $removeEmptyBlocks = true ) {;
  827. $template = '';
  828. if (!$this->flagCacheTemplatefile
  829. || $this->lastTemplatefile != $filename
  830. ) {
  831. $template = $this->getFile($filename);
  832. }
  833. $this->lastTemplatefile = $filename;
  834. return $template != '' ?
  835. $this->setTemplate(
  836. $template,
  837. $removeUnknownVariables,
  838. $removeEmptyBlocks) : false;
  839. } // end func LoadTemplatefile
  840. /**
  841. * Sets the file root. The file root gets prefixed to all filenames passed
  842. * to the object.
  843. *
  844. * Make sure that you override this function when using the class
  845. * on windows.
  846. *
  847. * @param string $root File root
  848. *
  849. * @see HTML_Template_IT()
  850. * @access public
  851. * @return null
  852. */
  853. function setRoot($root)
  854. {
  855. if ($root != '' && substr($root, -1) != '/') {
  856. $root .= '/';
  857. }
  858. $this->fileRoot = $root;
  859. } // end func setRoot
  860. /**
  861. * Build a list of all variables within of a block
  862. *
  863. * @access private
  864. * @return null
  865. */
  866. function buildBlockvariablelist()
  867. {
  868. foreach ($this->blocklist as $name => $content) {
  869. preg_match_all($this->variablesRegExp, $content, $regs);
  870. if (count($regs[1]) != 0) {
  871. foreach ($regs[1] as $k => $var) {
  872. $this->blockvariables[$name][$var] = true;
  873. }
  874. } else {
  875. $this->blockvariables[$name] = array();
  876. }
  877. }
  878. } // end func buildBlockvariablelist
  879. /**
  880. * Returns a list of all global variables
  881. *
  882. * @access public
  883. * @return array
  884. */
  885. function getGlobalvariables()
  886. {
  887. $regs = array();
  888. $values = array();
  889. foreach ($this->blockvariables['__global__'] as $allowedvar => $v) {
  890. if (isset($this->variableCache[$allowedvar])) {
  891. $regs[] = '@' . $this->openingDelimiter .
  892. $allowedvar . $this->closingDelimiter . '@';
  893. $values[] = $this->variableCache[$allowedvar];
  894. unset($this->variableCache[$allowedvar]);
  895. }
  896. }
  897. return array($regs, $values);
  898. } // end func getGlobalvariables
  899. /**
  900. * Recusively builds a list of all blocks within the template.
  901. *
  902. * @param string $string string that gets scanned
  903. *
  904. * @access private
  905. * @see $blocklist
  906. * @return array
  907. */
  908. function findBlocks($string)
  909. {
  910. $blocklist = array();
  911. if (preg_match_all($this->blockRegExp, $string, $regs, PREG_SET_ORDER)) {
  912. foreach ($regs as $k => $match) {
  913. $blockname = $match[1];
  914. $blockcontent = $match[2];
  915. if (isset($this->blocklist[$blockname])) {
  916. $msg = $this->errorMessage(IT_BLOCK_DUPLICATE, $blockname);
  917. $this->err[] = PEAR::raiseError($msg, IT_BLOCK_DUPLICATE);
  918. $this->flagBlocktrouble = true;
  919. }
  920. $this->blocklist[$blockname] = $blockcontent;
  921. $this->blockdata[$blockname] = "";
  922. $blocklist[] = $blockname;
  923. $inner = $this->findBlocks($blockcontent);
  924. $regex = '@<!--\s+BEGIN\s+%s\s+-->(.*)<!--\s+END\s+%s\s+-->@sm';
  925. foreach ($inner as $k => $name) {
  926. $pattern = sprintf($regex, preg_quote($name), preg_quote($name));
  927. $this->blocklist[$blockname] = preg_replace(
  928. $pattern,
  929. $this->openingDelimiter .
  930. '__' . $name . '__' .
  931. $this->closingDelimiter,
  932. $this->blocklist[$blockname]
  933. );
  934. $this->blockinner[$blockname][] = $name;
  935. $this->blockparents[$name] = $blockname;
  936. }
  937. }
  938. }
  939. return $blocklist;
  940. } // end func findBlocks
  941. /**
  942. * Reads a file from disk and returns its content.
  943. *
  944. * @param string $filename Filename
  945. *
  946. * @return string Filecontent
  947. * @access private
  948. */
  949. function getFile($filename)
  950. {
  951. if ($filename{0} == '/' && substr($this->fileRoot, -1) == '/') {
  952. $filename = substr($filename, 1);
  953. }
  954. $filename = $this->fileRoot . $filename;
  955. if (!($fh = @fopen($filename, 'r'))) {
  956. $this->err[] = PEAR::raiseError(
  957. $this->errorMessage(IT_TPL_NOT_FOUND) . ': "' .$filename .'"',
  958. IT_TPL_NOT_FOUND
  959. );
  960. return "";
  961. }
  962. $fsize = filesize($filename);
  963. if ($fsize < 1) {
  964. fclose($fh);
  965. return '';
  966. }
  967. $content = fread($fh, $fsize);
  968. fclose($fh);
  969. return preg_replace(
  970. "#<!-- INCLUDE (.*) -->#ime",
  971. "\$this->getFile('\\1')",
  972. $content
  973. );
  974. } // end func getFile
  975. /**
  976. * Adds delimiters to a string, so it can be used as a pattern
  977. * in preg_* functions
  978. *
  979. * @param string $str input
  980. *
  981. * @return string
  982. * @access private
  983. */
  984. function _addPregDelimiters($str)
  985. {
  986. return '@' . preg_quote($str) . '@';
  987. }
  988. /**
  989. * Escapes $ and \ as preg_replace will treat
  990. * them as a backreference and not literal.
  991. * See bug #9501
  992. *
  993. * @param string $str String to escape
  994. *
  995. * @since 1.2.2
  996. * @return string
  997. * @access private
  998. */
  999. function _escapeBackreferences($str)
  1000. {
  1001. $str = str_replace('\\', '\\\\', $str);
  1002. $str = preg_replace('@\$([0-9]{1,2})@', '\\\$${1}', $str);
  1003. return $str;
  1004. }
  1005. /**
  1006. * Replaces an opening delimiter by a special string
  1007. *
  1008. * @param string $str special string
  1009. *
  1010. * @return string
  1011. * @access private
  1012. */
  1013. function _preserveOpeningDelimiter($str)
  1014. {
  1015. return (false === strpos($str, $this->openingDelimiter))?
  1016. $str:
  1017. str_replace(
  1018. $this->openingDelimiter,
  1019. $this->openingDelimiter .
  1020. '%preserved%' . $this->closingDelimiter,
  1021. $str
  1022. );
  1023. }
  1024. /**
  1025. * Return a textual error message for a IT error code
  1026. *
  1027. * @param integer $value error code
  1028. * @param string $blockname unknown
  1029. *
  1030. * @access private
  1031. * @return string error message, or false if the error code was
  1032. * not recognized
  1033. */
  1034. function errorMessage($value, $blockname = '')
  1035. {
  1036. static $errorMessages;
  1037. if (!isset($errorMessages)) {
  1038. $errorMessages = array(
  1039. IT_OK => '',
  1040. IT_ERROR => 'unknown error',
  1041. IT_TPL_NOT_FOUND => 'Cannot read the template file',
  1042. IT_BLOCK_NOT_FOUND => 'Cannot find this block',
  1043. IT_BLOCK_DUPLICATE => 'The name of a block must be'.
  1044. ' uniquewithin a template.'.
  1045. ' Found "' . $blockname . '" twice.'.
  1046. 'Unpredictable results '.
  1047. 'may appear.',
  1048. IT_UNKNOWN_OPTION => 'Unknown option'
  1049. );
  1050. }
  1051. if (PEAR::isError($value)) {
  1052. $value = $value->getCode();
  1053. }
  1054. return isset($errorMessages[$value]) ?
  1055. $errorMessages[$value] : $errorMessages[IT_ERROR];
  1056. }
  1057. } // end class IntegratedTemplate
  1058. ?>