PageRenderTime 37ms CodeModel.GetById 13ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 1ms

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