TYPO3 API  SVNRelease
TemplateParser.php
Go to the documentation of this file.
00001 <?php
00002 
00003 /*                                                                        *
00004  * This script belongs to the FLOW3 package "Fluid".                      *
00005  *                                                                        *
00006  * It is free software; you can redistribute it and/or modify it under    *
00007  * the terms of the GNU Lesser General Public License as published by the *
00008  * Free Software Foundation, either version 3 of the License, or (at your *
00009  * option) any later version.                                             *
00010  *                                                                        *
00011  * This script is distributed in the hope that it will be useful, but     *
00012  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *
00013  * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser       *
00014  * General Public License for more details.                               *
00015  *                                                                        *
00016  * You should have received a copy of the GNU Lesser General Public       *
00017  * License along with the script.                                         *
00018  * If not, see http://www.gnu.org/licenses/lgpl.html                      *
00019  *                                                                        *
00020  * The TYPO3 project - inspiring people to share!                         *
00021  *                                                                        */
00022 
00023 /**
00024  * Template parser building up an object syntax tree
00025  *
00026  * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
00027  */
00028 class Tx_Fluid_Core_Parser_TemplateParser implements t3lib_Singleton {
00029 
00030     public static $SCAN_PATTERN_NAMESPACEDECLARATION = '/(?<!\\\\){namespace\s*([a-zA-Z]+[a-zA-Z0-9]*)\s*=\s*((?:F3|Tx|t3lib|tslib)(?:FLUID_NAMESPACE_SEPARATOR\w+)+)\s*}/m';
00031 
00032     /**
00033      * This regular expression splits the input string at all dynamic tags, AND
00034      * on all <![CDATA[...]]> sections.
00035      *
00036      * @author Sebastian Kurfürst <sebastian@typo3.org>
00037      */
00038     public static $SPLIT_PATTERN_TEMPLATE_DYNAMICTAGS = '/
00039         (
00040             (?: <\/?                                      # Start dynamic tags
00041                     (?:(?:NAMESPACE):[a-zA-Z0-9\\.]+)     # A tag consists of the namespace prefix and word characters
00042                     (?:                                   # Begin tag arguments
00043                         \s*[a-zA-Z0-9:]+                  # Argument Keys
00044                         =                                 # =
00045                         (?>                               # either... If we have found an argument, we will not back-track (That does the Atomic Bracket)
00046                             "(?:\\\"|[^"])*"              # a double-quoted string
00047                             |\'(?:\\\\\'|[^\'])*\'        # or a single quoted string
00048                         )\s*                              #
00049                     )*                                    # Tag arguments can be replaced many times.
00050                 \s*
00051                 \/?>                                      # Closing tag
00052             )
00053             |(?:                                          # Start match CDATA section
00054                 <!\[CDATA\[.*?\]\]>
00055             )
00056         )/xs';
00057 
00058     /**
00059      * This regular expression scans if the input string is a ViewHelper tag
00060      *
00061      * @author Sebastian Kurfürst <sebastian@typo3.org>
00062      */
00063     public static $SCAN_PATTERN_TEMPLATE_VIEWHELPERTAG = '/
00064         ^<                                                # A Tag begins with <
00065         (?P<NamespaceIdentifier>NAMESPACE):               # Then comes the Namespace prefix followed by a :
00066         (?P<MethodIdentifier>                             # Now comes the Name of the ViewHelper
00067             [a-zA-Z0-9\\.]+
00068         )
00069         (?P<Attributes>                                   # Begin Tag Attributes
00070             (?:                                           # A tag might have multiple attributes
00071                 \s*
00072                 [a-zA-Z0-9:]+                             # The attribute name
00073                 =                                         # =
00074                 (?>                                       # either... # If we have found an argument, we will not back-track (That does the Atomic Bracket)
00075                     "(?:\\\"|[^"])*"                      # a double-quoted string
00076                     |\'(?:\\\\\'|[^\'])*\'                # or a single quoted string
00077                 )                                         #
00078                 \s*
00079             )*                                            # A tag might have multiple attributes
00080         )                                                 # End Tag Attributes
00081         \s*
00082         (?P<Selfclosing>\/?)                              # A tag might be selfclosing
00083         >$/x';
00084 
00085     /**
00086      * This regular expression scans if the input string is a closing ViewHelper
00087      * tag.
00088      *
00089      * @author Sebastian Kurfürst <sebastian@typo3.org>
00090      */
00091     public static $SCAN_PATTERN_TEMPLATE_CLOSINGVIEWHELPERTAG = '/^<\/(?P<NamespaceIdentifier>NAMESPACE):(?P<MethodIdentifier>[a-zA-Z0-9\\.]+)\s*>$/';
00092 
00093     /**
00094      * This regular expression splits the tag arguments into its parts
00095      *
00096      * @author Sebastian Kurfürst <sebastian@typo3.org>
00097      */
00098     public static $SPLIT_PATTERN_TAGARGUMENTS = '/
00099         (?:                                              #
00100             \s*                                          #
00101             (?P<Argument>                                # The attribute name
00102                 [a-zA-Z0-9:]+                            #
00103             )                                            #
00104             =                                            # =
00105             (?>                                          # If we have found an argument, we will not back-track (That does the Atomic Bracket)
00106                 (?P<ValueQuoted>                         # either...
00107                     (?:"(?:\\\"|[^"])*")                 # a double-quoted string
00108                     |(?:\'(?:\\\\\'|[^\'])*\')           # or a single quoted string
00109                 )
00110             )\s*
00111         )
00112         /xs';
00113 
00114     /**
00115      * This pattern detects CDATA sections and outputs the text between opening
00116      * and closing CDATA.
00117      *
00118      * @author Sebastian Kurfürst <sebastian@typo3.org>
00119      */
00120     public static $SCAN_PATTERN_CDATA = '/^<!\[CDATA\[(.*?)\]\]>$/s';
00121 
00122     /**
00123      * Pattern which splits the shorthand syntax into different tokens. The
00124      * "shorthand syntax" is everything like {...}
00125      *
00126      * @author Sebastian Kurfürst <sebastian@typo3.org>
00127      */
00128     public static $SPLIT_PATTERN_SHORTHANDSYNTAX = '/
00129         (
00130             {                                # Start of shorthand syntax
00131                 (?:                          # Shorthand syntax is either composed of...
00132                     [a-zA-Z0-9\->_:,.()]     # Various characters
00133                     |"(?:\\\"|[^"])*"        # Double-quoted strings
00134                     |\'(?:\\\\\'|[^\'])*\'   # Single-quoted strings
00135                     |(?R)                    # Other shorthand syntaxes inside, albeit not in a quoted string
00136                     |\s+                     # Spaces
00137                 )+
00138             }                                # End of shorthand syntax
00139         )/x';
00140 
00141     /**
00142      * Pattern which detects the object accessor syntax:
00143      * {object.some.value}, additionally it detects ViewHelpers like
00144      * {f:for(param1:bla)} and chaining like
00145      * {object.some.value->f:bla.blubb()->f:bla.blubb2()}
00146      *
00147      * THIS IS ALMOST THE SAME AS IN $SCAN_PATTERN_SHORTHANDSYNTAX_ARRAYS
00148      *
00149      * @author Sebastian Kurfürst <sebastian@typo3.org>
00150      */
00151     public static $SCAN_PATTERN_SHORTHANDSYNTAX_OBJECTACCESSORS = '/
00152         ^{                                                      # Start of shorthand syntax
00153                                                             # A shorthand syntax is either...
00154             (?P<Object>[a-zA-Z0-9\-_.]*)                                     # ... an object accessor
00155             \s*(?P<Delimiter>(?:->)?)\s*
00156 
00157             (?P<ViewHelper>                                 # ... a ViewHelper
00158                 [a-zA-Z0-9]+                                # Namespace prefix of ViewHelper (as in $SCAN_PATTERN_TEMPLATE_VIEWHELPERTAG)
00159                 :
00160                 [a-zA-Z0-9\\.]+                             # Method Identifier (as in $SCAN_PATTERN_TEMPLATE_VIEWHELPERTAG)
00161                 \(                                          # Opening parameter brackets of ViewHelper
00162                     (?P<ViewHelperArguments>                # Start submatch for ViewHelper arguments. This is taken from $SCAN_PATTERN_SHORTHANDSYNTAX_ARRAYS
00163                         (?:
00164                             \s*[a-zA-Z0-9\-_]+                  # The keys of the array
00165                             \s*:\s*                             # Key|Value delimiter :
00166                             (?:                                 # Possible value options:
00167                                 "(?:\\\"|[^"])*"                # Double qouoted string
00168                                 |\'(?:\\\\\'|[^\'])*\'          # Single quoted string
00169                                 |[a-zA-Z0-9\-_.]+               # variable identifiers
00170                                 |{(?P>ViewHelperArguments)}     # Another sub-array
00171                             )                                   # END possible value options
00172                             \s*,?                               # There might be a , to seperate different parts of the array
00173                         )*                                  # The above cycle is repeated for all array elements
00174                     )                                       # End ViewHelper Arguments submatch
00175                 \)                                          # Closing parameter brackets of ViewHelper
00176             )?
00177             (?P<AdditionalViewHelpers>                      # There can be more than one ViewHelper chained, by adding more -> and the ViewHelper (recursively)
00178                 (?:
00179                     \s*->\s*
00180                     (?P>ViewHelper)
00181                 )*
00182             )
00183         }$/x';
00184 
00185     /**
00186      * THIS IS ALMOST THE SAME AS $SCAN_PATTERN_SHORTHANDSYNTAX_OBJECTACCESSORS
00187      *
00188      * @author Sebastian Kurfürst <sebastian@typo3.org>
00189      */
00190     public static $SPLIT_PATTERN_SHORTHANDSYNTAX_VIEWHELPER = '/
00191 
00192         (?P<NamespaceIdentifier>[a-zA-Z0-9]+)       # Namespace prefix of ViewHelper (as in $SCAN_PATTERN_TEMPLATE_VIEWHELPERTAG)
00193         :
00194         (?P<MethodIdentifier>[a-zA-Z0-9\\.]+)
00195         \(                                          # Opening parameter brackets of ViewHelper
00196             (?P<ViewHelperArguments>                # Start submatch for ViewHelper arguments. This is taken from $SCAN_PATTERN_SHORTHANDSYNTAX_ARRAYS
00197                 (?:
00198                     \s*[a-zA-Z0-9\-_]+                  # The keys of the array
00199                     \s*:\s*                             # Key|Value delimiter :
00200                     (?:                                 # Possible value options:
00201                         "(?:\\\"|[^"])*"                # Double qouoted string
00202                         |\'(?:\\\\\'|[^\'])*\'          # Single quoted string
00203                         |[a-zA-Z0-9\-_.]+               # variable identifiers
00204                         |{(?P>ViewHelperArguments)}     # Another sub-array
00205                     )                                   # END possible value options
00206                     \s*,?                               # There might be a , to seperate different parts of the array
00207                 )*                                  # The above cycle is repeated for all array elements
00208             )                                       # End ViewHelper Arguments submatch
00209         \)                                          # Closing parameter brackets of ViewHelper
00210         /x';
00211 
00212     /**
00213      * Pattern which detects the array/object syntax like in JavaScript, so it
00214      * detects strings like:
00215      * {object: value, object2: {nested: array}, object3: "Some string"}
00216      *
00217      * THIS IS ALMOST THE SAME AS IN SCAN_PATTERN_SHORTHANDSYNTAX_OBJECTACCESSORS
00218      *
00219      * @author Sebastian Kurfürst <sebastian@typo3.org>
00220      */
00221     public static $SCAN_PATTERN_SHORTHANDSYNTAX_ARRAYS = '/^
00222         (?P<Recursion>                                  # Start the recursive part of the regular expression - describing the array syntax
00223             {                                           # Each array needs to start with {
00224                 (?P<Array>                              # Start submatch
00225                     (?:
00226                         \s*[a-zA-Z0-9\-_]+              # The keys of the array
00227                         \s*:\s*                         # Key|Value delimiter :
00228                         (?:                             # Possible value options:
00229                             "(?:\\\"|[^"])*"            # Double qouoted string
00230                             |\'(?:\\\\\'|[^\'])*\'      # Single quoted string
00231                             |[a-zA-Z0-9\-_.]+           # variable identifiers
00232                             |(?P>Recursion)             # Another sub-array
00233                         )                               # END possible value options
00234                         \s*,?                           # There might be a , to seperate different parts of the array
00235                     )*                                  # The above cycle is repeated for all array elements
00236                 )                                       # End array submatch
00237             }                                           # Each array ends with }
00238         )$/x';
00239 
00240     /**
00241      * This pattern splits an array into its parts. It is quite similar to the
00242      * pattern above.
00243      *
00244      * @author Sebastian Kurfürst <sebastian@typo3.org>
00245      */
00246     public static $SPLIT_PATTERN_SHORTHANDSYNTAX_ARRAY_PARTS = '/
00247         (?P<ArrayPart>                                             # Start submatch
00248             (?P<Key>[a-zA-Z0-9\-_]+)                               # The keys of the array
00249             \s*:\s*                                                   # Key|Value delimiter :
00250             (?:                                                       # Possible value options:
00251                 (?P<QuotedString>                                     # Quoted string
00252                     (?:"(?:\\\"|[^"])*")
00253                     |(?:\'(?:\\\\\'|[^\'])*\')
00254                 )
00255                 |(?P<VariableIdentifier>[a-zA-Z][a-zA-Z0-9\-_.]*)    # variable identifiers have to start with a letter
00256                 |(?P<Number>[0-9.]+)                                  # Number
00257                 |{\s*(?P<Subarray>(?:(?P>ArrayPart)\s*,?\s*)+)\s*}              # Another sub-array
00258             )                                                         # END possible value options
00259         )                                                          # End array part submatch
00260     /x';
00261 
00262     /**
00263      * Namespace identifiers and their component name prefix (Associative array).
00264      * @var array
00265      */
00266     protected $namespaces = array(
00267         'f' => 'Tx_Fluid_ViewHelpers'
00268     );
00269 
00270     /**
00271      * @var Tx_Extbase_Object_ObjectManagerInterface
00272      */
00273     protected $objectManager;
00274 
00275     /**
00276      * @var Tx_Fluid_Core_Parser_Configuration
00277      */
00278     protected $configuration;
00279 
00280     /**
00281      * Constructor. Preprocesses the $SCAN_PATTERN_NAMESPACEDECLARATION by
00282      * inserting the correct namespace separator.
00283      *
00284      * @author Sebastian Kurfürst <sebastian@typo3.org>
00285      */
00286     public function __construct() {
00287         self::$SCAN_PATTERN_NAMESPACEDECLARATION = str_replace('FLUID_NAMESPACE_SEPARATOR', preg_quote(Tx_Fluid_Fluid::NAMESPACE_SEPARATOR), self::$SCAN_PATTERN_NAMESPACEDECLARATION);
00288     }
00289 
00290     /**
00291      * @param Tx_Extbase_Object_ObjectManagerInterface $objectManager
00292      * @return void
00293      * @author Sebastian Kurfürst <sebastian@typo3.org>
00294      */
00295     public function injectObjectManager(Tx_Extbase_Object_ObjectManagerInterface $objectManager) {
00296         $this->objectManager = $objectManager;
00297     }
00298 
00299     /**
00300      * Set the configuration for the parser.
00301      *
00302      * @param Tx_Fluid_Core_Parser_Configuration $configuration
00303      * @return void
00304      * @author Karsten Dambekalns <karsten@typo3.org>
00305      */
00306     public function setConfiguration(Tx_Fluid_Core_Parser_Configuration $configuration = NULL) {
00307         $this->configuration = $configuration;
00308     }
00309 
00310     /**
00311      * Parses a given template and returns a parsed template object.
00312      *
00313      * @param string $templateString The template to parse as a string
00314      * @return Tx_Fluid_Core_Parser_ParsedTemplateInterface Parsed template
00315      * @author Sebastian Kurfürst <sebastian@typo3.org>
00316      * @todo Refine doc comment
00317      */
00318     public function parse($templateString) {
00319         if (!is_string($templateString)) throw new Tx_Fluid_Core_Parser_Exception('Parse requires a template string as argument, ' . gettype($templateString) . ' given.', 1224237899);
00320 
00321         $this->reset();
00322 
00323         $templateString = $this->extractNamespaceDefinitions($templateString);
00324         $splitTemplate = $this->splitTemplateAtDynamicTags($templateString);
00325         $parsingState = $this->buildObjectTree($splitTemplate);
00326 
00327         return $parsingState;
00328     }
00329 
00330     /**
00331      * Gets the namespace definitions found.
00332      *
00333      * @return array Namespace identifiers and their component name prefix
00334      * @author Sebastian Kurfürst <sebastian@typo3.org>
00335      */
00336     public function getNamespaces() {
00337         return $this->namespaces;
00338     }
00339 
00340     /**
00341      * Resets the parser to its default values.
00342      *
00343      * @return void
00344      * @author Sebastian Kurfürst <sebastian@typo3.org>
00345      */
00346     protected function reset() {
00347         $this->namespaces = array(
00348             'f' => 'Tx_Fluid_ViewHelpers'
00349         );
00350     }
00351 
00352     /**
00353      * Extracts namespace definitions out of the given template string and sets
00354      * $this->namespaces.
00355      *
00356      * @param string $templateString Template string to extract the namespaces from
00357      * @return string The updated template string without namespace declarations inside
00358      * @author Sebastian Kurfürst <sebastian@typo3.org>
00359      */
00360     protected function extractNamespaceDefinitions($templateString) {
00361         $matchedVariables = array();
00362         if (preg_match_all(self::$SCAN_PATTERN_NAMESPACEDECLARATION, $templateString, $matchedVariables) > 0) {
00363             foreach (array_keys($matchedVariables[0]) as $index) {
00364                 $namespaceIdentifier = $matchedVariables[1][$index];
00365                 $fullyQualifiedNamespace = $matchedVariables[2][$index];
00366                 if (key_exists($namespaceIdentifier, $this->namespaces)) {
00367                     throw new Tx_Fluid_Core_Parser_Exception('Namespace identifier "' . $namespaceIdentifier . '" is already registered. Do not redeclare namespaces!', 1224241246);
00368                 }
00369                 $this->namespaces[$namespaceIdentifier] = $fullyQualifiedNamespace;
00370             }
00371 
00372             $templateString = preg_replace(self::$SCAN_PATTERN_NAMESPACEDECLARATION, '', $templateString);
00373         }
00374         return $templateString;
00375     }
00376 
00377     /**
00378      * Splits the template string on all dynamic tags found.
00379      *
00380      * @param string $templateString Template string to split.
00381      * @return array Splitted template
00382      * @author Sebastian Kurfürst <sebastian@typo3.org>
00383      */
00384     protected function splitTemplateAtDynamicTags($templateString) {
00385         $regularExpression = $this->prepareTemplateRegularExpression(self::$SPLIT_PATTERN_TEMPLATE_DYNAMICTAGS);
00386         return preg_split($regularExpression, $templateString, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
00387     }
00388 
00389     /**
00390      * Build object tree from the split template
00391      *
00392      * @param array $splitTemplate The split template, so that every tag with a namespace declaration is already a seperate array element.
00393      * @return Tx_Fluid_Core_Parser_ParsingState
00394      * @author Sebastian Kurfürst <sebastian@typo3.org>
00395      */
00396     protected function buildObjectTree($splitTemplate) {
00397         $regularExpression_openingViewHelperTag = $this->prepareTemplateRegularExpression(self::$SCAN_PATTERN_TEMPLATE_VIEWHELPERTAG);
00398         $regularExpression_closingViewHelperTag = $this->prepareTemplateRegularExpression(self::$SCAN_PATTERN_TEMPLATE_CLOSINGVIEWHELPERTAG);
00399 
00400         $state = $this->objectManager->create('Tx_Fluid_Core_Parser_ParsingState');
00401         $rootNode = $this->objectManager->create('Tx_Fluid_Core_Parser_SyntaxTree_RootNode');
00402         $state->setRootNode($rootNode);
00403         $state->pushNodeToStack($rootNode);
00404 
00405         $state->setVariableContainer($this->objectManager->create('Tx_Fluid_Core_ViewHelper_TemplateVariableContainer'));
00406 
00407         foreach ($splitTemplate as $templateElement) {
00408             $matchedVariables = array();
00409             if (preg_match(self::$SCAN_PATTERN_CDATA, $templateElement, $matchedVariables) > 0) {
00410                 $this->textHandler($state, $matchedVariables[1]);
00411             } elseif (preg_match($regularExpression_openingViewHelperTag, $templateElement, $matchedVariables) > 0) {
00412                 $this->openingViewHelperTagHandler($state, $matchedVariables['NamespaceIdentifier'], $matchedVariables['MethodIdentifier'], $matchedVariables['Attributes'], ($matchedVariables['Selfclosing'] === '' ? FALSE : TRUE));
00413             } elseif (preg_match($regularExpression_closingViewHelperTag, $templateElement, $matchedVariables) > 0) {
00414                 $this->closingViewHelperTagHandler($state, $matchedVariables['NamespaceIdentifier'], $matchedVariables['MethodIdentifier']);
00415             } else {
00416                 $this->textAndShorthandSyntaxHandler($state, $templateElement);
00417             }
00418         }
00419 
00420         if ($state->countNodeStack() !== 1) {
00421             throw new Tx_Fluid_Core_Parser_Exception('Not all tags were closed!', 1238169398);
00422         }
00423         return $state;
00424     }
00425 
00426     /**
00427      * Handles an opening or self-closing view helper tag.
00428      *
00429      * @param Tx_Fluid_Core_Parser_ParsingState $state Current parsing state
00430      * @param string $namespaceIdentifier Namespace identifier - being looked up in $this->namespaces
00431      * @param string $methodIdentifier Method identifier
00432      * @param string $arguments Arguments string, not yet parsed
00433      * @param boolean $selfclosing true, if the tag is a self-closing tag.
00434      * @return void
00435      * @author Sebastian Kurfürst <sebastian@typo3.org>
00436      */
00437     protected function openingViewHelperTagHandler(Tx_Fluid_Core_Parser_ParsingState $state, $namespaceIdentifier, $methodIdentifier, $arguments, $selfclosing) {
00438         $argumentsObjectTree = $this->parseArguments($arguments);
00439         $this->initializeViewHelperAndAddItToStack($state, $namespaceIdentifier, $methodIdentifier, $argumentsObjectTree);
00440 
00441         if ($selfclosing) {
00442             $node = $state->popNodeFromStack();
00443             $this->callInterceptor($node, Tx_Fluid_Core_Parser_InterceptorInterface::INTERCEPT_CLOSING_VIEWHELPER);
00444         }
00445     }
00446 
00447     /**
00448      * Initialize the given ViewHelper and adds it to the current node and to
00449      * the stack.
00450      *
00451      * @param Tx_Fluid_Core_Parser_ParsingState $state Current parsing state
00452      * @param string $namespaceIdentifier Namespace identifier - being looked up in $this->namespaces
00453      * @param string $methodIdentifier Method identifier
00454      * @param array $argumentsObjectTree Arguments object tree
00455      * @return void
00456      * @author Sebastian Kurfürst <sebastian@typo3.org>
00457      * @author Karsten Dambekalns <karsten@typo3.org>
00458      */
00459     protected function initializeViewHelperAndAddItToStack(Tx_Fluid_Core_Parser_ParsingState $state, $namespaceIdentifier, $methodIdentifier, $argumentsObjectTree) {
00460         if (!array_key_exists($namespaceIdentifier, $this->namespaces)) {
00461             throw new Tx_Fluid_Core_Parser_Exception('Namespace could not be resolved. This exception should never be thrown!', 1224254792);
00462         }
00463 
00464         $viewHelper = $this->objectManager->create($this->resolveViewHelperName($namespaceIdentifier, $methodIdentifier));
00465         $expectedViewHelperArguments = $viewHelper->prepareArguments();
00466         $this->abortIfUnregisteredArgumentsExist($expectedViewHelperArguments, $argumentsObjectTree);
00467         $this->abortIfRequiredArgumentsAreMissing($expectedViewHelperArguments, $argumentsObjectTree);
00468 
00469         $currentDynamicNode = $this->objectManager->create('Tx_Fluid_Core_Parser_SyntaxTree_ViewHelperNode', $viewHelper, $argumentsObjectTree);
00470 
00471         $state->getNodeFromStack()->addChildNode($currentDynamicNode);
00472 
00473             // PostParse Facet
00474         if ($viewHelper instanceof Tx_Fluid_Core_ViewHelper_Facets_PostParseInterface) {
00475             // Don't just use $viewHelper::postParseEvent(...),
00476             // as this will break with PHP < 5.3.
00477             call_user_func(array($viewHelper, 'postParseEvent'), $currentDynamicNode, $argumentsObjectTree, $state->getVariableContainer());
00478         }
00479 
00480         $this->callInterceptor($currentDynamicNode, Tx_Fluid_Core_Parser_InterceptorInterface::INTERCEPT_OPENING_VIEWHELPER);
00481 
00482         $state->pushNodeToStack($currentDynamicNode);
00483     }
00484 
00485     /**
00486      * Throw an exception if there are arguments which were not registered
00487      * before.
00488      *
00489      * @param array $expectedArguments Array of Tx_Fluid_Core_ViewHelper_ArgumentDefinition of all expected arguments
00490      * @param array $actualArguments Actual arguments
00491      * @throws Tx_Fluid_Core_Parser_Exception
00492      * @author Sebastian Kurfürst <sebastian@typo3.org>
00493      */
00494     protected function abortIfUnregisteredArgumentsExist($expectedArguments, $actualArguments) {
00495         $expectedArgumentNames = array();
00496         foreach ($expectedArguments as $expectedArgument) {
00497             $expectedArgumentNames[] = $expectedArgument->getName();
00498         }
00499 
00500         foreach (array_keys($actualArguments) as $argumentName) {
00501             if (!in_array($argumentName, $expectedArgumentNames)) {
00502                 throw new Tx_Fluid_Core_Parser_Exception('Argument "' . $argumentName . '" was not registered.', 1237823695);
00503             }
00504         }
00505     }
00506 
00507     /**
00508      * Throw an exception if required arguments are missing
00509      *
00510      * @param array $expectedArguments Array of Tx_Fluid_Core_ViewHelper_ArgumentDefinition of all expected arguments
00511      * @param array $actualArguments Actual arguments
00512      * @throws Tx_Fluid_Core_Parser_Exception
00513      * @author Sebastian Kurfürst <sebastian@typo3.org>
00514      */
00515     protected function abortIfRequiredArgumentsAreMissing($expectedArguments, $actualArguments) {
00516         $actualArgumentNames = array_keys($actualArguments);
00517         foreach ($expectedArguments as $expectedArgument) {
00518             if ($expectedArgument->isRequired() && !in_array($expectedArgument->getName(), $actualArgumentNames)) {
00519                 throw new Tx_Fluid_Core_Parser_Exception('Required argument "' . $expectedArgument->getName() . '" was not supplied.', 1237823699);
00520             }
00521         }
00522     }
00523 
00524     /**
00525      * Resolve a viewhelper name.
00526      *
00527      * @param string $namespaceIdentifier Namespace identifier for the view helper.
00528      * @param string $methodIdentifier Method identifier, might be hierarchical like "link.url"
00529      * @return string The fully qualified class name of the viewhelper
00530      * @author Sebastian Kurfürst <sebastian@typo3.org>
00531      */
00532     protected function resolveViewHelperName($namespaceIdentifier, $methodIdentifier) {
00533         $explodedViewHelperName = explode('.', $methodIdentifier);
00534         $className = '';
00535         if (count($explodedViewHelperName) > 1) {
00536             $className = implode(Tx_Fluid_Fluid::NAMESPACE_SEPARATOR, array_map('ucfirst', $explodedViewHelperName));
00537         } else {
00538             $className = ucfirst($explodedViewHelperName[0]);
00539         }
00540         $className .= 'ViewHelper';
00541 
00542         $name = $this->namespaces[$namespaceIdentifier] . Tx_Fluid_Fluid::NAMESPACE_SEPARATOR . $className;
00543 
00544         return $name;
00545     }
00546 
00547     /**
00548      * Handles a closing view helper tag
00549      *
00550      * @param Tx_Fluid_Core_Parser_ParsingState $state The current parsing state
00551      * @param string $namespaceIdentifier Namespace identifier for the closing tag.
00552      * @param string $methodIdentifier Method identifier.
00553      * @return void
00554      * @throws Tx_Fluid_Core_Parser_Exception
00555      * @author Sebastian Kurfürst <sebastian@typo3.org>
00556      */
00557     protected function closingViewHelperTagHandler(Tx_Fluid_Core_Parser_ParsingState $state, $namespaceIdentifier, $methodIdentifier) {
00558         if (!array_key_exists($namespaceIdentifier, $this->namespaces)) {
00559             throw new Tx_Fluid_Core_Parser_Exception('Namespace could not be resolved. This exception should never be thrown!', 1224256186);
00560         }
00561         $lastStackElement = $state->popNodeFromStack();
00562         if (!($lastStackElement instanceof Tx_Fluid_Core_Parser_SyntaxTree_ViewHelperNode)) {
00563             throw new Tx_Fluid_Core_Parser_Exception('You closed a templating tag which you never opened!', 1224485838);
00564         }
00565         if ($lastStackElement->getViewHelperClassName() != $this->resolveViewHelperName($namespaceIdentifier, $methodIdentifier)) {
00566             throw new Tx_Fluid_Core_Parser_Exception('Templating tags not properly nested. Expected: ' . $lastStackElement->getViewHelperClassName() . '; Actual: ' . $this->resolveViewHelperName($namespaceIdentifier, $methodIdentifier), 1224485398);
00567         }
00568         $this->callInterceptor($lastStackElement, Tx_Fluid_Core_Parser_InterceptorInterface::INTERCEPT_CLOSING_VIEWHELPER);
00569     }
00570 
00571     /**
00572      * Handles the appearance of an object accessor (like {posts.author.email}).
00573      * Creates a new instance of Tx_Fluid_ObjectAccessorNode.
00574      *
00575      * Handles ViewHelpers as well which are in the shorthand syntax.
00576      *
00577      * @param Tx_Fluid_Core_Parser_ParsingState $state The current parsing state
00578      * @param string $objectAccessorString String which identifies which objects to fetch
00579      * @param string $delimiter
00580      * @param string $viewHelperString
00581      * @param string $additionalViewHelpersString
00582      * @return void
00583      * @author Sebastian Kurfürst <sebastian@typo3.org>
00584      */
00585     protected function objectAccessorHandler(Tx_Fluid_Core_Parser_ParsingState $state, $objectAccessorString, $delimiter, $viewHelperString, $additionalViewHelpersString) {
00586         $viewHelperString .= $additionalViewHelpersString;
00587         $numberOfViewHelpers = 0;
00588 
00589             // The following post-processing handles a case when there is only a ViewHelper, and no Object Accessor.
00590             // Resolves bug #5107.
00591         if (strlen($delimiter) === 0 && strlen($viewHelperString) > 0) {
00592             $viewHelperString = $objectAccessorString . $viewHelperString;
00593             $objectAccessorString = '';
00594         }
00595 
00596             // ViewHelpers
00597         $matches = array();
00598         if (strlen($viewHelperString) > 0 && preg_match_all(self::$SPLIT_PATTERN_SHORTHANDSYNTAX_VIEWHELPER, $viewHelperString, $matches, PREG_SET_ORDER) > 0) {
00599                 // The last ViewHelper has to be added first for correct chaining.
00600             foreach (array_reverse($matches) as $singleMatch) {
00601                 if (strlen($singleMatch['ViewHelperArguments']) > 0) {
00602                     $arguments = $this->postProcessArgumentsForObjectAccessor(
00603                         $this->recursiveArrayHandler($singleMatch['ViewHelperArguments'])
00604                     );
00605                 } else {
00606                     $arguments = array();
00607                 }
00608                 $this->initializeViewHelperAndAddItToStack($state, $singleMatch['NamespaceIdentifier'], $singleMatch['MethodIdentifier'], $arguments);
00609                 $numberOfViewHelpers++;
00610             }
00611         }
00612 
00613             // Object Accessor
00614         if (strlen($objectAccessorString) > 0) {
00615 
00616             $node = $this->objectManager->create('Tx_Fluid_Core_Parser_SyntaxTree_ObjectAccessorNode', $objectAccessorString);
00617             $this->callInterceptor($node, Tx_Fluid_Core_Parser_InterceptorInterface::INTERCEPT_OBJECTACCESSOR);
00618 
00619             $state->getNodeFromStack()->addChildNode($node);
00620         }
00621 
00622             // Close ViewHelper Tags if needed.
00623         for ($i=0; $i<$numberOfViewHelpers; $i++) {
00624             $node = $state->popNodeFromStack();
00625             $this->callInterceptor($node, Tx_Fluid_Core_Parser_InterceptorInterface::INTERCEPT_CLOSING_VIEWHELPER);
00626         }
00627     }
00628 
00629     /**
00630      * Call all interceptors registered for a given interception point.
00631      *
00632      * @param Tx_Fluid_Core_Parser_SyntaxTree_NodeInterface $node The syntax tree node which can be modified by the interceptors.
00633      * @param integer $interceptionPoint the interception point. One of the Tx_Fluid_Core_Parser_InterceptorInterface::INTERCEPT_* constants.
00634      * @return void
00635      * @author Sebastian Kurfürst <sebastian@typo3.org>
00636      */
00637     protected function callInterceptor(Tx_Fluid_Core_Parser_SyntaxTree_NodeInterface &$node, $interceptionPoint) {
00638         if ($this->configuration !== NULL) {
00639             // $this->configuration is UNSET inside the arguments of a ViewHelper.
00640             // That's why the interceptors are only called if the object accesor is not inside a ViewHelper Argument
00641             // This could be a problem if We have a ViewHelper as an argument to another ViewHelper, and an ObjectAccessor nested inside there.
00642             // TODO: Clean up this.
00643             $interceptors = $this->configuration->getInterceptors($interceptionPoint);
00644             if (count($interceptors) > 0) {
00645                 foreach($interceptors as $interceptor) {
00646                     $node = $interceptor->process($node, $interceptionPoint);
00647                 }
00648             }
00649         }
00650     }
00651 
00652     /**
00653      * Post process the arguments for the ViewHelpers in the object accessor
00654      * syntax. We need to convert an array into an array of (only) nodes
00655      *
00656      * @param array $arguments The arguments to be processed
00657      * @return array the processed array
00658      * @author Sebastian Kurfürst <sebastian@typo3.org>
00659      * @todo This method should become superflous once the rest has been refactored, so that this code is not needed.
00660      */
00661     protected function postProcessArgumentsForObjectAccessor(array $arguments) {
00662         foreach ($arguments as $argumentName => $argumentValue) {
00663             if (!($argumentValue instanceof Tx_Fluid_Core_Parser_SyntaxTree_AbstractNode)) {
00664                 $arguments[$argumentName] = $this->objectManager->create('Tx_Fluid_Core_Parser_SyntaxTree_TextNode', (string)$argumentValue);
00665             }
00666         }
00667         return $arguments;
00668     }
00669 
00670     /**
00671      * Parse arguments of a given tag, and build up the Arguments Object Tree
00672      * for each argument.
00673      * Returns an associative array, where the key is the name of the argument,
00674      * and the value is a single Argument Object Tree.
00675      *
00676      * @param string $argumentsString All arguments as string
00677      * @return array An associative array of objects, where the key is the argument name.
00678      * @author Sebastian Kurfürst <sebastian@typo3.org>
00679      */
00680     protected function parseArguments($argumentsString) {
00681         $argumentsObjectTree = array();
00682         $matches = array();
00683         if (preg_match_all(self::$SPLIT_PATTERN_TAGARGUMENTS, $argumentsString, $matches, PREG_SET_ORDER) > 0) {
00684             $configurationBackup = $this->configuration;
00685             $this->configuration = NULL;
00686             foreach ($matches as $singleMatch) {
00687                 $argument = $singleMatch['Argument'];
00688                 $value = $this->unquoteString($singleMatch['ValueQuoted']);
00689                 $argumentsObjectTree[$argument] = $this->buildArgumentObjectTree($value);
00690             }
00691             $this->configuration = $configurationBackup;
00692         }
00693         return $argumentsObjectTree;
00694     }
00695 
00696     /**
00697      * Build up an argument object tree for the string in $argumentString.
00698      * This builds up the tree for a single argument value.
00699      *
00700      * This method also does some performance optimizations, so in case
00701      * no { or < is found, then we just return a TextNode.
00702      *
00703      * @param string $argumentString
00704      * @return ArgumentObject the corresponding argument object tree.
00705      * @author Sebastian Kurfürst <sebastian@typo3.org>
00706      */
00707     protected function buildArgumentObjectTree($argumentString) {
00708         if (strstr($argumentString, '{') === FALSE && strstr($argumentString, '<') === FALSE) {
00709             return $this->objectManager->create('Tx_Fluid_Core_Parser_SyntaxTree_TextNode', $argumentString);
00710         }
00711         $splitArgument = $this->splitTemplateAtDynamicTags($argumentString);
00712         $rootNode = $this->buildObjectTree($splitArgument)->getRootNode();
00713         return $rootNode;
00714     }
00715 
00716     /**
00717      * Removes escapings from a given argument string and trims the outermost
00718      * quotes.
00719      *
00720      * This method is meant as a helper for regular expression results.
00721      *
00722      * @param string $quotedValue Value to unquote
00723      * @return string Unquoted value
00724      * @author Sebastian Kurfürst <sebastian@typo3.org>
00725      * @author Karsten Dambekalns <karsten@typo3.org>
00726      */
00727     protected function unquoteString($quotedValue) {
00728         switch ($quotedValue[0]) {
00729             case '"':
00730                 $value = str_replace('\"', '"', trim($quotedValue, '"'));
00731             break;
00732             case "'":
00733                 $value = str_replace("\'", "'", trim($quotedValue, "'"));
00734             break;
00735         }
00736         return str_replace('\\\\', '\\', $value);
00737     }
00738 
00739     /**
00740      * Takes a regular expression template and replaces "NAMESPACE" with the
00741      * currently registered namespace identifiers. Returns a regular expression
00742      * which is ready to use.
00743      *
00744      * @param string $regularExpression Regular expression template
00745      * @return string Regular expression ready to be used
00746      * @author Sebastian Kurfürst <sebastian@typo3.org>
00747      */
00748     protected function prepareTemplateRegularExpression($regularExpression) {
00749         return str_replace('NAMESPACE', implode('|', array_keys($this->namespaces)), $regularExpression);
00750     }
00751 
00752     /**
00753      * Handler for everything which is not a ViewHelperNode.
00754      *
00755      * This includes Text, array syntax, and object accessor syntax.
00756      *
00757      * @param Tx_Fluid_Core_Parser_ParsingState $state Current parsing state
00758      * @param string $text Text to process
00759      * @return void
00760      * @author Sebastian Kurfürst <sebastian@typo3.org>
00761      */
00762     protected function textAndShorthandSyntaxHandler(Tx_Fluid_Core_Parser_ParsingState $state, $text) {
00763         $sections = preg_split($this->prepareTemplateRegularExpression(self::$SPLIT_PATTERN_SHORTHANDSYNTAX), $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
00764 
00765         foreach ($sections as $section) {
00766             $matchedVariables = array();
00767             if (preg_match(self::$SCAN_PATTERN_SHORTHANDSYNTAX_OBJECTACCESSORS, $section, $matchedVariables) > 0) {
00768                 $this->objectAccessorHandler($state, $matchedVariables['Object'], $matchedVariables['Delimiter'], (isset($matchedVariables['ViewHelper'])?$matchedVariables['ViewHelper']:''), (isset($matchedVariables['AdditionalViewHelpers'])?$matchedVariables['AdditionalViewHelpers']:''));
00769             } elseif (preg_match(self::$SCAN_PATTERN_SHORTHANDSYNTAX_ARRAYS, $section, $matchedVariables) > 0) {
00770                 $this->arrayHandler($state, $matchedVariables['Array']);
00771             } else {
00772                 $this->textHandler($state, $section);
00773             }
00774         }
00775     }
00776 
00777     /**
00778      * Handler for array syntax. This creates the array object recursively and
00779      * adds it to the current node.
00780      *
00781      * @param Tx_Fluid_Core_Parser_ParsingState $state The current parsing state
00782      * @param string $arrayText The array as string.
00783      * @return void
00784      * @author Sebastian Kurfürst <sebastian@typo3.org>
00785      */
00786     protected function arrayHandler(Tx_Fluid_Core_Parser_ParsingState $state, $arrayText) {
00787         $state->getNodeFromStack()->addChildNode(
00788             $this->objectManager->create('Tx_Fluid_Core_Parser_SyntaxTree_ArrayNode', $this->recursiveArrayHandler($arrayText))
00789         );
00790     }
00791 
00792     /**
00793      * Recursive function which takes the string representation of an array and
00794      * builds an object tree from it.
00795      *
00796      * Deals with the following value types:
00797      * - Numbers (Integers and Floats)
00798      * - Strings
00799      * - Variables
00800      * - sub-arrays
00801      *
00802      * @param string $arrayText Array text
00803      * @return Tx_Fluid_ArrayNode the array node built up
00804      * @author Sebastian Kurfürst <sebastian@typo3.org>
00805      */
00806     protected function recursiveArrayHandler($arrayText) {
00807         $matches = array();
00808         if (preg_match_all(self::$SPLIT_PATTERN_SHORTHANDSYNTAX_ARRAY_PARTS, $arrayText, $matches, PREG_SET_ORDER) > 0) {
00809             $arrayToBuild = array();
00810             foreach ($matches as $singleMatch) {
00811                 $arrayKey = $singleMatch['Key'];
00812                 if (!empty($singleMatch['VariableIdentifier'])) {
00813                     $arrayToBuild[$arrayKey] = $this->objectManager->create('Tx_Fluid_Core_Parser_SyntaxTree_ObjectAccessorNode', $singleMatch['VariableIdentifier']);
00814                 } elseif (array_key_exists('Number', $singleMatch) && ( !empty($singleMatch['Number']) || $singleMatch['Number'] === '0' ) ) {
00815                     $arrayToBuild[$arrayKey] = floatval($singleMatch['Number']);
00816                 } elseif ( ( array_key_exists('QuotedString', $singleMatch) && !empty($singleMatch['QuotedString']) ) ) {
00817                     $argumentString = $this->unquoteString($singleMatch['QuotedString']);
00818                     $arrayToBuild[$arrayKey] = $this->buildArgumentObjectTree($argumentString);
00819                 } elseif ( array_key_exists('Subarray', $singleMatch) && !empty($singleMatch['Subarray'])) {
00820                     $arrayToBuild[$arrayKey] = $this->objectManager->create('Tx_Fluid_Core_Parser_SyntaxTree_ArrayNode', $this->recursiveArrayHandler($singleMatch['Subarray']));
00821                 } else {
00822                     throw new Tx_Fluid_Core_Parser_Exception('This exception should never be thrown, as the array value has to be of some type (Value given: "' . var_export($singleMatch, TRUE) . '"). Please post your template to the bugtracker at forge.typo3.org.', 1225136013);
00823                 }
00824             }
00825             return $arrayToBuild;
00826         } else {
00827             throw new Tx_Fluid_Core_Parser_Exception('This exception should never be thrown, there is most likely some error in the regular expressions. Please post your template to the bugtracker at forge.typo3.org.', 1225136013);
00828         }
00829     }
00830 
00831     /**
00832      * Text node handler
00833      *
00834      * @param Tx_Fluid_Core_Parser_ParsingState $state
00835      * @param string $text
00836      * @return void
00837      * @author Sebastian Kurfürst <sebastian@typo3.org>
00838      * @author Karsten Dambekalns <karsten@typo3.org>
00839      */
00840     protected function textHandler(Tx_Fluid_Core_Parser_ParsingState $state, $text) {
00841         $node = $this->objectManager->create('Tx_Fluid_Core_Parser_SyntaxTree_TextNode', $text);
00842         $this->callInterceptor($node, Tx_Fluid_Core_Parser_InterceptorInterface::INTERCEPT_TEXT);
00843 
00844         $state->getNodeFromStack()->addChildNode($node);
00845     }
00846 
00847 }
00848 ?>