source: trunk/www.guidonia.net/wp/wp-content/plugins/tubepress/classes/net/php/pear/HTML/Template/ITX.class.php@ 44

Last change on this file since 44 was 44, checked in by luciano, 14 years ago
File size: 27.1 KB
Line 
1<?php
2//
3// +----------------------------------------------------------------------+
4// | Copyright (c) 1997-2005 Ulf Wendel, Pierre-Alain Joye |
5// +----------------------------------------------------------------------+
6// | This source file is subject to the New BSD license, That is bundled |
7// | with this package in the file LICENSE, and is available through |
8// | the world-wide-web at |
9// | http://www.opensource.org/licenses/bsd-license.php |
10// | If you did not receive a copy of the new BSD license and are unable |
11// | to obtain it through the world-wide-web, please send a note to |
12// | pajoye@php.net so we can mail you a copy immediately. |
13// +----------------------------------------------------------------------+
14// | Author: Ulf Wendel <ulf.wendel@phpdoc.de> |
15// | Pierre-Alain Joye <pajoye@php.net> |
16// +----------------------------------------------------------------------+
17//
18// $Id: ITX.php,v 1.16 2006/08/17 15:47:22 dsp Exp $
19//
20
21
22/**
23* Integrated Template Extension - ITX
24*
25* With this class you get the full power of the phplib template class.
26* You may have one file with blocks in it but you have as well one main file
27* and multiple files one for each block. This is quite usefull when you have
28* user configurable websites. Using blocks not in the main template allows
29* you to modify some parts of your layout easily.
30*
31* Note that you can replace an existing block and add new blocks at runtime.
32* Adding new blocks means changing a variable placeholder to a block.
33*
34* @author Ulf Wendel <uw@netuse.de>
35* @access public
36* @version $Id: ITX.php,v 1.16 2006/08/17 15:47:22 dsp Exp $
37* @package HTML_Template_IT
38*/
39class net_php_pear_HTML_Template_ITX extends net_php_pear_HTML_Template_IT
40{
41 /**
42 * Array with all warnings.
43 * @var array
44 * @access public
45 * @see $printWarning, $haltOnWarning, warning()
46 */
47 var $warn = array();
48
49 /**
50 * Print warnings?
51 * @var array
52 * @access public
53 * @see $haltOnWarning, $warn, warning()
54 */
55 var $printWarning = false;
56
57 /**
58 * Call die() on warning?
59 * @var boolean
60 * @access public
61 * @see $warn, $printWarning, warning()
62 */
63 var $haltOnWarning = false;
64
65 /**
66 * RegExp used to test for a valid blockname.
67 * @var string
68 */
69 var $checkblocknameRegExp = '';
70
71 /**
72 * Functionnameprefix used when searching function calls in the template.
73 * @var string
74 */
75 var $functionPrefix = 'func_';
76
77 /**
78 * Functionname RegExp.
79 * @var string
80 */
81 var $functionnameRegExp = '[_a-zA-Z]+[A-Za-z_0-9]*';
82
83 /**
84 * RegExp used to grep function calls in the template.
85 *
86 * The variable gets set by the constructor.
87 *
88 * @var string
89 * @see HTML_Template_IT()
90 */
91 var $functionRegExp = '';
92
93 /**
94 * List of functions found in the template.
95 *
96 * @var array
97 */
98 var $functions = array();
99
100 /**
101 * List of callback functions specified by the user.
102 *
103 * @var array
104 */
105 var $callback = array();
106
107 /**
108 * Builds some complex regexps and calls the constructor
109 * of the parent class.
110 *
111 * Make sure that you call this constructor if you derive your own
112 * template class from this one.
113 *
114 * @see HTML_Template_IT()
115 */
116 function net_php_pear_HTML_Template_ITX($root = '')
117 {
118
119 $this->checkblocknameRegExp = '@' . $this->blocknameRegExp . '@';
120 $this->functionRegExp = '@' . $this->functionPrefix . '(' .
121 $this->functionnameRegExp . ')\s*\(@sm';
122
123 $this->net_php_pear_HTML_Template_IT($root);
124 } // end func constructor
125
126 function init()
127 {
128 $this->free();
129 $this->buildFunctionlist();
130 $this->findBlocks($this->template);
131 // we don't need it any more
132 $this->template = '';
133 $this->buildBlockvariablelist();
134
135 } // end func init
136
137 /**
138 * Replaces an existing block with new content.
139 *
140 * This function will replace a block of the template and all blocks
141 * contained in the replaced block and add a new block insted, means
142 * you can dynamically change your template.
143 *
144 * Note that changing the template structure violates one of the IT[X]
145 * development goals. I've tried to write a simple to use template engine
146 * supporting blocks. In contrast to other systems IT[X] analyses the way
147 * you've nested blocks and knows which block belongs into another block.
148 * The nesting information helps to make the API short and simple. Replacing
149 * blocks does not only mean that IT[X] has to update the nesting
150 * information (relatively time consumpting task) but you have to make sure
151 * that you do not get confused due to the template change itself.
152 *
153 * @param string Blockname
154 * @param string Blockcontent
155 * @param boolean true if the new block inherits the content
156 * of the old block
157 * @return boolean
158 * @throws IT_Error
159 * @see replaceBlockfile(), addBlock(), addBlockfile()
160 * @access public
161 */
162 function replaceBlock($block, $template, $keep_content = false)
163 {
164 if (!isset($this->blocklist[$block])) {
165 return new net_php_pear_HTML_Template_IT_Error(
166 "The block "."'$block'".
167 " does not exist in the template and thus it can't be replaced.",
168 __FILE__, __LINE__
169 );
170 }
171
172 if ($template == '') {
173 return new net_php_pear_HTML_Template_IT_Error('No block content given.', __FILE__, __LINE__);
174 }
175
176 if ($keep_content) {
177 $blockdata = $this->blockdata[$block];
178 }
179
180 // remove all kinds of links to the block / data of the block
181 $this->removeBlockData($block);
182
183 $template = "<!-- BEGIN $block -->" . $template . "<!-- END $block -->";
184 $parents = $this->blockparents[$block];
185 $this->findBlocks($template);
186 $this->blockparents[$block] = $parents;
187
188 // KLUDGE: rebuild the list for all block - could be done faster
189 $this->buildBlockvariablelist();
190
191 if ($keep_content) {
192 $this->blockdata[$block] = $blockdata;
193 }
194
195 // old TODO - I'm not sure if we need this
196 // update caches
197
198 return true;
199 } // end func replaceBlock
200
201 /**
202 * Replaces an existing block with new content from a file.
203 *
204 * @brother replaceBlock()
205 * @param string Blockname
206 * @param string Name of the file that contains the blockcontent
207 * @param boolean true if the new block inherits the content of the old block
208 * @access public
209 */
210 function replaceBlockfile($block, $filename, $keep_content = false)
211 {
212 return $this->replaceBlock($block, $this->getFile($filename), $keep_content);
213 } // end func replaceBlockfile
214
215 /**
216 * Adds a block to the template changing a variable placeholder
217 * to a block placeholder.
218 *
219 * Add means "replace a variable placeholder by a new block".
220 * This is different to PHPLibs templates. The function loads a
221 * block, creates a handle for it and assigns it to a certain
222 * variable placeholder. To to the same with PHPLibs templates you would
223 * call set_file() to create the handle and parse() to assign the
224 * parsed block to a variable. By this PHPLibs templates assume
225 * that you tend to assign a block to more than one one placeholder.
226 * To assign a parsed block to more than only the placeholder you specify
227 * in this function you have to use a combination of getBlock()
228 * and setVariable().
229 *
230 * As no updates to cached data is necessary addBlock() and addBlockfile()
231 * are rather "cheap" meaning quick operations.
232 *
233 * The block content must not start with <!-- BEGIN blockname -->
234 * and end with <!-- END blockname --> this would cause overhead and
235 * produce an error.
236 *
237 * @param string Name of the variable placeholder, the name must be unique
238 * within the template.
239 * @param string Name of the block to be added
240 * @param string Content of the block
241 * @return boolean
242 * @throws IT_Error
243 * @see addBlockfile()
244 * @access public
245 */
246 function addBlock($placeholder, $blockname, $template)
247 {
248 // Don't trust any user even if it's a programmer or yourself...
249 if ($placeholder == '') {
250 return new net_php_pear_HTML_Template_IT_Error('No variable placeholder given.',
251 __FILE__, __LINE__
252 );
253 } elseif ($blockname == '' ||
254 !preg_match($this->checkblocknameRegExp, $blockname)
255 ) {
256 return new net_php_pear_HTML_Template_IT_Error("No or invalid blockname '$blockname' given.",
257 __FILE__, __LINE__
258 );
259 } elseif ($template == '') {
260 return new net_php_pear_HTML_Template_IT_Error('No block content given.', __FILE__, __LINE__);
261 } elseif (isset($this->blocklist[$blockname])) {
262 return new net_php_pear_HTML_Template_IT_Error('The block already exists.',
263 __FILE__, __LINE__
264 );
265 }
266
267 // find out where to insert the new block
268 $parents = $this->findPlaceholderBlocks($placeholder);
269 if (count($parents) == 0) {
270
271 return new net_php_pear_HTML_Template_IT_Error(
272 "The variable placeholder".
273 " '$placeholder' was not found in the template.",
274 __FILE__, __LINE__
275 );
276
277 } elseif (count($parents) > 1) {
278
279 reset($parents);
280 while (list($k, $parent) = each($parents)) {
281 $msg .= "$parent, ";
282 }
283 $msg = substr($parent, -2);
284
285 return new net_php_pear_HTML_Template_IT_Error("The variable placeholder "."'$placeholder'".
286 " must be unique, found in multiple blocks '$msg'.",
287 __FILE__, __LINE__
288 );
289 }
290
291 $template = "<!-- BEGIN $blockname -->" . $template . "<!-- END $blockname -->";
292 $this->findBlocks($template);
293 if ($this->flagBlocktrouble) {
294 return false; // findBlocks() already throws an exception
295 }
296 $this->blockinner[$parents[0]][] = $blockname;
297 $this->blocklist[$parents[0]] = preg_replace(
298 '@' . $this->openingDelimiter . $placeholder .
299 $this->closingDelimiter . '@',
300
301 $this->openingDelimiter . '__' . $blockname . '__' .
302 $this->closingDelimiter,
303
304 $this->blocklist[$parents[0]]
305 );
306
307 $this->deleteFromBlockvariablelist($parents[0], $placeholder);
308 $this->updateBlockvariablelist($blockname);
309
310 return true;
311 } // end func addBlock
312
313 /**
314 * Adds a block taken from a file to the template changing a variable
315 * placeholder to a block placeholder.
316 *
317 * @param string Name of the variable placeholder to be converted
318 * @param string Name of the block to be added
319 * @param string File that contains the block
320 * @brother addBlock()
321 * @access public
322 */
323 function addBlockfile($placeholder, $blockname, $filename)
324 {
325 return $this->addBlock($placeholder, $blockname, $this->getFile($filename));
326 } // end func addBlockfile
327
328 /**
329 * Returns the name of the (first) block that contains
330 * the specified placeholder.
331 *
332 * @param string Name of the placeholder you're searching
333 * @param string Name of the block to scan. If left out (default)
334 * all blocks are scanned.
335 * @return string Name of the (first) block that contains
336 * the specified placeholder.
337 * If the placeholder was not found or an error occured
338 * an empty string is returned.
339 * @throws IT_Error
340 * @access public
341 */
342 function placeholderExists($placeholder, $block = '')
343 {
344 if ($placeholder == '') {
345 new net_php_pear_HTML_Template_IT_Error('No placeholder name given.', __FILE__, __LINE__);
346 return '';
347 }
348
349 if ($block != '' && !isset($this->blocklist[$block])) {
350 new net_php_pear_HTML_Template_IT_Error("Unknown block '$block'.", __FILE__, __LINE__);
351 return '';
352 }
353
354 // name of the block where the given placeholder was found
355 $found = '';
356
357 if ($block != '') {
358 if (is_array($variables = $this->blockvariables[$block])) {
359 // search the value in the list of blockvariables
360 reset($variables);
361 while (list($k, $variable) = each($variables)) {
362 if ($k == $placeholder) {
363 $found = $block;
364 break;
365 }
366 }
367 }
368 } else {
369
370 // search all blocks and return the name of the first block that
371 // contains the placeholder
372 reset($this->blockvariables);
373 while (list($blockname, $variables) = each($this->blockvariables)){
374 if (is_array($variables) && isset($variables[$placeholder])) {
375 $found = $blockname;
376 break;
377 }
378 }
379 }
380
381 return $found;
382 } // end func placeholderExists
383
384 /**
385 * Checks the list of function calls in the template and
386 * calls their callback function.
387 *
388 * @access public
389 */
390 function performCallback()
391 {
392 reset($this->functions);
393 while (list($func_id, $function) = each($this->functions)) {
394 if (isset($this->callback[$function['name']])) {
395 if ($this->callback[$function['name']]['expandParameters']) {
396 $callFunction = 'call_user_func_array';
397 } else {
398 $callFunction = 'call_user_func';
399 }
400
401 if ($this->callback[$function['name']]['object'] != '') {
402 $call =
403 $callFunction(
404 array(
405 &$GLOBALS[$this->callback[$function['name']]['object']],
406 $this->callback[$function['name']]['function']),
407 $function['args']
408 );
409
410 } else {
411 $call =
412 $callFunction(
413 $this->callback[$function['name']]['function'],
414 $function['args']
415 );
416 }
417 $this->variableCache['__function' . $func_id . '__'] = $call;
418 }
419 }
420
421 } // end func performCallback
422
423 /**
424 * Returns a list of all function calls in the current template.
425 *
426 * @return array
427 * @access public
428 */
429 function getFunctioncalls()
430 {
431 return $this->functions;
432 } // end func getFunctioncalls
433
434 /**
435 * Replaces a function call with the given replacement.
436 *
437 * @param int Function ID
438 * @param string Replacement
439 * @deprecated
440 */
441 function setFunctioncontent($functionID, $replacement)
442 {
443 $this->variableCache['__function' . $functionID . '__'] = $replacement;
444 } // end func setFunctioncontent
445
446 /**
447 * Sets a callback function.
448 *
449 * IT[X] templates (note the X) can contain simple function calls.
450 * "function call" means that the editor of the template can add
451 * special placeholder to the template like 'func_h1("embedded in h1")'.
452 * IT[X] will grab this function calls and allow you to define a callback
453 * function for them.
454 *
455 * This is an absolutely evil feature. If your application makes heavy
456 * use of such callbacks and you're even implementing if-then etc. on
457 * the level of a template engine you're reiventing the wheel... - that's
458 * actually how PHP came into life. Anyway, sometimes it's handy.
459 *
460 * Consider also using XML/XSLT or native PHP. And please do not push
461 * IT[X] any further into this direction of adding logics to the template
462 * engine.
463 *
464 * For those of you ready for the X in IT[X]:
465 *
466 * <?php
467 * ...
468 * function h_one($args) {
469 * return sprintf('<h1>%s</h1>', $args[0]);
470 * }
471 *
472 * ...
473 * $itx = new HTML_Template_ITX( ... );
474 * ...
475 * $itx->setCallbackFunction('h1', 'h_one');
476 * $itx->performCallback();
477 * ?>
478 *
479 * template:
480 * func_h1('H1 Headline');
481 *
482 * @param string Function name in the template
483 * @param string Name of the callback function
484 * @param string Name of the callback object
485 * @param boolean If the callback is called with a list of parameters or
486 * with an array holding the parameters
487 * @return boolean False on failure.
488 * @throws IT_Error
489 * @access public
490 * @deprecated The $callbackobject parameter is depricated since
491 * version 1.2 and might be dropped in further versions.
492 */
493 function
494 setCallbackFunction($tplfunction, $callbackfunction, $callbackobject = '', $expandCallbackParameters=false)
495 {
496 if ($tplfunction == '' || $callbackfunction == '') {
497 return new net_php_pear_HTML_Template_IT_Error(
498 "No template function "."('$tplfunction')".
499 " and/or no callback function ('$callback') given.",
500 __FILE__, __LINE__
501 );
502 }
503 $this->callback[$tplfunction] = array(
504 'function' => $callbackfunction,
505 'object' => $callbackobject,
506 'expandParameters' => (boolean) $expandCallbackParameters
507 );
508
509 return true;
510 } // end func setCallbackFunction
511
512 /**
513 * Sets the Callback function lookup table
514 *
515 * @param array function table
516 * array[templatefunction] =
517 * array(
518 * "function" => userfunction,
519 * "object" => userobject
520 * )
521 * @access public
522 */
523 function setCallbackFuntiontable($functions)
524 {
525 $this->callback = $functions;
526 } // end func setCallbackFunctiontable
527
528 /**
529 * Recursively removes all data assiciated with a block, including all inner blocks
530 *
531 * @param string block to be removed
532 * @access private
533 */
534 function removeBlockData($block)
535 {
536 if (isset($this->blockinner[$block])) {
537 foreach ($this->blockinner[$block] as $k => $inner) {
538 $this->removeBlockData($inner);
539 }
540
541 unset($this->blockinner[$block]);
542 }
543
544 unset($this->blocklist[$block]);
545 unset($this->blockdata[$block]);
546 unset($this->blockvariables[$block]);
547 unset($this->touchedBlocks[$block]);
548
549 } // end func removeBlockinner
550
551 /**
552 * Returns a list of blocknames in the template.
553 *
554 * @return array [blockname => blockname]
555 * @access public
556 * @see blockExists()
557 */
558 function getBlocklist()
559 {
560 $blocklist = array();
561 foreach ($this->blocklist as $block => $content) {
562 $blocklist[$block] = $block;
563 }
564
565 return $blocklist;
566 } // end func getBlocklist
567
568 /**
569 * Checks wheter a block exists.
570 *
571 * @param string
572 * @return boolean
573 * @access public
574 * @see getBlocklist()
575 */
576 function blockExists($blockname)
577 {
578 return isset($this->blocklist[$blockname]);
579 } // end func blockExists
580
581 /**
582 * Returns a list of variables of a block.
583 *
584 * @param string Blockname
585 * @return array [varname => varname]
586 * @access public
587 * @see BlockvariableExists()
588 */
589 function getBlockvariables($block)
590 {
591 if (!isset($this->blockvariables[$block])) {
592 return array();
593 }
594
595 $variables = array();
596 foreach ($this->blockvariables[$block] as $variable => $v) {
597 $variables[$variable] = $variable;
598 }
599
600 return $variables;
601 } // end func getBlockvariables
602
603 /**
604 * Checks wheter a block variable exists.
605 *
606 * @param string Blockname
607 * @param string Variablename
608 * @return boolean
609 * @access public
610 * @see getBlockvariables()
611 */
612 function BlockvariableExists($block, $variable)
613 {
614 return isset($this->blockvariables[$block][$variable]);
615 } // end func BlockvariableExists
616
617 /**
618 * Builds a functionlist from the template.
619 * @access private
620 */
621 function buildFunctionlist()
622 {
623 $this->functions = array();
624
625 $template = $this->template;
626 $num = 0;
627
628 while (preg_match($this->functionRegExp, $template, $regs)) {
629
630 $pos = strpos($template, $regs[0]);
631 $template = substr($template, $pos + strlen($regs[0]));
632
633 $head = $this->getValue($template, ')');
634 $args = array();
635
636 $search = $regs[0] . $head . ')';
637
638 $replace = $this->openingDelimiter .
639 '__function' . $num . '__' .
640 $this->closingDelimiter;
641
642 $this->template = str_replace($search, $replace, $this->template);
643 $template = str_replace($search, $replace, $template);
644
645 while ($head != '' && $args2 = $this->getValue($head, ',')) {
646 $arg2 = trim($args2);
647 $args[] = ('"' == $arg2{0} || "'" == $arg2{0}) ?
648 substr($arg2, 1, -1) : $arg2;
649 if ($arg2 == $head) {
650 break;
651 }
652 $head = substr($head, strlen($arg2) + 1);
653 }
654
655 $this->functions[$num++] = array(
656 'name' => $regs[1],
657 'args' => $args
658 );
659 }
660
661 } // end func buildFunctionlist
662
663 /**
664 * Truncates the given code from the first occurence of
665 * $delimiter but ignores $delimiter enclosed by " or '.
666 *
667 * @access private
668 * @param string The code which should be parsed
669 * @param string The delimiter char
670 * @return string
671 * @see buildFunctionList()
672 */
673 function getValue($code, $delimiter) {
674 if ($code == '') {
675 return '';
676 }
677
678 if (!is_array($delimiter)) {
679 $delimiter = array( $delimiter => true );
680 }
681
682 $len = strlen($code);
683 $enclosed = false;
684 $enclosed_by = '';
685
686 if (isset($delimiter[$code[0]])) {
687 $i = 1;
688 } else {
689 for ($i = 0; $i < $len; ++$i) {
690 $char = $code[$i];
691
692 if (
693 ($char == '"' || $char == "'") &&
694 ($char == $enclosed_by || '' == $enclosed_by) &&
695 (0 == $i || ($i > 0 && '\\' != $code[$i - 1]))
696 ) {
697
698 if (!$enclosed) {
699 $enclosed_by = $char;
700 } else {
701 $enclosed_by = "";
702 }
703 $enclosed = !$enclosed;
704
705 }
706
707 if (!$enclosed && isset($delimiter[$char])) {
708 break;
709 }
710 }
711 }
712
713 return substr($code, 0, $i);
714 } // end func getValue
715
716 /**
717 * Deletes one or many variables from the block variable list.
718 *
719 * @param string Blockname
720 * @param mixed Name of one variable or array of variables
721 * ( array ( name => true ) ) to be stripped.
722 * @access private
723 */
724 function deleteFromBlockvariablelist($block, $variables)
725 {
726 if (!is_array($variables)) {
727 $variables = array($variables => true);
728 }
729
730 reset($this->blockvariables[$block]);
731 while (list($varname, $val) = each($this->blockvariables[$block])) {
732 if (isset($variables[$varname])) {
733 unset($this->blockvariables[$block][$varname]);
734 }
735 }
736 } // end deleteFromBlockvariablelist
737
738 /**
739 * Updates the variable list of a block.
740 *
741 * @param string Blockname
742 * @access private
743 */
744 function updateBlockvariablelist($block)
745 {
746 preg_match_all( $this->variablesRegExp,
747 $this->blocklist[$block], $regs
748 );
749
750 if (count($regs[1]) != 0) {
751 foreach ($regs[1] as $k => $var) {
752 $this->blockvariables[$block][$var] = true;
753 }
754 } else {
755 $this->blockvariables[$block] = array();
756 }
757
758 // check if any inner blocks were found
759 if (isset($this->blockinner[$block]) &&
760 is_array($this->blockinner[$block]) &&
761 count($this->blockinner[$block]) > 0
762 ) {
763 /*
764 * loop through inner blocks, registering the variable
765 * placeholders in each
766 */
767 foreach ($this->blockinner[$block] as $childBlock) {
768 $this->updateBlockvariablelist($childBlock);
769 }
770 }
771 } // end func updateBlockvariablelist
772
773 /**
774 * Returns an array of blocknames where the given variable
775 * placeholder is used.
776 *
777 * @param string Variable placeholder
778 * @return array $parents parents[0..n] = blockname
779 * @access public
780 */
781 function findPlaceholderBlocks($variable)
782 {
783 $parents = array();
784 reset($this->blocklist);
785 while (list($blockname, $content) = each($this->blocklist)) {
786 reset($this->blockvariables[$blockname]);
787 while (
788 list($varname, $val) = each($this->blockvariables[$blockname]))
789 {
790 if ($variable == $varname) {
791 $parents[] = $blockname;
792 }
793 }
794 }
795
796 return $parents;
797 } // end func findPlaceholderBlocks
798
799 /**
800 * Handles warnings, saves them to $warn and prints them or
801 * calls die() depending on the flags
802 *
803 * @param string Warning
804 * @param string File where the warning occured
805 * @param int Linenumber where the warning occured
806 * @see $warn, $printWarning, $haltOnWarning
807 * @access private
808 */
809 function warning($message, $file = '', $line = 0)
810 {
811 $message = sprintf(
812 'HTML_Template_ITX Warning: %s [File: %s, Line: %d]',
813 $message,
814 $file,
815 $line
816 );
817
818 $this->warn[] = $message;
819
820 if ($this->printWarning) {
821 print $message;
822 }
823
824 if ($this->haltOnWarning) {
825 die($message);
826 }
827 } // end func warning
828
829} // end class HTML_Template_ITX
830?>
Note: See TracBrowser for help on using the repository browser.