| [ Index ] |
PHP Cross Reference of Joomla 1.5.26 DE |
[Summary view] [Print] [Text view]
1 <?PHP 2 /** 3 * Base class for patTemplate readers 4 * 5 * $Id: Reader.php 10381 2008-06-01 03:35:53Z pasamio $ 6 * 7 * This class is able to parse patTemplate tags from any string you hand it over 8 * It will emulate some kind of SAX parsing by calling start-, end- and CData-handlers. 9 * 10 * @package patTemplate 11 * @subpackage Readers 12 * @author Stephan Schmidt <schst@php.net> 13 */ 14 15 // Check to ensure this file is within the rest of the framework 16 defined('JPATH_BASE') or die(); 17 18 /** 19 * No input 20 */ 21 define( 'PATTEMPLATE_READER_ERROR_NO_INPUT', 6000 ); 22 23 /** 24 * Unknown tag 25 */ 26 define( 'PATTEMPLATE_READER_ERROR_UNKNOWN_TAG', 6001 ); 27 28 /** 29 * Invalid tag (missing attribute) 30 */ 31 define( 'PATTEMPLATE_READER_ERROR_INVALID_TAG', 6002 ); 32 33 /** 34 * Closing tag is missing 35 */ 36 define( 'PATTEMPLATE_READER_ERROR_NO_CLOSING_TAG', 6003 ); 37 38 /** 39 * Invalid closing tag 40 */ 41 define( 'PATTEMPLATE_READER_ERROR_INVALID_CLOSING_TAG', 6004 ); 42 43 /** 44 * Invalid condition specified 45 */ 46 define( 'PATTEMPLATE_READER_ERROR_INVALID_CONDITION', 6005 ); 47 48 /** 49 * No name has been specified 50 */ 51 define( 'PATTEMPLATE_READER_ERROR_NO_NAME_SPECIFIED', 6010 ); 52 53 /** 54 * CData in a conditional template 55 */ 56 define( 'PATTEMPLATE_READER_NOTICE_INVALID_CDATA_SECTION', 6050 ); 57 58 /** 59 * template already exists 60 */ 61 define( 'PATTEMPLATE_READER_NOTICE_TEMPLATE_EXISTS', 6051 ); 62 63 /** 64 * Base class for patTemplate readers 65 * 66 * This class is able to parse patTemplate tags from any string you hand it over 67 * It will emulate some kind of SAX parsing by calling start-, end- and CData-handlers. 68 * 69 * @abstract 70 * @package patTemplate 71 * @subpackage Readers 72 * @author Stephan Schmidt <schst@php.net> 73 */ 74 class patTemplate_Reader extends patTemplate_Module 75 { 76 /** 77 * reference to the patTemplate object that instantiated the module 78 * 79 * @access protected 80 * @var object 81 */ 82 var $_tmpl; 83 84 /** 85 * stack for all open elements 86 * @access private 87 * @var array 88 */ 89 var $_elStack; 90 91 /** 92 * stack for all open templates 93 * @access private 94 * @var array 95 */ 96 var $_tmplStack; 97 98 /** 99 * character data 100 * @access private 101 * @var array 102 */ 103 var $_data; 104 105 /** 106 * tag depth 107 * @access private 108 * @var integer 109 */ 110 var $_depth; 111 112 /** 113 * templates that have been found 114 * @access protected 115 * @var array 116 */ 117 var $_templates = array(); 118 119 /** 120 * path to the template 121 * @access protected 122 * @var array 123 */ 124 var $_path = array(); 125 126 /** 127 * start tag for variables 128 * @access private 129 * @var string 130 */ 131 var $_startTag; 132 133 /** 134 * end tag for variables 135 * @access private 136 * @var string 137 */ 138 var $_endTag; 139 140 /** 141 * default attributes 142 * 143 * @access private 144 * @var array 145 */ 146 var $_defaultAtts = array(); 147 148 /** 149 * root attributes 150 * 151 * This is used when reading the template content 152 * from an external file. 153 * 154 * @access private 155 * @var array 156 */ 157 var $_rootAtts = array(); 158 159 /** 160 * inherit attributes 161 * 162 * @access private 163 * @var array 164 */ 165 var $_inheritAtts = array(); 166 167 /** 168 * name of the first template that has been found 169 * 170 * @access private 171 * @var string 172 */ 173 var $_root = null; 174 175 /** 176 * all data that has been processed 177 * 178 * @access private 179 * @var string 180 */ 181 var $_processedData = null; 182 183 /** 184 * current input 185 * 186 * @access private 187 * @var string 188 */ 189 var $_currentInput = null; 190 191 /** 192 * all loaded functions 193 * 194 * @access private 195 * @var array 196 */ 197 var $_functions = array(); 198 199 /** 200 * function aliases 201 * 202 * @access private 203 * @var array 204 */ 205 var $_funcAliases = array(); 206 207 /** 208 * options 209 * 210 * @access private 211 * @var array 212 */ 213 var $_options = array(); 214 215 /** 216 * reader is in use 217 * 218 * @access private 219 * @var boolean 220 */ 221 var $_inUse = false; 222 223 /** 224 * set a reference to the patTemplate object that instantiated the reader 225 * 226 * @access public 227 * @param object patTemplate object 228 */ 229 function setTemplateReference( &$tmpl ) 230 { 231 $this->_tmpl = &$tmpl; 232 } 233 234 /** 235 * read templates from any input 236 * 237 * @abstract must be implemented in the template readers 238 * @param mixed input to read from. 239 * This can be a string, a filename, a resource or whatever the derived class needs to read from 240 * @param array options, not implemented in current versions, but future versions will allow passing of options 241 * @return array template structure 242 */ 243 function readTemplates( $input, $options = array() ) 244 { 245 return array(); 246 } 247 248 /** 249 * load template from any input 250 * 251 * If the a template is loaded, the content will not get 252 * analyzed but the whole content is returned as a string. 253 * 254 * @abstract must be implemented in the template readers 255 * @param mixed input to load from. 256 * This can be a string, a filename, a resource or whatever the derived class needs to read from 257 * @param array options, not implemented in current versions, but future versions will allow passing of options 258 * @return string template content 259 */ 260 function loadTemplate( $input, $options = array() ) 261 { 262 return $input; 263 } 264 265 /** 266 * set options 267 * 268 * @access public 269 * @param array array containing options 270 */ 271 function setOptions( $options ) 272 { 273 $this->_startTag = $options['startTag']; 274 $this->_endTag = $options['endTag']; 275 276 $this->_options = $options; 277 278 if (isset($options['functionAliases'])) { 279 $this->_funcAliases = $options['functionAliases']; 280 } 281 array_map('strtolower', $this->_funcAliases); 282 } 283 284 /** 285 * add an alias for a function 286 * 287 * @access public 288 * @param string alias 289 * @param string function name 290 */ 291 function addFunctionAlias($alias, $function) 292 { 293 $this->_funcAliases[strtolower($alias)] = $function; 294 } 295 296 /** 297 * set the root attributes 298 * 299 * @access public 300 * @param array array containing options 301 */ 302 function setRootAttributes( $attributes ) 303 { 304 $this->_rootAtts = $attributes; 305 } 306 307 /** 308 * parse templates from string 309 * 310 * @access private 311 * @param string string to parse 312 * @return array templates 313 */ 314 function parseString( $string ) 315 { 316 $this->_inUse = true; 317 318 /** 319 * apply input filter before parsing 320 */ 321 $string = $this->_tmpl->applyInputFilters( $string ); 322 323 $this->_inheritAtts = array(); 324 $this->_elStack = array(); 325 $this->_data = array( '' ); 326 $this->_tmplStack = array(); 327 $this->_depth = 0; 328 $this->_templates = array(); 329 $this->_path = array(); 330 $this->_processedData = ''; 331 332 $this->_defaultAtts = $this->_tmpl->getDefaultAttributes(); 333 334 if( !isset( $this->_defaultAtts['autoload'] ) ) { 335 $this->_defaultAtts['autoload'] = 'on'; 336 } 337 338 /** 339 * create a special root template 340 */ 341 $attributes = $this->_rootAtts; 342 $attributes['name'] = '__ptroot'; 343 344 $rootTemplate = $this->_initTemplate( $attributes ); 345 $this->_root = null; 346 unset( $rootTemplate['isRoot'] ); 347 348 array_push( $this->_tmplStack, $rootTemplate ); 349 350 $patNamespace = $this->_tmpl->getNamespace(); 351 if (is_array($patNamespace)) { 352 $patNamespace = array_map('strtolower', $patNamespace); 353 } else { 354 $patNamespace = array(strtolower( $patNamespace )); 355 } 356 357 /** 358 *start parsing 359 */ 360 $regexp = '/(<(\/?)([[:alnum:]]+):([[:alnum:]]+)[[:space:]]*([^>]*)>)/im'; 361 362 $tokens = preg_split( $regexp, $string, -1, PREG_SPLIT_DELIM_CAPTURE ); 363 364 /** 365 * the first token is always character data 366 * Though it could just be empty 367 */ 368 if( $tokens[0] != '' ) { 369 $this->_characterData( $tokens[0] ); 370 } 371 372 $cnt = count( $tokens ); 373 $i = 1; 374 // process all tokens 375 while( $i < $cnt ) { 376 $fullTag = $tokens[$i++]; 377 $closing = $tokens[$i++]; 378 $namespace = strtolower( $tokens[$i++] ); 379 $tagname = strtolower( $tokens[$i++] ); 380 $attString = $tokens[$i++]; 381 $empty = substr( $attString, -1 ); 382 $data = $tokens[$i++]; 383 384 /** 385 * check, whether it's a known namespace 386 * currently only the template namespace is possible 387 */ 388 if( !in_array($namespace, $patNamespace) ) { 389 $this->_characterData( $fullTag ); 390 $this->_characterData( $data ); 391 continue; 392 } 393 394 /** 395 * is it a closing tag? 396 */ 397 if( $closing === '/' ) { 398 $result = $this->_endElement( $namespace, $tagname ); 399 if( patErrorManager::isError( $result ) ) { 400 return $result; 401 } 402 $this->_characterData( $data ); 403 continue; 404 } 405 406 /** 407 * Is empty or opening tag! 408 */ 409 if( $empty === '/' ) { 410 $attString = substr( $attString, 0, -1 ); 411 } 412 413 $attributes = $this->_parseAttributes( $attString ); 414 $result = $this->_startElement( $namespace, $tagname, $attributes ); 415 if( patErrorManager::isError( $result ) ) { 416 return $result; 417 } 418 419 /** 420 * check, if the tag is empty 421 */ 422 if( $empty === '/' ) { 423 $result = $this->_endElement( $namespace, $tagname ); 424 if( patErrorManager::isError( $result ) ) { 425 return $result; 426 } 427 } 428 429 $this->_characterData( $data ); 430 } 431 432 $rootTemplate = array_pop( $this->_tmplStack ); 433 434 $this->_closeTemplate( $rootTemplate, $this->_data[0] ); 435 436 /** 437 * check for tags that are still open 438 */ 439 if( $this->_depth > 0 ) { 440 $el = array_pop( $this->_elStack ); 441 return patErrorManager::raiseError( 442 PATTEMPLATE_READER_ERROR_NO_CLOSING_TAG, 443 $this->_createErrorMessage( "No closing tag for {$el['ns']}:{$el['name']} found" ) 444 ); 445 } 446 447 $this->_inUse = false; 448 449 return $this->_templates; 450 } 451 452 /** 453 * parse an attribute string and build an array 454 * 455 * @access private 456 * @param string attribute string 457 * @param array attribute array 458 */ 459 function _parseAttributes( $string ) 460 { 461 static $entities = array( 462 '<' => '<', 463 '>' => '>', 464 '&' => '&', 465 '"' => '"', 466 ''' => '\'' 467 ); 468 469 $attributes = array(); 470 $match = array(); 471 preg_match_all('/([a-zA-Z_0-9]+)="((?:\\\.|[^"\\\])*)"/U', $string, $match); 472 for ($i = 0; $i < count($match[1]); $i++) { 473 $attributes[strtolower( $match[1][$i] )] = strtr( (string)$match[2][$i], $entities ); 474 } 475 return $attributes; 476 } 477 478 /** 479 * handle start element 480 * 481 * @access private 482 * @param string element name 483 * @param array attributes 484 */ 485 function _startElement( $ns, $name, $attributes ) 486 { 487 array_push( $this->_elStack, array( 488 'ns' => $ns, 489 'name' => $name, 490 'attributes' => $attributes, 491 ) 492 ); 493 494 $this->_depth++; 495 496 $this->_data[$this->_depth] = ''; 497 498 /** 499 * handle tag 500 */ 501 switch( $name ) 502 { 503 /** 504 * template 505 */ 506 case 'tmpl': 507 $result = $this->_initTemplate( $attributes ); 508 break; 509 510 /** 511 * sub-template 512 */ 513 case 'sub': 514 $result = $this->_initSubTemplate( $attributes ); 515 break; 516 517 /** 518 * link 519 */ 520 case 'link': 521 $result = $this->_initLink( $attributes ); 522 break; 523 524 /** 525 * variable 526 */ 527 case 'var': 528 $result = false; 529 break; 530 531 /** 532 * instance 533 */ 534 case 'instance': 535 case 'comment': 536 $result = false; 537 break; 538 539 /** 540 * any other tag 541 */ 542 default: 543 if (isset($this->_funcAliases[strtolower($name)])) { 544 $name = $this->_funcAliases[strtolower($name)]; 545 } 546 $name = ucfirst( $name ); 547 548 if( !$this->_tmpl->moduleExists( 'Function', $name ) ) { 549 550 if (isset($this->_options['defaultFunction']) && !empty($this->_options['defaultFunction'])) { 551 $attributes['_originalTag'] = $name; 552 $name = ucfirst($this->_options['defaultFunction']); 553 } else { 554 return patErrorManager::raiseError( 555 PATTEMPLATE_READER_ERROR_UNKNOWN_TAG, 556 $this->_createErrorMessage( "Unknown tag {$ns}:{$name}." ) 557 ); 558 } 559 } 560 $result = array( 561 'type' => 'custom', 562 'function' => $name, 563 'attributes' => $attributes 564 ); 565 break; 566 } 567 568 if( patErrorManager::isError( $result ) ) { 569 return $result; 570 } 571 572 array_push( $this->_tmplStack, $result ); 573 return true; 574 } 575 576 /** 577 * handle end element 578 * 579 * @access private 580 * @param string element name 581 */ 582 function _endElement( $ns, $name ) 583 { 584 $el = array_pop( $this->_elStack ); 585 $data = $this->_getCData(); 586 $this->_depth--; 587 588 if( $el['name'] != $name || $el['ns'] != $ns ) { 589 return patErrorManager::raiseError( 590 PATTEMPLATE_READER_ERROR_INVALID_CLOSING_TAG, 591 $this->_createErrorMessage( "Invalid closing tag {$ns}:{$name}, {$el['ns']}:{$el['name']} expected" ) 592 ); 593 } 594 595 $tmpl = array_pop( $this->_tmplStack ); 596 597 /** 598 * handle tag 599 */ 600 switch( $name ) 601 { 602 /** 603 * template 604 */ 605 case 'tmpl': 606 $this->_closeTemplate( $tmpl, $data ); 607 break; 608 609 /** 610 * sub-template 611 */ 612 case 'sub': 613 $this->_closeSubTemplate( $tmpl, $data ); 614 break; 615 616 /** 617 * link 618 */ 619 case 'link': 620 $this->_closeLink( $tmpl ); 621 break; 622 623 /** 624 * variable 625 */ 626 case 'var': 627 $this->_handleVariable( $el['attributes'], $data ); 628 break; 629 630 /** 631 * instance 632 */ 633 case 'instance': 634 break; 635 636 /** 637 * comment 638 */ 639 case 'comment': 640 $this->_handleComment( $el['attributes'], $data ); 641 break; 642 643 /** 644 * custom function 645 */ 646 default: 647 $name = ucfirst( $tmpl['function'] ); 648 649 if( !isset( $this->_functions[$name] ) ) { 650 $this->_functions[$name] = $this->_tmpl->loadModule( 'Function', $name ); 651 $this->_functions[$name]->setReader( $this ); 652 } 653 654 $result = $this->_functions[$name]->call( $tmpl['attributes'], $data ); 655 656 if( patErrorManager::isError( $result ) ) { 657 return $result; 658 } 659 660 if( is_string( $result ) ) { 661 $this->_characterData( $result, false ); 662 } 663 break; 664 } 665 return true; 666 } 667 668 /** 669 * handle character data 670 * 671 * @access private 672 * @param string data 673 */ 674 function _characterData( $data, $readFromTemplate = true ) 675 { 676 $this->_data[$this->_depth] .= $data; 677 678 if ($readFromTemplate) { 679 $this->_processedData .= $data; 680 } 681 682 return true; 683 } 684 685 /** 686 * handle a Link 687 * 688 * @access private 689 * @param array attributes 690 * @return boolean true on success 691 */ 692 function _initLink( $attributes ) 693 { 694 /** 695 * needs a src attribute 696 */ 697 if( !isset( $attributes['src'] ) ) { 698 return patErrorManager::raiseError( 699 PATTEMPLATE_READER_ERROR_INVALID_TAG, 700 $this->_createErrorMessage( "Attribute 'src' missing for link" ) 701 ); 702 } 703 704 /** 705 * create a new template 706 */ 707 $tmpl = array( 708 'type' => 'link', 709 'src' => $attributes['src'], 710 ); 711 return $tmpl; 712 } 713 714 /** 715 * close a link template 716 * 717 * It will be added to the dependecies of the parent template. 718 * 719 * @access private 720 * @param array template definition for the link 721 */ 722 function _closeLink( $tmpl ) 723 { 724 /** 725 * add it to the dependencies 726 */ 727 if( !empty( $this->_tmplStack ) ) 728 { 729 $this->_addToParentTag( 'dependencies', strtolower( $tmpl['src'] ) ); 730 $this->_characterData( sprintf( "%sTMPL:%s%s", $this->_startTag, strtoupper( $tmpl['src'] ), $this->_endTag ) ); 731 } 732 733 return true; 734 } 735 736 /** 737 * create a new template 738 * 739 * @access private 740 * @param array attributes 741 * @return boolean true on success 742 */ 743 function _initTemplate( $attributes ) 744 { 745 /** 746 * build name for the template 747 */ 748 if (!isset( $attributes['name'] )) { 749 $name = $this->_buildTemplateName(); 750 } else { 751 $name = strtolower( $attributes['name'] ); 752 unset( $attributes['name'] ); 753 } 754 755 /** 756 * name must be unique 757 */ 758 if( isset( $this->_templates[$name] ) || $this->_tmpl->exists( $name ) ) { 759 patErrorManager::raiseNotice( 760 PATTEMPLATE_READER_NOTICE_TEMPLATE_EXISTS, 761 $this->_createErrorMessage( "Template $name already exists" ), 762 $name 763 ); 764 } 765 766 /** 767 * update the path 768 */ 769 array_push( $this->_path, $name ); 770 771 if( isset( $attributes['maxloop'] ) ) { 772 if (!isset( $attributes['parent'] )) { 773 $attributes['parent'] = $this->_getFromParentTemplate( 'name' ); 774 } 775 } 776 777 $attributes = $this->_prepareTmplAttributes( $attributes, $name ); 778 779 array_push( $this->_inheritAtts, array( 780 'whitespace' => $attributes['whitespace'], 781 'unusedvars' => $attributes['unusedvars'], 782 'autoclear' => $attributes['autoclear'] 783 ) 784 ); 785 786 /** 787 * create a new template 788 */ 789 $tmpl = array( 790 'type' => 'tmpl', 791 'name' => $name, 792 'attributes' => $attributes, 793 'content' => '', 794 'dependencies' => array(), 795 'varspecs' => array(), 796 'comments' => array(), 797 'loaded' => false, 798 'parsed' => false, 799 'input' => $this->_name.'://'.$this->_currentInput 800 ); 801 802 if( $this->_root == null ) { 803 $this->_root = $name; 804 $tmpl['isRoot'] = true; 805 } 806 807 808 /** 809 * prepare subtemplates 810 */ 811 switch( $attributes['type'] ) { 812 case 'condition': 813 case 'modulo': 814 $tmpl['subtemplates'] = array(); 815 break; 816 } 817 818 return $tmpl; 819 } 820 821 /** 822 * prepare attributes 823 * 824 * @access private 825 * @param array attributes 826 * @param string template name (only used for error messages) 827 * @return array attributes 828 */ 829 function _prepareTmplAttributes( $attributes, $templatename ) 830 { 831 /** 832 * do not prepare twice 833 */ 834 if( isset( $attributes['__prepared'] ) && $attributes['__prepared'] === true ) { 835 return $attributes; 836 } 837 838 $attributes = $this->_inheritAttributes( $attributes ); 839 840 /** 841 * get the attributes 842 */ 843 $attributes = array_merge( $this->_tmpl->getDefaultAttributes(), $attributes ); 844 845 $attributes['type'] = strtolower( $attributes['type'] ); 846 847 if( !isset( $attributes['rowoffset'] ) ) { 848 $attributes['rowoffset'] = 1; 849 } 850 851 if( !isset( $attributes['addsystemvars'] ) ) { 852 $attributes['addsystemvars'] = false; 853 } else { 854 switch ($attributes['addsystemvars']) { 855 case 'on': 856 case 'boolean': 857 $attributes['addsystemvars'] = 'boolean'; 858 break; 859 case 'int': 860 case 'integer': 861 $attributes['addsystemvars'] = 'integer'; 862 break; 863 case 'off': 864 $attributes['addsystemvars'] = false; 865 break; 866 } 867 } 868 869 /** 870 * external template 871 */ 872 if( isset( $attributes['src'] ) ) { 873 if( !isset( $attributes['parse'] ) ) 874 $attributes['parse'] = 'on'; 875 if( !isset( $attributes['reader'] ) ) 876 $attributes['reader'] = $this->getName(); 877 if( !isset( $attributes['autoload'] ) ) 878 $attributes['autoload'] = $this->_defaultAtts['autoload']; 879 880 if (isset($attributes['relative']) && strtolower($attributes['relative'] === 'yes')) { 881 $attributes['relative'] = $this->getCurrentInput(); 882 } else { 883 $attributes['relative'] = false; 884 } 885 } 886 887 /** 888 * varscope is set 889 */ 890 if( isset( $attributes['varscope'] ) ) { 891 /** 892 * varscope is parent 893 */ 894 if( $attributes['varscope'] === '__parent' ) { 895 $attributes['varscope'] = $this->_getFromParentTemplate( 'name' ); 896 } 897 898 $attributes['varscope'] = strtolower( $attributes['varscope'] ); 899 if (strstr($attributes['varscope'], ',')) { 900 $attributes['varscope'] = array_map('trim', explode(',', $attributes['varscope'])); 901 } 902 } 903 904 switch( $attributes['type'] ) { 905 /** 906 * validate condition template 907 */ 908 case 'condition': 909 if( !isset( $attributes['conditionvar'] ) ) { 910 return patErrorManager::raiseError( 911 PATTEMPLATE_READER_ERROR_INVALID_TAG, 912 $this->_createErrorMessage( "Attribute 'conditionvar' missing for $templatename" ) 913 ); 914 } 915 $attributes['conditionvar'] = strtoupper( $attributes['conditionvar'] ); 916 917 if( strstr( $attributes['conditionvar'], '.' ) ) { 918 list( $attributes['conditiontmpl'], $attributes['conditionvar'] ) = explode( '.', $attributes['conditionvar'] ); 919 $attributes['conditiontmpl'] = strtolower( $attributes['conditiontmpl'] ); 920 } 921 922 $attributes['autoclear'] = 'yes'; 923 924 if (!isset( $attributes['useglobals'] )) { 925 $attributes['useglobals'] = 'no'; 926 } 927 break; 928 929 /** 930 * validate simplecondition template 931 */ 932 case 'simplecondition': 933 if( !isset( $attributes['requiredvars'] ) ) { 934 return patErrorManager::raiseError( 935 PATTEMPLATE_READER_ERROR_INVALID_TAG, 936 $this->_createErrorMessage( "Attribute 'requiredvars' missing for $templatename" ) 937 ); 938 } 939 $tmp = array_map( 'trim', explode( ',', $attributes['requiredvars'] ) ); 940 $attributes['requiredvars'] = array(); 941 foreach( $tmp as $var ) { 942 943 $pos = strpos( $var, '=' ); 944 if ($pos !== false) { 945 $val = trim(substr( $var, $pos+1 )); 946 $var = trim(substr( $var, 0, $pos )); 947 } else { 948 $val = null; 949 } 950 $var = strtoupper($var); 951 $pos = strpos( $var, '.' ); 952 953 if ($pos === false) { 954 array_push( $attributes['requiredvars'], array( $templatename, $var, $val ) ); 955 } else { 956 array_push( $attributes['requiredvars'], array( 957 strtolower( substr( $var, 0, $pos ) ), 958 substr( $var, $pos+1 ), 959 $val 960 ) 961 ); 962 } 963 964 } 965 $attributes['autoclear'] = 'yes'; 966 break; 967 968 /** 969 * oddeven => switch to new modulo syntax 970 */ 971 case 'oddeven': 972 $attributes['type'] = 'modulo'; 973 $attributes['modulo'] = 2; 974 $attributes['autoclear'] = 'yes'; 975 break; 976 977 /** 978 * modulo => requires a module attribute 979 */ 980 case 'modulo': 981 if( !isset( $attributes['modulo'] ) ) { 982 return patErrorManager::raiseError( 983 PATTEMPLATE_READER_ERROR_INVALID_TAG, 984 $this->_createErrorMessage( "Attribute 'modulo' missing for $templatename" ) 985 ); 986 } 987 $attributes['autoclear'] = 'yes'; 988 break; 989 990 /** 991 * standard template => do nothing 992 */ 993 case 'standard': 994 break; 995 996 /** 997 * unknown type 998 */ 999 default: 1000 return patErrorManager::raiseError( 1001 PATTEMPLATE_READER_ERROR_INVALID_TAG, 1002 $this->_createErrorMessage( "Unknown value for attribute type: {$attributes['type']}" ) 1003 ); 1004 break; 1005 } 1006 1007 $attributes['__prepared'] = true; 1008 1009 return $attributes; 1010 } 1011 1012 /** 1013 * build a template name 1014 * 1015 * @access private 1016 * @return string new template name 1017 */ 1018 function _buildTemplateName() 1019 { 1020 return strtolower( uniqid( 'tmpl' ) ); 1021 } 1022 1023 /** 1024 * close the current template 1025 * 1026 * @access private 1027 * @return boolean true on success 1028 */ 1029 function _closeTemplate( $tmpl, $data ) 1030 { 1031 $name = array_pop( $this->_path ); 1032 1033 $data = $this->_adjustWhitespace( $data, $tmpl['attributes']['whitespace'] ); 1034 1035 array_pop( $this->_inheritAtts ); 1036 1037 /** 1038 * check for special templates 1039 */ 1040 switch( $tmpl['attributes']['type'] ) 1041 { 1042 /** 1043 * check for whitespace in conditional templates 1044 * and raise a notice 1045 */ 1046 case 'condition': 1047 case 'modulo': 1048 if( trim( $data ) != '' ) { 1049 patErrorManager::raiseNotice( 1050 PATTEMPLATE_READER_NOTICE_INVALID_CDATA_SECTION, 1051 $this->_createErrorMessage( sprintf( 'No cdata is allowed inside a template of type %s (cdata was found in %s)', $tmpl['attributes']['type'], $tmpl['name'] ) ) 1052 ); 1053 } 1054 $data = null; 1055 break; 1056 } 1057 1058 /** 1059 * store the content 1060 */ 1061 $tmpl['content'] = $data; 1062 1063 /** 1064 * No external template 1065 */ 1066 if( !isset( $tmpl['attributes']['src'] ) ) { 1067 $tmpl['loaded'] = true; 1068 } 1069 1070 /** 1071 * add it to the dependencies 1072 */ 1073 if( !empty( $this->_tmplStack ) ) { 1074 $this->_addToParentTag( 'dependencies', $name ); 1075 1076 if( isset( $tmpl['attributes']['placeholder'] ) ) { 1077 // maintain BC 1078 if( $this->shouldMaintainBc() && $tmpl['attributes']['placeholder'] === 'none' ) { 1079 $tmpl['attributes']['placeholder'] = '__none'; 1080 } 1081 1082 if( $tmpl['attributes']['placeholder'] !== '__none' ) { 1083 $this->_characterData( $this->_startTag.(strtoupper( $tmpl['attributes']['placeholder'] ) ).$this->_endTag ); 1084 } 1085 } else { 1086 $this->_characterData( sprintf( "%sTMPL:%s%s", $this->_startTag, strtoupper( $name ), $this->_endTag ) ); 1087 } 1088 } 1089 1090 unset( $tmpl['name'] ); 1091 unset( $tmpl['tag'] ); 1092 1093 $this->_templates[$name] = $tmpl; 1094 1095 return true; 1096 } 1097 1098 /** 1099 * create a new sub-template 1100 * 1101 * @access private 1102 * @param array attributes 1103 * @return boolean true on success 1104 */ 1105 function _initSubTemplate( $attributes ) 1106 { 1107 /** 1108 * has to be embedded in a 'tmpl' tag 1109 */ 1110 if (!$this->_parentTagIs('tmpl')) { 1111 return patErrorManager::raiseError( 1112 PATTEMPLATE_READER_ERROR_INVALID_TAG, 1113 $this->_createErrorMessage( 'A subtemplate is only allowed in a TMPL tag' ) 1114 ); 1115 } 1116 1117 /** 1118 * needs a condition attribute 1119 */ 1120 if (!isset( $attributes['condition'] )) { 1121 return patErrorManager::raiseError( 1122 PATTEMPLATE_READER_ERROR_NO_CONDITION_SPECIFIED, 1123 $this->_createErrorMessage( 'Missing \'condition\' attribute for subtemplate' ) 1124 ); 1125 } 1126 $matches = array(); 1127 $regexp = '/^'.$this->_startTag.'([^a-z]+[^\\\])'.$this->_endTag.'$/U'; 1128 if (preg_match($regexp, $attributes['condition'], $matches)) { 1129 $attributes['var'] = $matches[1]; 1130 } 1131 1132 /** 1133 * maintain BC 1134 */ 1135 if( $this->shouldMaintainBc() && in_array( $attributes['condition'], array( 'default', 'empty', 'odd', 'even' ) ) ) { 1136 $attributes['condition'] = '__' . $attributes['condition']; 1137 } 1138 1139 if( $attributes['condition'] == '__odd' ) { 1140 $attributes['condition'] = 1; 1141 } elseif( $attributes['condition'] == '__even' ) { 1142 $attributes['condition'] = 0; 1143 } 1144 1145 $parent = array_pop( $this->_tmplStack ); 1146 array_push( $this->_tmplStack, $parent ); 1147 if ($parent['attributes']['type'] == 'modulo') { 1148 1149 if( preg_match( '/^\d$/', $attributes['condition'] ) ) { 1150 if( (integer)$attributes['condition'] >= $parent['attributes']['modulo'] ) { 1151 return patErrorManager::raiseError( 1152 PATTEMPLATE_READER_ERROR_INVALID_CONDITION, 1153 $this->_createErrorMessage( 'Condition may only be between 0 and '.($parent['attributes']['modulo']-1) ) 1154 ); 1155 } 1156 } 1157 } 1158 1159 $attributes = $this->_inheritAttributes( $attributes ); 1160 1161 $condition = $attributes['condition']; 1162 unset( $attributes['condition'] ); 1163 1164 $subTmpl = array( 1165 'type' => 'sub', 1166 'condition' => $condition, 1167 'data' => '', 1168 'attributes' => $attributes, 1169 'comments' => array(), 1170 'dependencies' => array() 1171 ); 1172 1173 return $subTmpl; 1174 } 1175 1176 /** 1177 * close subtemplate 1178 * 1179 * @access private 1180 * @param string data 1181 * @return boolean true on success 1182 */ 1183 function _closeSubTemplate( $subTmpl, $data ) 1184 { 1185 $data = $this->_adjustWhitespace( $data, $subTmpl['attributes']['whitespace'] ); 1186 1187 $subTmpl['data'] = $data; 1188 $condition = $subTmpl['condition']; 1189 unset( $subTmpl['condition'] ); 1190 1191 $this->_addToParentTemplate( 'subtemplates', 1192 $subTmpl, 1193 $condition 1194 ); 1195 return true; 1196 } 1197 1198 /** 1199 * handle a variable 1200 * 1201 * @access private 1202 * @param array attributes of the var tag 1203 * @param string cdata between the tags (will be used as default) 1204 * @return boolean true on success 1205 */ 1206 function _handleVariable( $attributes, $data ) 1207 { 1208 if( !isset( $attributes['name'] ) ) { 1209 return patErrorManager::raiseError( 1210 PATTEMPLATE_READER_ERROR_NO_NAME_SPECIFIED, 1211 $this->_createErrorMessage( 'Variable needs a name attribute' ) 1212 ); 1213 } 1214 1215 $specs = array(); 1216 1217 /** 1218 * get name 1219 */ 1220 $name = strtoupper( $attributes['name'] ); 1221 unset( $attributes['name'] ); 1222 $specs['name'] = $name; 1223 1224 /** 1225 * use data as default value 1226 */ 1227 if( isset( $attributes['default'] ) ) { 1228 $data = $attributes['default']; 1229 $specs['default'] = $data; 1230 unset( $attributes['default'] ); 1231 } elseif (!empty( $data )) { 1232 $specs['default'] = $data; 1233 } 1234 1235 /** 1236 * add it to template, if it's not hidden 1237 */ 1238 if (!isset( $attributes['hidden'] ) || $attributes['hidden'] == 'no') { 1239 $this->_characterData( $this->_startTag . strtoupper( $name ) . $this->_endTag ); 1240 } 1241 1242 if( isset( $attributes['hidden'] ) ) { 1243 unset( $attributes['hidden'] ); 1244 } 1245 1246 /** 1247 * copy value from any other variable 1248 */ 1249 if (isset( $attributes['copyfrom'] )) { 1250 $specs['copyfrom'] = strtoupper( $attributes['copyfrom'] ); 1251 1252 if (strstr( $specs['copyfrom'], '.' )) { 1253 $specs['copyfrom'] = explode( '.', $specs['copyfrom'] ); 1254 $specs['copyfrom'][0] = strtolower( $specs['copyfrom'][0] ); 1255 } 1256 1257 unset( $attributes['copyfrom'] ); 1258 } 1259 1260 if( isset( $attributes['modifier'] ) ) { 1261 $modifier = $attributes['modifier']; 1262 unset( $attributes['modifier'] ); 1263 1264 $type = isset( $attributes['modifiertype'] ) ? $attributes['modifiertype'] : 'auto'; 1265 1266 if( isset( $attributes['modifiertype'] ) ) 1267 unset( $attributes['modifiertype'] ); 1268 1269 $specs['modifier'] = array( 'mod' => $modifier, 'type' => $type, 'params' => $attributes ); 1270 } 1271 1272 if (!empty( $specs )) { 1273 $this->_addToParentTemplate( 1274 'varspecs', 1275 $specs, 1276 $name 1277 ); 1278 } 1279 return true; 1280 } 1281 1282 1283 /** 1284 * handle a comment 1285 * 1286 * @access private 1287 * @param array attributes of the comment tag 1288 * @param string cdata between the tags (will be used as default) 1289 * @return boolean true on success 1290 */ 1291 function _handleComment( $attributes, $data ) 1292 { 1293 $this->_addToParentTag( 'comments', $data ); 1294 } 1295 1296 /** 1297 * get the character data of the element 1298 * 1299 * @access private 1300 * @return string 1301 */ 1302 function _getCData() 1303 { 1304 if( $this->_depth == 0 ) { 1305 return ''; 1306 } 1307 return $this->_data[$this->_depth]; 1308 } 1309 1310 /** 1311 * add to a property of the parent template 1312 * 1313 * @access private 1314 * @param string property to add to 1315 * @param mixed value to add 1316 * @param string key 1317 */ 1318 function _addToParentTemplate( $property, $value, $key = null ) 1319 { 1320 $cnt = count( $this->_tmplStack ); 1321 1322 if ($cnt === 0) { 1323 return false; 1324 } 1325 1326 $pos = $cnt - 1; 1327 while ($pos >= 0) { 1328 if ($this->_tmplStack[$pos]['type'] != 'tmpl') { 1329 $pos--; 1330 continue; 1331 } 1332 1333 if ($key === null) { 1334 1335 if (!in_array( $value, $this->_tmplStack[$pos][$property] )) { 1336 array_push( $this->_tmplStack[$pos][$property], $value ); 1337 } 1338 } else { 1339 $this->_tmplStack[$pos][$property][$key] = $value; 1340 } 1341 1342 return true; 1343 } 1344 1345 return false; 1346 } 1347 1348 /** 1349 * get a property of the parent template 1350 * 1351 * @access private 1352 * @param string property to add to 1353 * @return mixed value to add 1354 */ 1355 function _getFromParentTemplate( $property ) 1356 { 1357 $cnt = count( $this->_tmplStack ); 1358 1359 if ($cnt === 0) { 1360 return false; 1361 } 1362 1363 $pos = $cnt - 1; 1364 while ($pos >= 0) { 1365 if( $this->_tmplStack[$pos]['type'] != 'tmpl' ) { 1366 $pos--; 1367 continue; 1368 } 1369 1370 if (isset( $this->_tmplStack[$pos][$property] )) { 1371 return $this->_tmplStack[$pos][$property]; 1372 } 1373 1374 return false; 1375 } 1376 return false; 1377 } 1378 1379 1380 /** 1381 * add to a property of the parent tag 1382 * 1383 * @access private 1384 * @param string property to add to 1385 * @param mixed value to add 1386 * @param string key 1387 */ 1388 function _addToParentTag( $property, $value, $key = null ) 1389 { 1390 $cnt = count( $this->_tmplStack ); 1391 1392 if ($cnt === 0) { 1393 return false; 1394 } 1395 1396 $pos = $cnt - 1; 1397 1398 if ($key === null) { 1399 1400 if (!in_array( $value, $this->_tmplStack[$pos][$property] )) { 1401 array_push( $this->_tmplStack[$pos][$property], $value ); 1402 } 1403 } else { 1404 $this->_tmplStack[$pos][$property][$key] = $value; 1405 } 1406 1407 return true; 1408 } 1409 1410 /** 1411 * adjust whitespace in a CData block 1412 * 1413 * @access private 1414 * @param string data 1415 * @param string behaviour 1416 * @return string data 1417 */ 1418 function _adjustWhitespace( $data, $behaviour ) 1419 { 1420 switch( $behaviour ) { 1421 case 'trim': 1422 $data = str_replace( '\n', ' ', $data ); 1423 $data = preg_replace( '/\s\s+/', ' ', $data ); 1424 $data = trim( $data ); 1425 break; 1426 } 1427 return $data; 1428 } 1429 1430 /** 1431 * inherit attributes from the parent template 1432 * 1433 * The following attributes are inherited automatically: 1434 * - whitespace 1435 * - unusedvars 1436 * 1437 * @access private 1438 * @param array attributes 1439 * @param array attributes with inherited attributes 1440 * @return array new attribute collection 1441 */ 1442 function _inheritAttributes( $attributes ) 1443 { 1444 if (!empty( $this->_inheritAtts )) { 1445 $parent = end( $this->_inheritAtts ); 1446 } else { 1447 $parent = array( 1448 'whitespace' => $this->_defaultAtts['whitespace'], 1449 'unusedvars' => $this->_defaultAtts['unusedvars'], 1450 'autoclear' => $this->_defaultAtts['autoclear'] 1451 ); 1452 } 1453 1454 $attributes = array_merge( $parent, $attributes ); 1455 1456 return $attributes; 1457 } 1458 1459 /** 1460 * checks, whether the parent tag is of a certain type 1461 * 1462 * This is needed to ensure, that subtemplates are only 1463 * placed inside a template 1464 * 1465 * @access private 1466 * @param string type (tmpl, sub, var, link) 1467 * @return boolean 1468 */ 1469 function _parentTagIs( $type ) 1470 { 1471 $parent = array_pop( $this->_tmplStack ); 1472 if( $parent === null ) { 1473 return false; 1474 } 1475 array_push( $this->_tmplStack, $parent ); 1476 1477 if( $parent['type'] == $type ) { 1478 return true; 1479 } 1480 1481 return false; 1482 } 1483 1484 /** 1485 * get the current line number 1486 * 1487 * @access private 1488 * @return integer line number 1489 */ 1490 function _getCurrentLine() 1491 { 1492 $line = count( explode( "\n", $this->_processedData ) ); 1493 return $line; 1494 } 1495 1496 /** 1497 * create an error message 1498 * 1499 * This method takes an error messages and appends the 1500 * current line number as well as a pointer to the input 1501 * (filename) 1502 * 1503 * @access private 1504 * @param string base error message 1505 * @return strin error message 1506 */ 1507 function _createErrorMessage( $msg ) 1508 { 1509 return sprintf( '%s in %s on line %d', $msg, $this->getCurrentInput(), $this->_getCurrentLine() ); 1510 } 1511 1512 /** 1513 * get the current input 1514 * 1515 * @access public 1516 * @return string 1517 */ 1518 function getCurrentInput() 1519 { 1520 return $this->_currentInput; 1521 } 1522 1523 /** 1524 * tests whether the reader should maintain backwards compatibility 1525 * 1526 * If enabled, you can still use 'default', 'empty', 'odd' and 'even' 1527 * instead of '__default', '__empty', etc. 1528 * 1529 * This will be disabled by default in future versions. 1530 * 1531 * @access public 1532 * @return boolean 1533 */ 1534 function shouldMaintainBc() 1535 { 1536 if (!isset( $this->_options['maintainBc'] )) { 1537 return false; 1538 } 1539 return $this->_options['maintainBc']; 1540 } 1541 1542 /** 1543 * returns, whether the reader currently is in use 1544 * 1545 * @access public 1546 * @return boolean 1547 */ 1548 function isInUse() 1549 { 1550 return $this->_inUse; 1551 } 1552 1553 /** 1554 * get the template root for this reader 1555 * 1556 * @access public 1557 * @return string 1558 */ 1559 function getTemplateRoot() 1560 { 1561 if (!isset($this->_options['root'])) { 1562 return null; 1563 } 1564 if (isset($this->_options['root'][$this->_name])) { 1565 return $this->_options['root'][$this->_name]; 1566 } 1567 if (isset($this->_options['root']['__default'])) { 1568 return $this->_options['root']['__default']; 1569 } 1570 return null; 1571 } 1572 } 1573 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Mar 28 15:54:07 2012 | Cross-referenced by PHPXref 0.7.1 |