[ Index ]

PHP Cross Reference of Joomla 1.5.26 DE

title

Body

[close]

/libraries/pattemplate/patTemplate/ -> Reader.php (source)

   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                                      '&lt;' => '<',
 463                                      '&gt;' => '>',
 464                                      '&amp;' => '&',
 465                                      '&quot;' => '"',
 466                                      '&apos;' => '\''
 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  ?>


Generated: Wed Mar 28 15:54:07 2012 Cross-referenced by PHPXref 0.7.1