Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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
 
31
require_once 'PEAR.php';
32
 
33
define('IT_OK', 1);
34
define('IT_ERROR', -1);
35
define('IT_TPL_NOT_FOUND', -2);
36
define('IT_BLOCK_NOT_FOUND', -3);
37
define('IT_BLOCK_DUPLICATE', -4);
38
define('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
 */
138
class 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
?>