|
TYPO3 API
SVNRelease
|
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 * Node which will call a ViewHelper associated with this node. 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_SyntaxTree_ViewHelperNode extends Tx_Fluid_Core_Parser_SyntaxTree_AbstractNode { 00029 00030 /** 00031 * Class name of view helper 00032 * @var string 00033 */ 00034 protected $viewHelperClassName; 00035 00036 /** 00037 * Arguments of view helper - References to RootNodes. 00038 * @var array 00039 */ 00040 protected $arguments = array(); 00041 00042 /** 00043 * The ViewHelper associated with this node 00044 * @var Tx_Fluid_Core_ViewHelper_AbstractViewHelper 00045 */ 00046 protected $uninitializedViewHelper = NULL; 00047 00048 /** 00049 * A mapping RenderingContext -> ViewHelper to only re-initialize ViewHelpers 00050 * when a context change occurs. 00051 * @var Tx_Extbase_Persistence_ObjectStorage 00052 */ 00053 protected $viewHelpersByContext = NULL; 00054 00055 /** 00056 * List of comparators which are supported in the boolean expression language. 00057 * 00058 * Make sure that if one string is contained in one another, the longer 00059 * string is listed BEFORE the shorter one. 00060 * Example: put ">=" before ">" 00061 * @var array of comparators 00062 */ 00063 static protected $comparators = array('==', '!=', '%', '>=', '>', '<=', '<'); 00064 00065 /** 00066 * A regular expression which checks the text nodes of a boolean expression. 00067 * Used to define how the regular expression language should look like. 00068 * @var string Regular expression 00069 */ 00070 static protected $booleanExpressionTextNodeCheckerRegularExpression = '/ 00071 ^ # Start with first input symbol 00072 (?: # start repeat 00073 COMPARATORS # We allow all comparators 00074 |\s* # Arbitary spaces 00075 |-? # Numbers, possibly with the "minus" symbol in front. 00076 [0-9]+ # some digits 00077 (?: # and optionally a dot, followed by some more digits 00078 \\. 00079 [0-9]+ 00080 )? 00081 )* 00082 $/x'; 00083 00084 /** 00085 * Constructor. 00086 * 00087 * @param Tx_Fluid_Core_ViewHelper_AbstractViewHelper $viewHelper The view helper 00088 * @param array $arguments Arguments of view helper - each value is a RootNode. 00089 * @author Sebastian Kurfürst <sebastian@typo3.org> 00090 * @author Karsten Dambekalns <karsten@typo3.org> 00091 */ 00092 public function __construct(Tx_Fluid_Core_ViewHelper_AbstractViewHelper $viewHelper, array $arguments) { 00093 $this->uninitializedViewHelper = $viewHelper; 00094 $this->viewHelpersByContext = t3lib_div::makeInstance('Tx_Extbase_Persistence_ObjectStorage'); 00095 $this->arguments = $arguments; 00096 00097 if (FALSE /*FIXME*/) { 00098 $this->viewHelperClassName = $this->uninitializedViewHelper->FLOW3_AOP_Proxy_getProxyTargetClassName(); 00099 } else { 00100 $this->viewHelperClassName = get_class($this->uninitializedViewHelper); 00101 } 00102 } 00103 00104 /** 00105 * Returns the attached (but still uninitialized) ViewHelper for this ViewHelperNode. 00106 * We need this method because sometimes Interceptors need to ask some information from the ViewHelper. 00107 * 00108 * @return Tx_Fluid_Core_ViewHelper_AbstractViewHelper the attached ViewHelper, if it is initialized 00109 */ 00110 public function getUninitializedViewHelper() { 00111 return $this->uninitializedViewHelper; 00112 } 00113 00114 /** 00115 * Get class name of view helper 00116 * 00117 * @return string Class Name of associated view helper 00118 * @author Sebastian Kurfürst <sebastian@typo3.org> 00119 */ 00120 public function getViewHelperClassName() { 00121 return $this->viewHelperClassName; 00122 } 00123 00124 /** 00125 * Call the view helper associated with this object. 00126 * 00127 * First, it evaluates the arguments of the view helper. 00128 * 00129 * If the view helper implements Tx_Fluid_Core_ViewHelper_Facets_ChildNodeAccessInterface, 00130 * it calls setChildNodes(array childNodes) on the view helper. 00131 * 00132 * Afterwards, checks that the view helper did not leave a variable lying around. 00133 * 00134 * @param Tx_Fluid_Core_Rendering_RenderingContextInterface $renderingContext 00135 * @return object evaluated node after the view helper has been called. 00136 * @author Sebastian Kurfürst <sebastian@typo3.org> 00137 * @author Karsten Dambekalns <karsten@typo3.org> 00138 * @todo check recreation of viewhelper when revisiting caching 00139 */ 00140 public function evaluate(Tx_Fluid_Core_Rendering_RenderingContextInterface $renderingContext) { 00141 $objectManager = $renderingContext->getObjectManager(); 00142 $contextVariables = $renderingContext->getTemplateVariableContainer()->getAllIdentifiers(); 00143 00144 if ($this->viewHelpersByContext->contains($renderingContext)) { 00145 $viewHelper = $this->viewHelpersByContext[$renderingContext]; 00146 } else { 00147 $viewHelper = clone $this->uninitializedViewHelper; 00148 $this->viewHelpersByContext->attach($renderingContext, $viewHelper); 00149 } 00150 00151 $evaluatedArguments = array(); 00152 $renderMethodParameters = array(); 00153 if (count($viewHelper->prepareArguments())) { 00154 foreach ($viewHelper->prepareArguments() as $argumentName => $argumentDefinition) { 00155 if (isset($this->arguments[$argumentName])) { 00156 $argumentValue = $this->arguments[$argumentName]; 00157 $evaluatedArguments[$argumentName] = $this->convertArgumentValue($argumentValue, $argumentDefinition->getType(), $renderingContext); 00158 } else { 00159 $evaluatedArguments[$argumentName] = $argumentDefinition->getDefaultValue(); 00160 } 00161 if ($argumentDefinition->isMethodParameter()) { 00162 $renderMethodParameters[$argumentName] = $evaluatedArguments[$argumentName]; 00163 } 00164 } 00165 } 00166 00167 $viewHelperArguments = $objectManager->create('Tx_Fluid_Core_ViewHelper_Arguments', $evaluatedArguments); 00168 $viewHelper->setArguments($viewHelperArguments); 00169 $viewHelper->setTemplateVariableContainer($renderingContext->getTemplateVariableContainer()); 00170 if ($renderingContext->getControllerContext() !== NULL) { 00171 $viewHelper->setControllerContext($renderingContext->getControllerContext()); 00172 } 00173 $viewHelper->setViewHelperVariableContainer($renderingContext->getViewHelperVariableContainer()); 00174 $viewHelper->setViewHelperNode($this); 00175 $viewHelper->setRenderingContext($renderingContext); 00176 00177 if ($viewHelper instanceof Tx_Fluid_Core_ViewHelper_Facets_ChildNodeAccessInterface) { 00178 $viewHelper->setChildNodes($this->childNodes); 00179 } 00180 00181 $output = $viewHelper->initializeArgumentsAndRender($renderMethodParameters); 00182 00183 return $output; 00184 } 00185 00186 /** 00187 * Convert argument strings to their equivalents. Needed to handle strings with a boolean meaning. 00188 * 00189 * @param Tx_Fluid_Core_Parser_SyntaxTree_AbstractNode $syntaxTreeNode Value to be converted 00190 * @param string $type Target type 00191 * @return mixed New value 00192 * @author Sebastian Kurfürst <sebastian@typo3.org> 00193 * @author Bastian Waidelich <bastian@typo3.org> 00194 */ 00195 protected function convertArgumentValue(Tx_Fluid_Core_Parser_SyntaxTree_AbstractNode $syntaxTreeNode, $type, Tx_Fluid_Core_Rendering_RenderingContextInterface $renderingContext) { 00196 if ($type === 'boolean') { 00197 return $this->evaluateBooleanExpression($syntaxTreeNode, $renderingContext); 00198 } 00199 return $syntaxTreeNode->evaluate($renderingContext); 00200 } 00201 00202 /** 00203 * Convert boolean expression syntax tree to some meaningful value. 00204 * The expression is available as the SyntaxTree of the argument. 00205 * 00206 * We currently only support expressions of the form: 00207 * XX Comparator YY 00208 * Where XX and YY can be either: 00209 * - a number 00210 * - an Object accessor 00211 * - an array 00212 * - a ViewHelper 00213 * 00214 * and comparator must be one of the above. 00215 * 00216 * In case no comparator is found, the fallback of "convertToBoolean" is used. 00217 * 00218 * 00219 * Internal work: 00220 * First, we loop through the child syntaxtree nodes, to fill the left side of the comparator, 00221 * the right side of the comparator, and the comparator itself. 00222 * Then, we evaluate the obtained left and right side using the given comparator. This is done inside the evaluateComparator method. 00223 * 00224 * @param Tx_Fluid_Core_Parser_SyntaxTree_AbstractNode $syntaxTreeNode Value to be converted 00225 * @param Tx_Fluid_Core_Rendering_RenderingContextInterface $renderingContext 00226 * @return boolean Evaluated value 00227 * @throws Tx_Fluid_Core_Parser_Exception 00228 * @author Sebastian Kurfürst <sebastian@typo3.org> 00229 */ 00230 protected function evaluateBooleanExpression(Tx_Fluid_Core_Parser_SyntaxTree_AbstractNode $syntaxTreeNode, Tx_Fluid_Core_Rendering_RenderingContextInterface $renderingContext) { 00231 $childNodes = $syntaxTreeNode->getChildNodes(); 00232 if (count($childNodes) > 3) { 00233 throw new Tx_Fluid_Core_Parser_Exception('The expression "' . $syntaxTreeNode->evaluate($renderingContext) . '" has more than tree parts.', 1244201848); 00234 } 00235 00236 $leftSide = NULL; 00237 $rightSide = NULL; 00238 $comparator = NULL; 00239 foreach ($childNodes as $childNode) { 00240 if ($childNode instanceof Tx_Fluid_Core_Parser_SyntaxTree_TextNode && !preg_match(str_replace('COMPARATORS', implode('|', self::$comparators), self::$booleanExpressionTextNodeCheckerRegularExpression), $childNode->evaluate($renderingContext))) { 00241 $comparator = NULL; 00242 // skip loop and fall back to classical to boolean conversion. 00243 break; 00244 } 00245 00246 if ($comparator !== NULL) { 00247 // comparator already set, we are evaluating the right side of the comparator 00248 if ($rightSide === NULL) { 00249 $rightSide = $childNode->evaluate($renderingContext); 00250 } else { 00251 $rightSide .= $childNode->evaluate($renderingContext); 00252 } 00253 } elseif ($childNode instanceof Tx_Fluid_Core_Parser_SyntaxTree_TextNode 00254 && ($comparator = $this->getComparatorFromString($childNode->evaluate($renderingContext)))) { 00255 // comparator in current string segment 00256 $explodedString = explode($comparator, $childNode->evaluate($renderingContext)); 00257 if (isset($explodedString[0]) && trim($explodedString[0]) !== '') { 00258 $leftSide .= trim($explodedString[0]); 00259 } 00260 if (isset($explodedString[1]) && trim($explodedString[1]) !== '') { 00261 $rightSide .= trim($explodedString[1]); 00262 } 00263 } else { 00264 // comparator not found yet, on the left side of the comparator 00265 if ($leftSide === NULL) { 00266 $leftSide = $childNode->evaluate($renderingContext); 00267 } else { 00268 $leftSide .= $childNode->evaluate($renderingContext); 00269 } 00270 } 00271 } 00272 00273 if ($comparator !== NULL) { 00274 return $this->evaluateComparator($comparator, $leftSide, $rightSide); 00275 } else { 00276 return $this->convertToBoolean($syntaxTreeNode->evaluate($renderingContext)); 00277 } 00278 } 00279 00280 /** 00281 * Do the actual comparison. Compares $leftSide and $rightSide with $comparator and emits a boolean value 00282 * 00283 * @param string $comparator One of self::$comparators 00284 * @param mixed $leftSide Left side to compare 00285 * @param mixed $rightSide Right side to compare 00286 * @return boolean TRUE if comparison of left and right side using the comparator emit TRUE, false otherwise 00287 * @throws Tx_Fluid_Core_Parser_Exception 00288 * @author Sebastian Kurfürst <sebastian@typo3.org> 00289 */ 00290 protected function evaluateComparator($comparator, $leftSide, $rightSide) { 00291 switch ($comparator) { 00292 case '==': 00293 if (is_object($leftSide) && is_object($rightSide)) { 00294 return ($leftSide === $rightSide); 00295 } 00296 return ($leftSide == $rightSide); 00297 break; 00298 case '!=': 00299 if (is_object($leftSide) && is_object($rightSide)) { 00300 return ($leftSide !== $rightSide); 00301 } 00302 return ($leftSide != $rightSide); 00303 break; 00304 case '%': 00305 return (boolean)((int)$leftSide % (int)$rightSide); 00306 case '>': 00307 return ($leftSide > $rightSide); 00308 case '>=': 00309 return ($leftSide >= $rightSide); 00310 case '<': 00311 return ($leftSide < $rightSide); 00312 case '<=': 00313 return ($leftSide <= $rightSide); 00314 default: 00315 throw new Tx_Fluid_Core_Parser_Exception('Comparator "' . $comparator . '" is not implemented.', 1244234398); 00316 } 00317 } 00318 00319 /** 00320 * Determine if there is a comparator inside $string, and if yes, returns it. 00321 * 00322 * @param string $string string to check for a comparator inside 00323 * @return string The comparator or NULL if none found. 00324 * @author Sebastian Kurfürst <sebastian@typo3.org> 00325 * @author Bastian Waidelich <bastian@typo3.org> 00326 */ 00327 protected function getComparatorFromString($string) { 00328 foreach (self::$comparators as $comparator) { 00329 if (strpos($string, $comparator) !== FALSE) { 00330 return $comparator; 00331 } 00332 } 00333 00334 return NULL; 00335 } 00336 00337 /** 00338 * Convert argument strings to their equivalents. Needed to handle strings with a boolean meaning. 00339 * 00340 * @param mixed $value Value to be converted to boolean 00341 * @return boolean 00342 * @author Bastian Waidelich <bastian@typo3.org> 00343 * @todo this should be moved to another class 00344 */ 00345 protected function convertToBoolean($value) { 00346 if (is_bool($value)) { 00347 return $value; 00348 } 00349 if (is_numeric($value)) { 00350 return $value > 0; 00351 } 00352 if (is_string($value)) { 00353 return (!empty($value) && strtolower($value) !== 'false'); 00354 } 00355 if (is_array($value) || (is_object($value) && $value instanceof Countable)) { 00356 return count($value) > 0; 00357 } 00358 if (is_object($value)) { 00359 return TRUE; 00360 } 00361 return FALSE; 00362 } 00363 00364 /** 00365 * Clean up for serializing. 00366 * 00367 * @return array 00368 * @author Sebastian Kurfürst <sebastian@typo3.org> 00369 */ 00370 public function __sleep() { 00371 return array('viewHelperClassName', 'arguments', 'childNodes'); 00372 } 00373 } 00374 00375 ?>
1.8.0