eZ Publish  [4.0]
eztemplatemultipassparser.php
Go to the documentation of this file.
00001 <?php
00002 //
00003 // Definition of eZTemplateMultiPassParser class
00004 //
00005 // Created on: <26-Nov-2002 17:25:44 amos>
00006 //
00007 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00008 // SOFTWARE NAME: eZ Publish
00009 // SOFTWARE RELEASE: 4.0.x
00010 // COPYRIGHT NOTICE: Copyright (C) 1999-2008 eZ Systems AS
00011 // SOFTWARE LICENSE: GNU General Public License v2.0
00012 // NOTICE: >
00013 //   This program is free software; you can redistribute it and/or
00014 //   modify it under the terms of version 2.0  of the GNU General
00015 //   Public License as published by the Free Software Foundation.
00016 //
00017 //   This program is distributed in the hope that it will be useful,
00018 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 //   GNU General Public License for more details.
00021 //
00022 //   You should have received a copy of version 2.0 of the GNU General
00023 //   Public License along with this program; if not, write to the Free
00024 //   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00025 //   MA 02110-1301, USA.
00026 //
00027 //
00028 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00029 //
00030 
00031 /*! \file eztemplatemultipassparser.php
00032 */
00033 
00034 /*!
00035   \class eZTemplateMultiPassParser eztemplatemultipassparser.php
00036   \brief The class eZTemplateMultiPassParser does
00037 
00038 */
00039 
00040 //include_once( 'lib/eztemplate/classes/eztemplateparser.php' );
00041 //include_once( 'lib/eztemplate/classes/eztemplateelementparser.php' );
00042 //include_once( 'lib/eztemplate/classes/eztemplate.php' );
00043 
00044 class eZTemplateMultiPassParser extends eZTemplateParser
00045 {
00046     /*!
00047      Constructor
00048     */
00049     function eZTemplateMultiPassParser()
00050     {
00051         $this->ElementParser = eZTemplateElementParser::instance();
00052     }
00053 
00054 
00055     /*!
00056      Parses the template file $sourceText. See the description of this class
00057      for more information on the parsing process.
00058 
00059     */
00060     function parse( $tpl, $sourceText, &$rootElement, $rootNamespace, &$resourceData )
00061     {
00062         $relatedResource = $resourceData['resource'];
00063         $relatedTemplateName = $resourceData['template-filename'];
00064 
00065         $currentRoot =& $rootElement;
00066         $leftDelimiter = $tpl->LDelim;
00067         $rightDelimiter = $tpl->RDelim;
00068         $sourceLength = strlen( $sourceText );
00069         $sourcePosition = 0;
00070 
00071         eZDebug::accumulatorStart( 'template_multi_parser_1', 'template_total', 'Template parser: create text elements' );
00072         $textElements = $this->parseIntoTextElements( $tpl, $sourceText, $sourcePosition,
00073                                                       $leftDelimiter, $rightDelimiter, $sourceLength,
00074                                                       $relatedTemplateName );
00075         eZDebug::accumulatorStop( 'template_multi_parser_1' );
00076 
00077         eZDebug::accumulatorStart( 'template_multi_parser_2', 'template_total', 'Template parser: remove whitespace' );
00078         $textElements = $this->parseWhitespaceRemoval( $tpl, $textElements );
00079         eZDebug::accumulatorStop( 'template_multi_parser_2' );
00080 
00081         eZDebug::accumulatorStart( 'template_multi_parser_3', 'template_total', 'Template parser: construct tree' );
00082         $this->parseIntoTree( $tpl, $textElements, $currentRoot,
00083                               $rootNamespace, $relatedResource, $relatedTemplateName );
00084         eZDebug::accumulatorStop( 'template_multi_parser_3' );
00085     }
00086 
00087     function gotoEndPosition( $text, $line, $column, &$endLine, &$endColumn )
00088     {
00089         $lines = preg_split( "#\r\n|\r|\n#", $text );
00090         if ( count( $lines ) > 0 )
00091         {
00092             $endLine = $line + count( $lines ) - 1;
00093             $lastLine = $lines[count( $lines ) - 1];
00094             if ( count( $lines ) > 1 )
00095                 $endColumn = strlen( $lastLine );
00096             else
00097                 $endColumn = $column + strlen( $lastLine );
00098         }
00099         else
00100         {
00101             $endLine = $line;
00102             $endColumn = $column;
00103         }
00104     }
00105 
00106     function parseIntoTextElements( $tpl, $sourceText, $sourcePosition,
00107                                      $leftDelimiter, $rightDelimiter, $sourceLength,
00108                                      $relatedTemplateName )
00109     {
00110         if ( $tpl->ShowDetails )
00111             eZDebug::addTimingPoint( "Parse pass 1 (simple tag parsing)" );
00112         $currentLine = 1;
00113         $currentColumn = 0;
00114         $textElements = array();
00115         while( $sourcePosition < $sourceLength )
00116         {
00117             $tagPos = strpos( $sourceText, $leftDelimiter, $sourcePosition );
00118             if ( $tagPos === false )
00119             {
00120                 // No more tags
00121                 unset( $data );
00122                 $data = substr( $sourceText, $sourcePosition );
00123                 $this->gotoEndPosition( $data, $currentLine, $currentColumn, $endLine, $endColumn );
00124                 $textElements[] = array( "text" => $data,
00125                                          "type" => eZTemplate::ELEMENT_TEXT,
00126                                          'placement' => array( 'templatefile' => $relatedTemplateName,
00127                                                                'start' => array( 'line' => $currentLine,
00128                                                                                  'column' => $currentColumn,
00129                                                                                  'position' => $sourcePosition ),
00130                                                                'stop' => array( 'line' => $endLine,
00131                                                                                 'column' => $endColumn,
00132                                                                                 'position' => $sourceLength - 1 ) ) );
00133                 $sourcePosition = $sourceLength;
00134                 $currentLine = $endLine;
00135                 $currentColumn = $endColumn;
00136             }
00137             else
00138             {
00139                 $blockStart = $tagPos;
00140                 $tagPos++;
00141                 if ( $tagPos < $sourceLength and
00142                      $sourceText[$tagPos] == "*" ) // Comment
00143                 {
00144                     $endPos = strpos( $sourceText, "*$rightDelimiter", $tagPos + 1 );
00145                     $len = $endPos - $tagPos;
00146                     if ( $sourcePosition < $blockStart )
00147                     {
00148                         // Add text before tag.
00149                         unset( $data );
00150                         $data = substr( $sourceText, $sourcePosition, $blockStart - $sourcePosition );
00151                         $this->gotoEndPosition( $data, $currentLine, $currentColumn, $endLine, $endColumn );
00152                         $textElements[] = array( "text" => $data,
00153                                                  "type" => eZTemplate::ELEMENT_TEXT,
00154                                                  'placement' => array( 'templatefile' => $relatedTemplateName,
00155                                                                        'start' => array( 'line' => $currentLine,
00156                                                                                          'column' => $currentColumn,
00157                                                                                          'position' => $sourcePosition ),
00158                                                                        'stop' => array( 'line' => $endLine,
00159                                                                                         'column' => $endColumn,
00160                                                                                         'position' => $blockStart ) ) );
00161                         $currentLine = $endLine;
00162                         $currentColumn = $endColumn;
00163                     }
00164                     if ( $endPos === false )
00165                     {
00166                         $endPos = $sourceLength;
00167                         $blockEnd = $sourceLength;
00168                     }
00169                     else
00170                     {
00171                         $blockEnd = $endPos + 2;
00172                     }
00173                     $comment_text = substr( $sourceText, $tagPos + 1, $endPos - $tagPos - 1 );
00174                     $this->gotoEndPosition( $comment_text, $currentLine, $currentColumn, $endLine, $endColumn );
00175                     $textElements[] = array( "text" => $comment_text,
00176                                              "type" => eZTemplate::ELEMENT_COMMENT,
00177                                              'placement' => array( 'templatefile' => $relatedTemplateName,
00178                                                                    'start' => array( 'line' => $currentLine,
00179                                                                                      'column' => $currentColumn,
00180                                                                                      'position' => $tagPos + 1 ),
00181                                                                    'stop' => array( 'line' => $endLine,
00182                                                                                     'column' => $endColumn,
00183                                                                                     'position' => $endPos - 1 ) ) );
00184                     if ( $sourcePosition < $blockEnd )
00185                         $sourcePosition = $blockEnd;
00186                     $currentLine = $endLine;
00187                     $currentColumn = $endColumn;
00188 //                     eZDebug::writeDebug( "eZTemplate: Comment: $comment" );
00189                 }
00190                 else
00191                 {
00192                     $tmp_pos = $tagPos;
00193                     while( ( $endPos = strpos( $sourceText, $rightDelimiter, $tmp_pos ) ) !== false )
00194                     {
00195                         if ( $sourceText[$endPos-1] != "\\" )
00196                             break;
00197                         $tmp_pos = $endPos + 1;
00198                     }
00199                     if ( $endPos === false )
00200                     {
00201                         // Unterminated tag
00202                         $data = substr( $sourceText, $sourcePosition );
00203                         $this->gotoEndPosition( $data, $currentLine, $currentColumn, $endLine, $endColumn );
00204                         $textBefore = substr( $sourceText, $sourcePosition, $tagPos - $sourcePosition );
00205                         $textPortion = substr( $sourceText, $tagPos );
00206                         $this->gotoEndPosition( $textBefore, $currentLine, $currentColumn, $tagStartLine, $tagStartColumn );
00207                         $this->gotoEndPosition( $textPortion, $tagStartLine, $tagStartColumn, $tagEndLine, $tagEndColumn );
00208                         $tpl->error( "", "parser error @ $relatedTemplateName:$currentLine" . "[$currentColumn]" . "\n" .
00209                                      "Unterminated tag, needs a $rightDelimiter to end the tag.\n" . $leftDelimiter . $textPortion,
00210                                      array( array( $tagStartLine, $tagStartColumn, $tagPosition ),
00211                                               array( $tagEndLine, $tagEndColumn, $sourceLength - 1 ),
00212                                               $relatedTemplateName ) );
00213                         $textElements[] = array( "text" => $data,
00214                                                  "type" => eZTemplate::ELEMENT_TEXT,
00215                                                  'placement' => array( 'templatefile' => $relatedTemplateName,
00216                                                                        'start' => array( 'line' => $currentLine,
00217                                                                                          'column' => $currentColumn,
00218                                                                                          'position' => $sourcePosition ),
00219                                                                        'stop' => array( 'line' => $endLine,
00220                                                                                         'column' => $endColumn,
00221                                                                                         'position' => $sourceLength - 1 ) ) );
00222                         $sourcePosition = $sourceLength;
00223                         $currentLine = $endLine;
00224                         $currentColumn = $endColumn;
00225                     }
00226                     else
00227                     {
00228                         $blockEnd = $endPos + 1;
00229                         $len = $endPos - $tagPos;
00230                         if ( $sourcePosition < $blockStart )
00231                         {
00232                             // Add text before tag.
00233                             $data = substr( $sourceText, $sourcePosition, $blockStart - $sourcePosition );
00234                             $this->gotoEndPosition( $data, $currentLine, $currentColumn, $endLine, $endColumn );
00235                             $textElements[] = array( "text" => $data,
00236                                                      "type" => eZTemplate::ELEMENT_TEXT,
00237                                                      'placement' => array( 'templatefile' => $relatedTemplateName,
00238                                                                            'start' => array( 'line' => $currentLine,
00239                                                                                              'column' => $currentColumn,
00240                                                                                              'position' => $sourcePosition ),
00241                                                                            'stop' => array( 'line' => $endLine,
00242                                                                                             'column' => $endColumn,
00243                                                                                             'position' => $blockStart ) ) );
00244                             $currentLine = $endLine;
00245                             $currentColumn = $endColumn;
00246                         }
00247 
00248                         $tag = substr( $sourceText, $tagPos, $len );
00249                         $tag = preg_replace( "/\\\\[}]/", "}", $tag );
00250                         $tagTrim = trim( $tag );
00251                         $isEndTag = false;
00252                         $isSingleTag = false;
00253 
00254                         if ( $tag[0] == "/" )
00255                         {
00256                             $isEndTag = true;
00257                             $tag = substr( $tag, 1 );
00258                         }
00259                         else if ( $tagTrim[strlen( $tagTrim ) - 1] == "/" )
00260                         {
00261                             $isSingleTag = true;
00262                             $tagTrim = trim( substr( $tagTrim, 0, strlen( $tagTrim ) - 1 ) );
00263                             $tag = $tagTrim;
00264                         }
00265 
00266                         $this->gotoEndPosition( $tag, $currentLine, $currentColumn, $endLine, $endColumn );
00267                         if ( $tag[0] == "$" or
00268                              $tag[0] == "\"" or
00269                              $tag[0] == "'" or
00270                              is_numeric( $tag[0] ) or
00271                              ( $tag[0] == '-' and
00272                                isset( $tag[1] ) and
00273                                is_numeric( $tag[1] ) ) or
00274                              preg_match( "/^[a-z0-9_-]+\(/", $tag ) )
00275                         {
00276                             $textElements[] = array( "text" => $tag,
00277                                                      "type" => eZTemplate::ELEMENT_VARIABLE,
00278                                                      'placement' => array( 'templatefile' => $relatedTemplateName,
00279                                                                            'start' => array( 'line' => $currentLine,
00280                                                                                              'column' => $currentColumn,
00281                                                                                              'position' => $blockStart + 1 ),
00282                                                                            'stop' => array( 'line' => $endLine,
00283                                                                                             'column' => $endColumn,
00284                                                                                             'position' => $blockEnd - 1 ) ) );
00285                         }
00286                         else
00287                         {
00288                             $type = eZTemplate::ELEMENT_NORMAL_TAG;
00289                             if ( $isEndTag )
00290                                 $type = eZTemplate::ELEMENT_END_TAG;
00291                             else if ( $isSingleTag )
00292                                 $type = eZTemplate::ELEMENT_SINGLE_TAG;
00293                             $spacepos = strpos( $tag, " " );
00294                             if ( $spacepos === false )
00295                                 $name = $tag;
00296                             else
00297                                 $name = substr( $tag, 0, $spacepos );
00298                             if ( isset( $tpl->Literals[$name] ) )
00299                             {
00300                                 $literalEndTag = "{/$name}";
00301                                 $literalEndPos = strpos( $sourceText, $literalEndTag, $blockEnd );
00302                                 if ( $literalEndPos === false )
00303                                     $literalEndPos = $sourceLength;
00304                                 $data = substr( $sourceText, $blockEnd, $literalEndPos - $blockEnd );
00305                                 $this->gotoEndPosition( $data, $currentLine, $currentColumn, $endLine, $endColumn );
00306                                 $blockEnd = $literalEndPos + strlen( $literalEndTag );
00307                                 $textElements[] = array( "text" => $data,
00308                                                          "type" => eZTemplate::ELEMENT_TEXT,
00309                                                          'placement' => false );
00310                             }
00311                             else
00312                                 $textElements[] = array( "text" => $tag,
00313                                                          "name" => $name,
00314                                                          "type" => $type,
00315                                                          'placement' => array( 'templatefile' => $relatedTemplateName,
00316                                                                                'start' => array( 'line' => $currentLine,
00317                                                                                                  'column' => $currentColumn,
00318                                                                                                  'position' => $blockStart + 1 ),
00319                                                                                'stop' => array( 'line' => $endLine,
00320                                                                                                 'column' => $endColumn,
00321                                                                                                 'position' => $blockEnd - 1 ) ) );
00322                         }
00323 
00324                         if ( $sourcePosition < $blockEnd )
00325                             $sourcePosition = $blockEnd;
00326                         $currentLine = $endLine;
00327                         $currentColumn = $endColumn;
00328                     }
00329                 }
00330             }
00331         }
00332         return $textElements;
00333     }
00334 
00335     function parseWhitespaceRemoval( $tpl, &$textElements )
00336     {
00337         if ( $tpl->ShowDetails )
00338             eZDebug::addTimingPoint( "Parse pass 2 (whitespace removal)" );
00339         $tempTextElements = array();
00340         $textElements[] = null;
00341 
00342         $element = false;
00343         foreach( $textElements as $nextElement )
00344         {
00345             if ( $element )
00346             {
00347                 switch ( $element["type"] )
00348                 {
00349                     case eZTemplate::ELEMENT_COMMENT:
00350                     {
00351                         // Ignore comments
00352                     } break;
00353 
00354                     case eZTemplate::ELEMENT_TEXT:
00355                     case eZTemplate::ELEMENT_VARIABLE:
00356                     {
00357                         if ( $nextElement !== null )
00358                         {
00359                             switch ( $nextElement["type"] )
00360                             {
00361                                 case eZTemplate::ELEMENT_END_TAG:
00362                                 case eZTemplate::ELEMENT_SINGLE_TAG:
00363                                 case eZTemplate::ELEMENT_NORMAL_TAG:
00364                                 {
00365                                     $text = $element["text"];
00366                                     $text_cnt = strlen( $text );
00367                                     if ( $text_cnt > 0 )
00368                                     {
00369                                         $char = $text[$text_cnt - 1];
00370                                         if ( $char == "\n" )
00371                                         {
00372                                             $element["text"] = substr( $text, 0, $text_cnt - 1 );
00373                                         }
00374                                     }
00375                                 } break;
00376                             }
00377                         }
00378                         if ( $element["text"] !== '' )
00379                         {
00380                             $tempTextElements[] = $element;
00381                         }
00382                     } break;
00383 
00384                     case eZTemplate::ELEMENT_END_TAG:
00385                     case eZTemplate::ELEMENT_SINGLE_TAG:
00386                     case eZTemplate::ELEMENT_NORMAL_TAG:
00387                     {
00388                         if ( $nextElement !== null )
00389                         {
00390                             switch ( $nextElement["type"] )
00391                             {
00392                                 case eZTemplate::ELEMENT_TEXT:
00393                                 case eZTemplate::ELEMENT_VARIABLE:
00394                                 {
00395                                     $text = $nextElement["text"];
00396                                     $text_cnt = strlen( $text );
00397                                     if ( $text_cnt > 0 )
00398                                     {
00399                                         $char = $text[0];
00400                                         if ( $char == "\n" )
00401                                         {
00402                                             $nextElement["text"] = substr( $text, 1 );
00403                                         }
00404                                     }
00405                                 } break;
00406                             }
00407                         }
00408                         $tempTextElements[] = $element;
00409                     } break;
00410                 }
00411             }
00412             $element = $nextElement;
00413         }
00414         return $tempTextElements;
00415     }
00416 
00417     function appendChild( &$root, &$node )
00418     {
00419         if ( !is_array( $root[1] ) )
00420             $root[1] = array();
00421         $root[1][] =& $node;
00422     }
00423 
00424     function parseIntoTree( $tpl, &$textElements, &$treeRoot,
00425                             $rootNamespace, $relatedResource, $relatedTemplateName )
00426     {
00427         $outerElseTags = array( 'else' => array( 'if', 'elseif' ), 'section-else' => array( 'section' ) );
00428 
00429         $currentRoot =& $treeRoot;
00430         if ( $tpl->ShowDetails )
00431             eZDebug::addTimingPoint( "Parse pass 3 (build tree)" );
00432 
00433         $tagStack = array();
00434 
00435         foreach( $textElements as $elementKey => $element )
00436         {
00437             $elementPlacement = $element['placement'];
00438             $startLine = $elementPlacement['start']['line'];
00439             $startColumn = $elementPlacement['start']['column'];
00440             $startPosition = $elementPlacement['start']['position'];
00441             $stopLine = $elementPlacement['stop']['line'];
00442             $stopColumn = $elementPlacement['stop']['column'];
00443             $stopPosition = $elementPlacement['stop']['position'];
00444             $templateFile = $elementPlacement['templatefile'];
00445             $placement = array( array( $startLine,
00446                                        $startColumn,
00447                                        $startPosition ),
00448                                 array( $stopLine,
00449                                        $stopColumn,
00450                                        $stopPosition ),
00451                                 $templateFile );
00452             switch ( $element["type"] )
00453             {
00454                 case eZTemplate::ELEMENT_TEXT:
00455                 {
00456                     unset( $node );
00457                     $node = array( eZTemplate::NODE_TEXT,
00458                                    false,
00459                                    $element['text'],
00460                                    $placement );
00461                     $this->appendChild( $currentRoot, $node );
00462                 } break;
00463                 case eZTemplate::ELEMENT_VARIABLE:
00464                 {
00465                     $text = $element["text"];
00466                     $text_len = strlen( $text );
00467                     $var_data = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, 0, $var_end, $text_len, $rootNamespace );
00468 
00469                     unset( $node );
00470                     $node = array( eZTemplate::NODE_VARIABLE,
00471                                    false,
00472                                    $var_data,
00473                                    $placement );
00474                     $this->appendChild( $currentRoot, $node );
00475                     if ( $var_end < $text_len )
00476                     {
00477                         $placement = $element['placement'];
00478                         $startLine = $placement['start']['line'];
00479                         $startColumn = $placement['start']['column'];
00480                         $subText = substr( $text, 0, $var_end );
00481                         $this->gotoEndPosition( $subText, $startLine, $startColumn, $currentLine, $currentColumn );
00482                         $tpl->error( "", "parser error @ $relatedTemplateName:$currentLine" . "[$currentColumn]" . "\n" .
00483                                      "Extra characters found in expression, they will be ignored.\n" .
00484                                      substr( $text, $var_end, $text_len - $var_end ) . "' (" . substr( $text, 0, $var_end ) . ")",
00485                                      $placement );
00486                     }
00487                 } break;
00488                 case eZTemplate::ELEMENT_SINGLE_TAG:
00489                 case eZTemplate::ELEMENT_NORMAL_TAG:
00490                 case eZTemplate::ELEMENT_END_TAG:
00491                 {
00492                     $text = $element["text"];
00493                     $text_len = strlen( $text );
00494                     $type = $element["type"];
00495 
00496                     $ident_pos = $this->ElementParser->identifierEndPosition( $tpl, $text, 0, $text_len );
00497                     $tag = substr( $text, 0, $ident_pos - 0 );
00498                     $attr_pos = $ident_pos;
00499                     unset( $args );
00500 
00501                     $args = array();
00502 
00503                     // special handling for some functions having complex syntax
00504                     if ( $type == eZTemplate::ELEMENT_NORMAL_TAG &&
00505                          in_array( $tag, array( 'if', 'elseif', 'while', 'for', 'foreach', 'def', 'undef',
00506                                                 'set', 'let', 'default', 'set-block', 'append-block', 'section' ) ) )
00507                     {
00508                         $attr_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $attr_pos, $text_len );
00509 
00510 
00511                         if ( $tag == 'if' || $tag == 'elseif' )
00512                             $this->parseUnnamedCondition( $tag, $args, $tpl, $text, $text_len, $attr_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
00513                         elseif ( $tag == 'while' )
00514                             $this->parseWhileFunction( $args, $tpl, $text, $text_len, $attr_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
00515                         elseif ( $tag == 'for' )
00516                             $this->parseForFunction( $args, $tpl, $text, $text_len, $attr_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
00517                         elseif ( $tag == 'foreach' )
00518                             $this->parseForeachFunction( $args, $tpl, $text, $text_len, $attr_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
00519                         elseif ( $tag == 'def' || $tag == 'undef' )
00520                             $this->parseDefFunction( $tag, $args, $tpl, $text, $text_len, $attr_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
00521                         elseif ( $tag == 'set' || $tag == 'let' || $tag == 'default' )
00522                             $this->parseSetFunction( $tag, $args, $tpl, $text, $text_len, $attr_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
00523                         elseif ( $tag == 'set-block' || $tag == 'append-block' )
00524                             $this->parseBlockFunction( $tag, $args, $tpl, $text, $text_len, $attr_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
00525                         elseif ( $tag == 'section' )
00526                             $this->parseSectionFunction( $tag, $args, $tpl, $text, $text_len, $attr_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
00527                     }
00528                     elseif ( $type == eZTemplate::ELEMENT_END_TAG && $tag == 'do' )
00529                     {
00530                         $this->parseDoFunction( $args, $tpl, $text, $text_len, $attr_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
00531                     }
00532 
00533                     // other functions having simplier syntax are parsed below
00534                     $lastPosition = false;
00535                     while ( $attr_pos < $text_len )
00536                     {
00537                         if ( $lastPosition !== false and
00538                              $lastPosition == $attr_pos )
00539                         {
00540                             break;
00541                         }
00542                         $lastPosition = $attr_pos;
00543                         $attr_pos_start = $this->ElementParser->whitespaceEndPos( $tpl, $text, $attr_pos, $text_len );
00544                         if ( $attr_pos_start == $attr_pos and
00545                              $attr_pos_start < $text_len )
00546                         {
00547                             $placement = $element['placement'];
00548                             $startLine = $placement['start']['line'];
00549                             $startColumn = $placement['start']['column'];
00550                             $subText = substr( $text, 0, $attr_pos );
00551                             $this->gotoEndPosition( $subText, $startLine, $startColumn, $currentLine, $currentColumn );
00552                             $tpl->error( "", "parser error @ $relatedTemplateName:$currentLine" . "[$currentColumn]" . "\n" .
00553                                          "Extra characters found, should have been a whitespace or the end of the expression\n".
00554                                          "Characters: '" . substr( $text, $attr_pos ) . "'" );
00555                             break;
00556                         }
00557                         $attr_pos = $attr_pos_start;
00558                         $attr_name_pos = $this->ElementParser->identifierEndPosition( $tpl, $text, $attr_pos, $text_len );
00559                         $attr_name = substr( $text, $attr_pos, $attr_name_pos - $attr_pos );
00560 
00561                         /* Skip whitespace between here and the next one */
00562                         $equal_sign_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $attr_name_pos, $text_len );
00563                         if ( ( $equal_sign_pos < $text_len ) && ( $text[$equal_sign_pos] == '=' ) )
00564                         {
00565                             $attr_name_pos = $equal_sign_pos;
00566                         }
00567 
00568                         if ( $attr_name_pos >= $text_len or
00569                              ( $text[$attr_name_pos] != '=' and
00570                                preg_match( "/[ \t\r\n]/", $text[$attr_name_pos] ) ) )
00571                         {
00572                             unset( $var_data );
00573                             $var_data = array();
00574                             $var_data[] = array( eZTemplate::TYPE_NUMERIC, // type
00575                                                  true, // content
00576                                                  false // debug
00577                                                  );
00578                             $args[$attr_name] = $var_data;
00579                             $attr_pos = $attr_name_pos;
00580                             continue;
00581                         }
00582                         if ( $text[$attr_name_pos] != "=" )
00583                         {
00584                             $placement = $element['placement'];
00585                             $startLine = $placement['start']['line'];
00586                             $startColumn = $placement['start']['column'];
00587                             $subText = substr( $text, 0, $attr_name_pos );
00588                             $this->gotoEndPosition( $subText, $startLine, $startColumn, $currentLine, $currentColumn );
00589                             $tpl->error( "", "parser error @ $relatedTemplateName:$currentLine" . "[$currentColumn]\n".
00590                                          "Invalid parameter characters in function '$tag': '" .
00591                                           substr( $text, $attr_name_pos )  . "'" );
00592                             break;
00593                         }
00594                         ++$attr_name_pos;
00595                         unset( $var_data );
00596                         $var_data = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $attr_name_pos, $var_end, $text_len, $rootNamespace );
00597                         $args[$attr_name] = $var_data;
00598                         $attr_pos = $var_end;
00599                     }
00600 
00601                     if ( $type == eZTemplate::ELEMENT_END_TAG and count( $args ) > 0 )
00602                     {
00603                         if ( $tag != 'do' )
00604                         {
00605                             $placement = $element['placement'];
00606                             $startLine = $placement['start']['line'];
00607                             $startColumn = $placement['start']['column'];
00608                             $tpl->error( "", "parser error @ $relatedTemplateName:$startLine" . "[$startColumn]" . "\n" .
00609                                          "End tag \"$tag\" cannot have attributes\n$tpl->LDelim/" . $text . $tpl->RDelim,
00610                                          $element['placement'] );
00611                             $args = array();
00612                         }
00613                     }
00614 
00615                     if ( $type == eZTemplate::ELEMENT_NORMAL_TAG )
00616                     {
00617                         $ignoreCurrentTag = false;
00618                         if( in_array( $tag, array_keys($outerElseTags) ) )  // 'esle'-kind operators
00619                         {
00620                             unset( $oldTag );
00621                             unset( $oldTagName );
00622                             $oldTag = end( $tagStack );
00623                             $oldTagName = $oldTag["Tag"];
00624 
00625                             $ignoreCurrentTag = true;
00626                             if ( in_array( $oldTagName, $outerElseTags[$tag] ) )
00627                             {
00628                                 $ignoreCurrentTag = false;
00629                             }
00630                             else // if there is incorrect 'else' using
00631                             {
00632                                 // checking for 'if' in stack
00633                                 $tagBackStack = $tagStack;
00634                                 $lastIfKey = false;
00635                                 foreach ( $tagBackStack as $prevTagKey => $prevTag )
00636                                 {
00637                                     if ( in_array( $prevTag['Tag'], $outerElseTags[$tag] ) )
00638                                     {
00639                                         $lastIfKey = $prevTagKey;
00640                                     }
00641                                 }
00642 
00643                                 if( $lastIfKey !== false )
00644                                 {
00645                                     // checking for later tags (search for closing tag)
00646                                     $laterElements = array_slice( $textElements, $elementKey + 1 );
00647                                     $laterStack = array();
00648                                     foreach( $laterElements as $laterElement )
00649                                     {
00650                                         if ( $laterElement['type'] == eZTemplate::ELEMENT_NORMAL_TAG)
00651                                         {
00652                                             if( !in_array( $laterElement['name'], array_keys($outerElseTags) ) )
00653                                             {
00654                                                 $laterStack[] = $laterElement['name'];
00655                                             }
00656                                             else
00657                                             {
00658                                                 if ( $laterStack === array() )
00659                                                 {
00660                                                     break; // double else (current else will be ignored)
00661                                                 }
00662                                             }
00663                                         }
00664                                         elseif ( $laterElement['type'] == eZTemplate::ELEMENT_END_TAG )
00665                                         {
00666                                             if ( $laterStack !== array() )
00667                                             {
00668                                                 if ( array_pop( $laterStack ) !== $laterElement['name'] )
00669                                                 {
00670                                                     break;  // later tags mismatch (current else will be ignored)
00671                                                 }
00672                                             }
00673                                             else
00674                                             {
00675                                                 if ( $laterElement['name'] == $oldTagName)
00676                                                 {
00677                                                     break;  // previous tag correctly closed (current else will be ignored)
00678                                                 }
00679                                                 elseif ( in_array( $laterElement['name'], $outerElseTags[$tag] ) )
00680                                                 {
00681                                                     $ignoreCurrentTag = false;
00682                                                     break;  // 'if' tag correctly closed (inner tags must be closed too)
00683                                                 }
00684                                             }
00685                                         }
00686                                     }
00687                                 }
00688 
00689                                 $place = $element['placement'];
00690                                 $startLine = $place['start']['line'];
00691                                 $startColumn = $place['start']['column'];
00692                                 if ( $ignoreCurrentTag )
00693                                 {
00694                                     $tpl->error( "", "parser error @ $relatedTemplateName:$startLine" . "[$startColumn]" . "\n" .
00695                                                  "Unterminated tag \"$oldTagName\" does not match tag \"$tag\". \"$tag\" will be ignored.",
00696                                                  $element['placement'] );
00697                                 }
00698                                 else
00699                                 {
00700                                     unset( $currentRoot );
00701                                     $lastIfKey++;
00702                                     $currentRoot =& $tagStack[$lastIfKey]["Root"];
00703 
00704                                     $autoTerminatedTags = array_slice( $tagStack, $lastIfKey);
00705                                     $autoTerminatedTagNames = array();
00706                                     foreach( $autoTerminatedTags as $autoTerminatedTag)
00707                                         $autoTerminatedTagNames[] = $autoTerminatedTag["Tag"];
00708                                     $tagStack = array_slice( $tagStack, 0, $lastIfKey);
00709 
00710                                     $tpl->error( "", "parser error @ $relatedTemplateName:$startLine" . "[$startColumn]" . "\n" .
00711                                                  "Unterminated tag \"".implode( "\", \"", $autoTerminatedTagNames)."\" does not match tag \"$tag\" and will be autoterminated.",
00712                                                  $element['placement'] );
00713                                     unset( $autoTerminatedTags );
00714                                     unset( $autoTerminatedTagNames );
00715                                 }
00716                                 unset( $lastIfKey );
00717                             }
00718                         }
00719 
00720                         if ( !$ignoreCurrentTag )
00721                         {
00722                             unset( $node );
00723                             $node = array( eZTemplate::NODE_FUNCTION,
00724                                            false,
00725                                            $tag,
00726                                            $args,
00727                                            $placement );
00728                             $this->appendChild( $currentRoot, $node );
00729                             $has_children = true;
00730                             if ( isset( $tpl->FunctionAttributes[$tag] ) )
00731                             {
00732                                 if ( is_array( $tpl->FunctionAttributes[$tag] ) )
00733                                     $tpl->loadAndRegisterFunctions( $tpl->FunctionAttributes[$tag] );
00734                                 $has_children = $tpl->FunctionAttributes[$tag];
00735                             }
00736                             else if ( isset( $tpl->Functions[$tag] ) )
00737                             {
00738                                 if ( is_array( $tpl->Functions[$tag] ) )
00739                                     $tpl->loadAndRegisterFunctions( $tpl->Functions[$tag] );
00740                                 $has_children = $tpl->hasChildren( $tpl->Functions[$tag], $tag );
00741                             }
00742                             if ( $has_children )
00743                             {
00744                                 $tagStack[] = array( "Root" => &$currentRoot,
00745                                                      "Tag" => $tag );
00746                                 unset( $currentRoot );
00747                                 $currentRoot =& $node;
00748                             }
00749                         }
00750                     }
00751                     else if ( $type == eZTemplate::ELEMENT_END_TAG )
00752                     {
00753                         $has_children = true;
00754                         if ( isset( $tpl->FunctionAttributes[$tag] ) )
00755                         {
00756                             if ( is_array( $tpl->FunctionAttributes[$tag] ) )
00757                                 $tpl->loadAndRegisterFunctions( $tpl->FunctionAttributes[$tag] );
00758                             $has_children = $tpl->FunctionAttributes[$tag];
00759                         }
00760                         else if ( isset( $tpl->Functions[$tag] ) )
00761                         {
00762                             if ( is_array( $tpl->Functions[$tag] ) )
00763                                 $tpl->loadAndRegisterFunctions( $tpl->Functions[$tag] );
00764                             $has_children = $tpl->hasChildren( $tpl->Functions[$tag], $tag );
00765                         }
00766                         if ( !$has_children )
00767                         {
00768                             $placement = $element['placement'];
00769                             $startLine = $placement['start']['line'];
00770                             $startColumn = $placement['start']['column'];
00771                             $tpl->error( "", "parser error @ $relatedTemplateName:$startLine" . "[$startColumn]" . "\n" .
00772                                          "End tag \"$tag\" for function which does not accept children, ignoring tag",
00773                                          $element['placement'] );
00774                         }
00775                         else
00776                         {
00777                             unset( $oldTag );
00778                             unset( $oldTagName );
00779                             //include_once( "lib/ezutils/classes/ezphpcreator.php" );
00780                             $oldTag = array_pop( $tagStack );
00781                             $oldTagName = $oldTag["Tag"];
00782                             unset( $currentRoot );
00783                             $currentRoot =& $oldTag["Root"];
00784 
00785                             if ( $oldTagName != $tag )
00786                             {
00787                                 $placement = $element['placement'];
00788                                 $startLine = $placement['start']['line'];
00789                                 $startColumn = $placement['start']['column'];
00790                                 $tpl->error( "", "parser error @ $relatedTemplateName:$startLine" . "[$startColumn]" . "\n" .
00791                                              "Unterminated tag \"$oldTagName\" does not match tag \"$tag\"",
00792                                              $element['placement'] );
00793                             }
00794 
00795                             // a dirty hack to pass arguments specified in {/do} to the corresponding function.
00796                             if ( $tag == 'do' )
00797                             {
00798                                 $doOpenTag =& $currentRoot[1][count( $currentRoot[1] ) - 1];
00799                                 $doOpenTag[3] =& $args;
00800                             }
00801                         }
00802                     }
00803                     else // eZTemplate::ELEMENT_SINGLE_TAG
00804                     {
00805                         unset( $node );
00806                         $node = array( eZTemplate::NODE_FUNCTION,
00807                                        false,
00808                                        $tag,
00809                                        $args,
00810                                        $placement );
00811                         $this->appendChild( $currentRoot, $node );
00812                     }
00813                     unset( $tag );
00814 
00815                 } break;
00816             }
00817         }
00818 
00819         if ( $tpl->ShowDetails )
00820             eZDebug::addTimingPoint( "Parse pass 3 done" );
00821     }
00822 
00823     /*!
00824      * parse 'sequence' loop parameter: "sequence <array> as <$seqVar>"
00825      */
00826     function parseSequenceParameter( $parseSequenceKeyword, $funcName, &$args, $tpl, &$text, &$text_len, &$cur_pos,
00827                                      $relatedTemplateName, $startLine, $startColumn, $rootNamespace )
00828     {
00829         if ( $parseSequenceKeyword )
00830         {
00831             // parse 'sequence' keyword
00832             $sequenceEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $cur_pos, $text_len );
00833             $sequence = substr( $text, $cur_pos, $sequenceEndPos-$cur_pos );
00834             if ( $sequence != 'sequence' )
00835             {
00836                 $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
00837                                               $funcName, "Expected keyword 'sequence' not found" );
00838                 return false;
00839             }
00840             $cur_pos = $sequenceEndPos;
00841 
00842             // skip whitespaces
00843             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
00844         }
00845 
00846         // parse sequence array
00847         $args['sequence_array'] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
00848 
00849         // skip whitespaces
00850         $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
00851 
00852         // parse 'as' keyword
00853         $asEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $cur_pos, $text_len );
00854         $word = substr( $text, $cur_pos, $asEndPos-$cur_pos );
00855         if ( $word != 'as' )
00856         {
00857             $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
00858                               $funcName, "Expected keyword 'as' not found" );
00859             unset( $args['sequence_array'] );
00860             return false;
00861         }
00862         $cur_pos = $asEndPos;
00863 
00864         // skip whitespaces
00865         $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
00866 
00867         // parse sequence variable
00868         $seqVar = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
00869         if ( !$seqVar )
00870         {
00871             $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
00872                                           $funcName, "Sequence variable name cannot be empty" );
00873             unset( $args['sequence_array'] );
00874             return false;
00875         }
00876         $args['sequence_var'] = $seqVar;
00877 
00878         return true;
00879     }
00880 
00881     /*!
00882     Parse {for} function.
00883     Syntax:
00884     \code
00885     // for <firstValue> to <lastValue> as <$loopVar> [sequence <array> as <$var>]
00886     \endcode
00887     */
00888     function parseForFunction( &$args, $tpl, &$text, &$text_len, &$cur_pos,
00889                                $relatedTemplateName, $startLine, $startColumn, $rootNamespace )
00890     {
00891         $firstValStartPos = $cur_pos;
00892 
00893         // parse first value
00894         $firstVal = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $firstValStartPos, $firstValEndPos, $text_len, $rootNamespace
00895                                                              /*, eZTemplate::TYPE_NUMERIC_BIT | eZTemplate::TYPE_VARIABLE_BIT*/ );
00896         $args['first_val'] = $firstVal;
00897 
00898         $toStartPos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $firstValEndPos, $text_len );
00899 
00900         // parse 'to'
00901         $toEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $toStartPos, $text_len );
00902         $to = substr( $text, $toStartPos, $toEndPos-$toStartPos );
00903         if ( $to != 'to' )
00904         {
00905             $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
00906                                           'for', "Expected keyword 'to' not found" );
00907             return;
00908         }
00909 
00910 
00911         $lastValStartPos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $toEndPos, $text_len );
00912 
00913         // parse last value
00914         $args['last_val'] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $lastValStartPos, $lastValEndPos, $text_len, $rootNamespace );
00915 
00916         $asStartPos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $lastValEndPos, $text_len );
00917 
00918         // parse 'as'
00919         $asEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $asStartPos, $text_len );
00920         $as = substr( $text, $asStartPos, $asEndPos-$asStartPos );
00921         if ( $as != 'as' )
00922         {
00923             $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
00924                                           'for', "Expected keyword 'as' not found" );
00925             return;
00926         }
00927 
00928         $loopVarStartPos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $asEndPos, $text_len );
00929 
00930         // parse loop variable
00931         $args['loop_var'] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $loopVarStartPos, $loopVarEndPos, $text_len, $rootNamespace );
00932 
00933         if ( $loopVarEndPos == $text_len  ) // no more parameters
00934             $cur_pos = $loopVarEndPos;
00935         else
00936         {
00937             // skip whitespaces
00938             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $loopVarEndPos, $text_len );
00939 
00940             if ( ! $this->parseSequenceParameter( true, 'for',
00941                                $args, $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace ) )
00942                 return;
00943         }
00944     }
00945 
00946     /*!
00947     Parse {foreach} function.
00948     Syntax:
00949     \code
00950     {foreach <array> as [$keyVar =>] $itemVar
00951              [sequence <array> as $sequenceVar]
00952              [offset <offset>]
00953              [max <max>]
00954              [reverse]
00955     }
00956     \endcode
00957     */
00958     function parseForeachFunction( &$args, $tpl, &$text, &$text_len, &$cur_pos,
00959                                    $relatedTemplateName, $startLine, $startColumn, $rootNamespace )
00960     {
00961         // parse array
00962         $args['array'] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
00963 
00964         // skip whitespaces
00965         $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
00966 
00967         // parse 'as' keyword
00968         $asEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $cur_pos, $text_len );
00969         $word = substr( $text, $cur_pos, $asEndPos-$cur_pos );
00970         if ( $word != 'as' )
00971         {
00972             $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
00973                                           'foreach', "Expected keyword 'as' not found" );
00974             return;
00975         }
00976         $cur_pos = $asEndPos;
00977 
00978         // skip whitespaces
00979         $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
00980 
00981         // parse variable name
00982         $var1 = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
00983 
00984         $nextTokenPos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
00985 
00986         // parse itemVar (if specified)
00987         if ( $nextTokenPos <= ( $text_len-2 ) && $text[$nextTokenPos] == '=' && $text[$nextTokenPos+1] == '>' )
00988         {
00989             // skip whitespaces
00990             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $nextTokenPos+2, $text_len );
00991 
00992             $args['key_var']  = $var1;
00993 
00994             // parse item variable name
00995             $args['item_var'] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
00996         }
00997         else
00998             $args['item_var'] = $var1;
00999 
01000         /*
01001          * parse optional parameters
01002          */
01003 
01004         while ( $cur_pos < $text_len )
01005         {
01006             // skip whitespaces
01007             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01008 
01009             $paramNameEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $cur_pos, $text_len );
01010             $paramName = substr( $text, $cur_pos, $paramNameEndPos-$cur_pos );
01011             $cur_pos = $paramNameEndPos;
01012 
01013             if ( $paramName == 'sequence' )
01014             {
01015                 if ( ! $this->parseSequenceParameter( false, 'foreach',
01016                                                $args, $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn, $rootNamespace ) )
01017                     return;
01018             }
01019             elseif ( $paramName == 'offset' )
01020             {
01021                 $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01022                 $args['offset'] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
01023             }
01024             elseif ( $paramName == 'max' )
01025             {
01026                 $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01027                 $args['max'] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
01028             }
01029             elseif ( $paramName == 'reverse' )
01030             {
01031                 $reverseValText = '1';
01032                 $args['reverse'] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $reverseValText, 0, $reverseValPos, 1, $rootNamespace );
01033             }
01034             else
01035             {
01036                 $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01037                                               'foreach', "Unknown parameter '$paramName'" );
01038                 return;
01039             }
01040         }
01041     }
01042 
01043     /*!
01044     Parse do..while function
01045     Syntax:
01046     \code
01047 
01048     {do}
01049         [{delimiter}...{/delimiter}]
01050         [{break}]
01051         [{continue}]
01052         [{skip}]
01053     {/do while <condition> [sequence <array> as $seqVar]}
01054     */
01055     function parseDoFunction( &$args, $tpl, &$text, &$text_len, &$cur_pos,
01056                               $relatedTemplateName, $startLine, $startColumn, $rootNamespace )
01057     {
01058         // skip whitespaces
01059         $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01060 
01061         // parse while keyword
01062         $wordEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $cur_pos, $text_len );
01063         $word = substr( $text, $cur_pos, $wordEndPos-$cur_pos );
01064         if ( $word != 'while' )
01065         {
01066             $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01067                                           'do', "Expected keyword 'while' not found in parameters" );
01068             return;
01069         }
01070         $cur_pos = $wordEndPos;
01071 
01072         // skip whitespaces
01073         $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01074 
01075         $args['condition'] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
01076 
01077         // skip whitespaces
01078         $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01079 
01080         if ( $cur_pos == $text_len ) // no more arguments
01081             return;
01082 
01083         $this->parseSequenceParameter( true, 'do',
01084                                        $args, $tpl, $text, $text_len, $cur_pos,
01085                                        $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
01086     }
01087 
01088 
01089     /*!
01090     Parse def/undef functions
01091     Syntax:
01092     \code
01093         {def $var1=<value1> [$var2=<value2> ...]}
01094         {undef [$var1 [$var2] ...]}
01095     \endcode
01096     */
01097 
01098     function parseDefFunction( $funcName, &$args, $tpl, &$text, &$text_len, &$cur_pos,
01099                                $relatedTemplateName, $startLine, $startColumn, $rootNamespace )
01100     {
01101         if ( $cur_pos == $text_len && $funcName == 'def' ) // no more arguments
01102         {
01103             $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01104                                           $funcName, 'Not enough arguments' );
01105             return;
01106         }
01107 
01108         while ( $cur_pos < $text_len )
01109         {
01110             // parse variable name
01111             if ( $text[$cur_pos] != '$' )
01112             {
01113                 $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01114                                               $funcName, '($) expected' );
01115                 return;
01116             }
01117 
01118             $cur_pos++;
01119             $wordEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $cur_pos, $text_len );
01120             $varName = substr( $text, $cur_pos, $wordEndPos-$cur_pos );
01121             $cur_pos = $wordEndPos;
01122 
01123             if ( !$varName )
01124             {
01125                 $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01126                                               $funcName, 'Empty variable name' );
01127                 return;
01128             }
01129 
01130             if ( $funcName == 'def' )
01131             {
01132                 // skip whitespaces
01133                 $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01134 
01135                 // parse variable value
01136                 if ( $cur_pos >= $text_len || $text[$cur_pos] != '=' )
01137                 {
01138                     $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01139                                                   $funcName, '(=) expected' );
01140                     return;
01141                 }
01142                 $cur_pos++;
01143 
01144                 // skip whitespaces
01145                 $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01146 
01147                 $args[$varName] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
01148             }
01149             else
01150             {
01151                 $varValueText = '1';
01152                 $args[$varName] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $varValueText, 0, $varValPos, 1, $rootNamespace );
01153             }
01154 
01155 
01156             // skip whitespaces
01157             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01158         }
01159     }
01160 
01161     /*!
01162     Parse arguments for {if}/{elseif}
01163     */
01164     function parseUnnamedCondition( $funcName, &$args, $tpl, &$text, &$text_len, &$cur_pos,
01165                                     $relatedTemplateName, $startLine, $startColumn, $rootNamespace )
01166     {
01167         $cond = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
01168         if ( !count( $cond ) )
01169         {
01170             $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01171                                           $funcName, 'Not enough arguments' );
01172             return;
01173 
01174         }
01175         $args['condition'] = $cond;
01176     }
01177 
01178     /*!
01179     Parse arguments for {while}
01180     */
01181     function parseWhileFunction( &$args, $tpl, &$text, &$text_len, &$cur_pos,
01182                                  $relatedTemplateName, $startLine, $startColumn, $rootNamespace )
01183     {
01184         $cond = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
01185         if ( !count( $cond ) )
01186         {
01187             $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01188                                           'while', 'Not enough arguments' );
01189             return;
01190 
01191         }
01192         $args['condition'] = $cond;
01193 
01194         // skip whitespaces
01195         $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01196 
01197         if ( $cur_pos == $text_len ) // no more arguments
01198             return;
01199 
01200         $this->parseSequenceParameter( true, 'while',
01201                                        $args, $tpl, $text, $text_len, $cur_pos,
01202                                        $relatedTemplateName, $startLine, $startColumn, $rootNamespace );
01203     }
01204 
01205     /*!
01206     Parse arguments for {set}/{let}/{default}
01207     */
01208     function parseSetFunction( $funcName, &$args, $tpl, &$text, &$text_len, &$cur_pos,
01209                                $relatedTemplateName, $startLine, $startColumn, $rootNamespace )
01210     {
01211         while ( $cur_pos < $text_len )
01212         {
01213             $dollarSignFound = false;
01214 
01215             // skip optional dollar sign
01216             if ( $text[$cur_pos] == '$' )
01217             {
01218                 $dollarSignFound = true;
01219                 $cur_pos++;
01220             }
01221 
01222             // parse variable name
01223             $wordEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $cur_pos, $text_len );
01224             $varName = substr( $text, $cur_pos, $wordEndPos-$cur_pos );
01225             $cur_pos = $wordEndPos;
01226 
01227             if ( !$varName )
01228             {
01229                 $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01230                                               $funcName, 'Empty variable name' );
01231                 return;
01232             }
01233 
01234             /*
01235              * If parameter 'name' or 'scope' was passed without dollar sign
01236              * change its name to '-name' or '-scope', respectively.
01237              * In this case it is treated specially in the {set}/{let}/{default} functions.
01238              */
01239             if ( !$dollarSignFound && ( $varName == 'name' || $varName == 'scope' ) )
01240                 $varName = "-$varName";
01241 
01242             // skip whitespaces
01243             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01244 
01245             // if no value, assume boolean true
01246             if ( $cur_pos >= $text_len or
01247                  ( $text[$cur_pos] != '=' /*and preg_match( "/[ \t\r\n]/", $text[$cur_pos] )*/ ) )
01248             {
01249                 unset( $var_data );
01250                 $var_data = array();
01251                 $args[$varName] = array( array( eZTemplate::TYPE_NUMERIC, // type
01252                                                 true, // content
01253                                                 false // debug
01254                                                 ) );
01255                 continue;
01256             }
01257 
01258             if ( $cur_pos >= $text_len )
01259                 break;
01260 
01261             $cur_pos++;
01262 
01263             // skip whitespaces
01264             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01265 
01266             $args[$varName] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
01267 
01268             // skip whitespaces
01269             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01270         }
01271     }
01272 
01273     /*!
01274     Parse arguments for {set-block}/{append-block}.
01275     This method has been created to correctly handle the case when ($) is used in variable name, e.g. {set-block variable=$var}
01276     Here we strip the dollar sign and pass the variable name as string.
01277     */
01278     function parseBlockFunction( $funcName, &$args, $tpl, &$text, &$text_len, &$cur_pos,
01279                                  $relatedTemplateName, $startLine, $startColumn, $rootNamespace )
01280     {
01281         while ( $cur_pos < $text_len )
01282         {
01283             // parse parameter name
01284             $wordEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $cur_pos, $text_len );
01285             $paramName  = substr( $text, $cur_pos, $wordEndPos-$cur_pos );
01286             $cur_pos    = $wordEndPos;
01287             if ( !$paramName )
01288             {
01289                 $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01290                                               $funcName, 'Empty parameter name' );
01291                 return;
01292             }
01293 
01294             // skip whitespaces
01295             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01296 
01297             // skip (=)
01298             if ( $text[$cur_pos] != '=' )
01299             {
01300                 $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01301                                               $funcName, '(=) expected' );
01302                 return;
01303             }
01304             $cur_pos++;
01305 
01306             // skip whitespaces
01307             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01308 
01309             // skip optional dollar sign
01310             if ( $paramName == 'variable' && $cur_pos < $text_len && $text[$cur_pos] == '$' )
01311             {
01312                 $cur_pos++;
01313             }
01314 
01315             // parse parameter value
01316             $args[$paramName] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
01317 
01318             // skip whitespaces
01319             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01320         }
01321     }
01322 
01323     /*!
01324     Parse arguments for {section}.
01325     This method has been created to correctly handle the case when ($) is used in variable name, e.g. {section var=$item}
01326     Here we strip the dollar sign and pass the variable name as string.
01327     */
01328     function parseSectionFunction( $funcName, &$args, $tpl, &$text, &$text_len, &$cur_pos,
01329                                    $relatedTemplateName, $startLine, $startColumn, $rootNamespace )
01330     {
01331         while ( $cur_pos < $text_len )
01332         {
01333             // parse parameter name
01334             $wordEndPos = $this->ElementParser->identifierEndPosition( $tpl, $text, $cur_pos, $text_len );
01335             $paramName  = substr( $text, $cur_pos, $wordEndPos-$cur_pos );
01336             $cur_pos    = $wordEndPos;
01337             if ( !$paramName )
01338             {
01339                 $this->showParseErrorMessage( $tpl, $text, $text_len, $cur_pos, $relatedTemplateName, $startLine, $startColumn,
01340                                               $funcName, 'Empty parameter name' );
01341                 return;
01342             }
01343 
01344             // skip whitespaces
01345             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01346 
01347             // skip (=)
01348             if ( $cur_pos >= $text_len || $text[$cur_pos] != '=' ) // if the parameter has no value, i.e. not followed by '=<value>'
01349             {
01350                 // the parameter gets boolean true value.
01351                 $args[$paramName] = array( array( eZTemplate::TYPE_NUMERIC, // type
01352                                                   true, // content
01353                                                   false // debug
01354                                                   ) );
01355                 continue;
01356             }
01357             $cur_pos++;
01358 
01359             // skip whitespaces
01360             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01361 
01362             // skip optional dollar sign that is allowed in value of 'var' parameter
01363             if ( $paramName == 'var' && $cur_pos < $text_len && $text[$cur_pos] == '$' )
01364             {
01365                 $cur_pos++;
01366             }
01367 
01368             // parse parameter value
01369             $args[$paramName] = $this->ElementParser->parseVariableTag( $tpl, $relatedTemplateName, $text, $cur_pos, $cur_pos, $text_len, $rootNamespace );
01370 
01371             // skip whitespaces
01372             $cur_pos = $this->ElementParser->whitespaceEndPos( $tpl, $text, $cur_pos, $text_len );
01373         }
01374     }
01375 
01376     function showParseErrorMessage( $tpl, &$text, $text_len, &$cur_pos, $tplName, $startLine, $startColumn, $funcName, $message )
01377     {
01378         $subText = substr( $text, 0, $cur_pos );
01379         $this->gotoEndPosition( $subText, $startLine, $startColumn, $currentLine, $currentColumn );
01380         $tpl->error( $funcName, "parser error @ $tplName:$currentLine\n" .
01381                      "$message at [" . substr( $text, $cur_pos ) . "]" );
01382         $cur_pos = $text_len;
01383     }
01384 
01385     static function instance()
01386     {
01387         if ( !isset( $GLOBALS['eZTemplateMultiPassParserInstance'] ) ||
01388              !( $GLOBALS['eZTemplateMultiPassParserInstance'] instanceof eZTemplateMultiPassParser ) )
01389         {
01390             $GLOBALS['eZTemplateMultiPassParserInstance'] = new eZTemplateMultiPassParser();
01391         }
01392 
01393         return $GLOBALS['eZTemplateMultiPassParserInstance'];
01394     }
01395 
01396     /// \privatesection
01397     public $ElementParser;
01398 }
01399 
01400 ?>