TYPO3 API  SVNRelease
AbstractViewHelper.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  * The abstract base class for all view helpers.
00025  *
00026  * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
00027  * @api
00028  */
00029 abstract class Tx_Fluid_Core_ViewHelper_AbstractViewHelper {
00030 
00031     /**
00032      * TRUE if arguments have already been initialized
00033      * @var boolean
00034      */
00035     private $argumentsInitialized = FALSE;
00036 
00037     /**
00038      * Stores all Tx_Fluid_ArgumentDefinition instances
00039      * @var array
00040      */
00041     private $argumentDefinitions = array();
00042 
00043     /**
00044      * Current view helper node
00045      * @var Tx_Fluid_Core_Parser_SyntaxTree_ViewHelperNode
00046      */
00047     private $viewHelperNode;
00048 
00049     /**
00050      * Arguments accessor.
00051      * @var Tx_Fluid_Core_ViewHelper_Arguments
00052      * @api
00053      */
00054     protected $arguments;
00055 
00056     /**
00057      * Current variable container reference.
00058      * @var Tx_Fluid_Core_ViewHelper_TemplateVariableContainer
00059      * @api
00060      */
00061     protected $templateVariableContainer;
00062 
00063     /**
00064      * Controller Context to use
00065      * @var Tx_Extbase_MVC_Controller_ControllerContext
00066      * @api
00067      */
00068     protected $controllerContext;
00069 
00070     /**
00071      * @var Tx_Fluid_Core_Rendering_RenderingContextInterface
00072      */
00073     private $renderingContext;
00074 
00075     /**
00076      * ViewHelper Variable Container
00077      * @var Tx_Fluid_Core_ViewHelper_ViewHelperVariableContainer
00078      * @api
00079      */
00080     protected $viewHelperVariableContainer;
00081 
00082     /**
00083      * Reflection service
00084      * @var Tx_Extbase_Reflection_Service
00085      */
00086     private $reflectionService;
00087 
00088     /**
00089      * With this flag, you can disable the escaping interceptor inside this ViewHelper.
00090      * THIS MIGHT CHANGE WITHOUT NOTICE, NO PUBLIC API!
00091      * @var boolean
00092      * @internal
00093      */
00094     protected $escapingInterceptorEnabled = TRUE;
00095 
00096     /**
00097      * @param Tx_Fluid_Core_ViewHelper_Arguments $arguments
00098      * @return void
00099      * @author Bastian Waidelich <bastian@typo3.org>
00100      */
00101     public function setArguments(Tx_Fluid_Core_ViewHelper_Arguments $arguments) {
00102         $this->arguments = $arguments;
00103     }
00104 
00105     /**
00106      * @param Tx_Fluid_Core_ViewHelper_TemplateVariableContainer $templateVariableContainer Variable Container to be used for rendering
00107      * @return void
00108      * @author Bastian Waidelich <bastian@typo3.org>
00109      */
00110     public function setTemplateVariableContainer(Tx_Fluid_Core_ViewHelper_TemplateVariableContainer $templateVariableContainer) {
00111         $this->templateVariableContainer = $templateVariableContainer;
00112     }
00113 
00114     /**
00115      * @param Tx_Extbase_MVC_Controller_ControllerContext $controllerContext Controller context which is available inside the view
00116      * @return void
00117      * @author Sebastian Kurfürst <sebastian@typo3.org>
00118      */
00119     public function setControllerContext(Tx_Extbase_MVC_Controller_ControllerContext $controllerContext) {
00120         $this->controllerContext = $controllerContext;
00121     }
00122 
00123     /**
00124      * @param Tx_Fluid_Core_Rendering_RenderingContextInterface $renderingContext
00125      * @return void
00126      * @author Robert Lemke <robert@typo3.org>
00127      */
00128     public function setRenderingContext(Tx_Fluid_Core_Rendering_RenderingContextInterface $renderingContext) {
00129      $this->renderingContext = $renderingContext;
00130     }
00131 
00132 
00133     /**
00134      * @param Tx_Fluid_Core_ViewHelper_ViewHelperVariableContainer $viewHelperVariableContainer
00135      * @return void
00136      * @author Sebastian Kurfürst <sebastian@typo3.org>
00137      */
00138     public function setViewHelperVariableContainer(Tx_Fluid_Core_ViewHelper_ViewHelperVariableContainer $viewHelperVariableContainer) {
00139         $this->viewHelperVariableContainer = $viewHelperVariableContainer;
00140     }
00141 
00142     /**
00143      * Inject a Reflection service
00144      * @param Tx_Extbase_Reflection_Service $reflectionService Reflection service
00145      * @author Sebastian Kurfürst <sebastian@typo3.org>
00146      */
00147     public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) {
00148         $this->reflectionService = $reflectionService;
00149     }
00150 
00151     /**
00152      * Returns whether the escaping interceptor should be disabled or enabled inside the tags contents.
00153      *
00154      * THIS METHOD MIGHT CHANGE WITHOUT NOTICE; NO PUBLIC API!
00155      *
00156      * @internal
00157      * @return boolean
00158      */
00159     public function isEscapingInterceptorEnabled() {
00160         return $this->escapingInterceptorEnabled;
00161     }
00162 
00163     /**
00164      * Register a new argument. Call this method from your ViewHelper subclass
00165      * inside the initializeArguments() method.
00166      *
00167      * @param string $name Name of the argument
00168      * @param string $type Type of the argument
00169      * @param string $description Description of the argument
00170      * @param boolean $required If TRUE, argument is required. Defaults to FALSE.
00171      * @param mixed $defaultValue Default value of argument
00172      * @return Tx_Fluid_Core_ViewHelper_AbstractViewHelper $this, to allow chaining.
00173      * @author Sebastian Kurfürst <sebastian@typo3.org>
00174      * @todo Object Factory usage!
00175      * @api
00176      */
00177     protected function registerArgument($name, $type, $description, $required = FALSE, $defaultValue = NULL) {
00178         if (array_key_exists($name, $this->argumentDefinitions)) {
00179             throw new Tx_Fluid_Core_ViewHelper_Exception('Argument "' . $name . '" has already been defined, thus it should not be defined again.', 1253036401);
00180         }
00181         $this->argumentDefinitions[$name] = new Tx_Fluid_Core_ViewHelper_ArgumentDefinition($name, $type, $description, $required, $defaultValue);
00182         return $this;
00183     }
00184 
00185     /**
00186      * Overrides a registered argument. Call this method from your ViewHelper subclass
00187      * inside the initializeArguments() method if you want to override a previously registered argument.
00188      * @see registerArgument()
00189      *
00190      * @param string $name Name of the argument
00191      * @param string $type Type of the argument
00192      * @param string $description Description of the argument
00193      * @param boolean $required If TRUE, argument is required. Defaults to FALSE.
00194      * @param mixed $defaultValue Default value of argument
00195      * @return Tx_Fluid_Core_ViewHelper_AbstractViewHelper $this, to allow chaining.
00196      * @author Bastian Waidelich <bastian@typo3.org>
00197      * @todo Object Factory usage!
00198      * @api
00199      */
00200     protected function overrideArgument($name, $type, $description, $required = FALSE, $defaultValue = NULL) {
00201         if (!array_key_exists($name, $this->argumentDefinitions)) {
00202             throw new Tx_Fluid_Core_ViewHelper_Exception('Argument "' . $name . '" has not been defined, thus it can\'t be overridden.', 1279212461);
00203         }
00204         $this->argumentDefinitions[$name] = new Tx_Fluid_Core_ViewHelper_ArgumentDefinition($name, $type, $description, $required, $defaultValue);
00205         return $this;
00206     }
00207 
00208     /**
00209      * Sets all needed attributes needed for the rendering. Called by the
00210      * framework. Populates $this->viewHelperNode.
00211      * This is PURELY INTERNAL! Never override this method!!
00212      *
00213      * @param Tx_Fluid_Core_Parser_SyntaxTree_ViewHelperNode $node View Helper node to be set.
00214      * @return void
00215      * @author Sebastian Kurfürst <sebastian@typo3.org>
00216      */
00217     public function setViewHelperNode(Tx_Fluid_Core_Parser_SyntaxTree_ViewHelperNode $node) {
00218         $this->viewHelperNode = $node;
00219     }
00220 
00221     /**
00222      * Initialize the arguments of the ViewHelper, and call the render() method of the ViewHelper.
00223      *
00224      * @param array $renderMethodParameters the parameters of the render() method.
00225      * @return string the rendered ViewHelper.
00226      * @author Sebastian Kurfürst <sebastian@typo3.org>
00227      */
00228     public function initializeArgumentsAndRender(array $renderMethodParameters) {
00229         $this->validateArguments();
00230         $this->initialize();
00231         return $this->callRenderMethod($renderMethodParameters);
00232     }
00233 
00234     /**
00235      * Call the render() method and handle errors.
00236      *
00237      * @param array $renderMethodParameters the parameters of the render() method.
00238      * @return string the rendered ViewHelper
00239      * @author Sebastian Kurfürst <sebastian@typo3.org>
00240      */
00241     protected function callRenderMethod(array $renderMethodParameters) {
00242         try {
00243             return call_user_func_array(array($this, 'render'), $renderMethodParameters);
00244         } catch (Tx_Fluid_Core_ViewHelper_Exception $exception) {
00245                 // @todo [BW] rethrow exception, log, ignore.. depending on the current context
00246             return $exception->getMessage();
00247         }
00248     }
00249 
00250     /**
00251      * Initializes the view helper before invoking the render method.
00252      *
00253      * Override this method to solve tasks before the view helper content is rendered.
00254      *
00255      * @return void
00256      * @author Bastian Waidelich <bastian@typo3.org>
00257      * @api
00258      */
00259     public function initialize() {
00260     }
00261 
00262     /**
00263      * Helper method which triggers the rendering of everything between the
00264      * opening and the closing tag.
00265      *
00266      * @return mixed The finally rendered child nodes.
00267      * @author Sebastian Kurfürst <sebastian@typo3.org>
00268      * @author Bastian Waidelich <bastian@typo3.org>
00269      * @api
00270      */
00271     protected function renderChildren() {
00272         return $this->viewHelperNode->evaluateChildNodes($this->renderingContext);
00273     }
00274 
00275     /**
00276      * Initialize all arguments and return them
00277      *
00278      * @return array Array of Tx_Fluid_Core_ViewHelper_ArgumentDefinition instances.
00279      * @author Sebastian Kurfürst <sebastian@typo3.org>
00280      */
00281     public function prepareArguments() {
00282         if (!$this->argumentsInitialized) {
00283             $this->registerRenderMethodArguments();
00284             $this->initializeArguments();
00285             $this->argumentsInitialized = TRUE;
00286         }
00287         return $this->argumentDefinitions;
00288     }
00289 
00290     /**
00291      * Register method arguments for "render" by analysing the doc comment above.
00292      *
00293      * @return void
00294      * @author Sebastian Kurfürst <sebastian@typo3.org>
00295      * @author Bastian Waidelich <bastian@typo3.org>
00296      */
00297     private function registerRenderMethodArguments() {
00298         $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), 'render');
00299         if (count($methodParameters) === 0) {
00300             return;
00301         }
00302 
00303         if (Tx_Fluid_Fluid::$debugMode) {
00304             $methodTags = $this->reflectionService->getMethodTagsValues(get_class($this), 'render');
00305 
00306             $paramAnnotations = array();
00307             if (isset($methodTags['param'])) {
00308                 $paramAnnotations = $methodTags['param'];
00309             }
00310         }
00311 
00312         $i = 0;
00313         foreach ($methodParameters as $parameterName => $parameterInfo) {
00314             $dataType = NULL;
00315             if (isset($parameterInfo['type'])) {
00316                 $dataType = $parameterInfo['type'];
00317             } elseif ($parameterInfo['array']) {
00318                 $dataType = 'array';
00319             }
00320             if ($dataType === NULL) {
00321                 throw new Tx_Fluid_Core_Parser_Exception('could not determine type of argument "' . $parameterName .'" of the render-method in ViewHelper "' . get_class($this) . '". Either the methods docComment is invalid or some PHP optimizer strips off comments.', 1242292003);
00322             }
00323 
00324             $description = '';
00325             if (Tx_Fluid_Fluid::$debugMode && isset($paramAnnotations[$i])) {
00326                 $explodedAnnotation = explode(' ', $paramAnnotations[$i]);
00327                 array_shift($explodedAnnotation);
00328                 array_shift($explodedAnnotation);
00329                 $description = implode(' ', $explodedAnnotation);
00330             }
00331             $defaultValue = NULL;
00332             if (isset($parameterInfo['defaultValue'])) {
00333                 $defaultValue = $parameterInfo['defaultValue'];
00334             }
00335             $this->argumentDefinitions[$parameterName] = new Tx_Fluid_Core_ViewHelper_ArgumentDefinition($parameterName, $dataType, $description, ($parameterInfo['optional'] === FALSE), $defaultValue, TRUE);
00336             $i++;
00337         }
00338     }
00339 
00340     /**
00341      * Validate arguments, and throw exception if arguments do not validate.
00342      *
00343      * @return void
00344      * @author Sebastian Kurfürst <sebastian@typo3.org>
00345      * @author Bastian Waidelich <bastian@typo3.org>
00346      */
00347     public function validateArguments() {
00348         $argumentDefinitions = $this->prepareArguments();
00349         if (!count($argumentDefinitions)) return;
00350 
00351         foreach ($argumentDefinitions as $argumentName => $registeredArgument) {
00352             if ($this->arguments->offsetExists($argumentName)) {
00353                 $type = $registeredArgument->getType();
00354                 if ($this->arguments[$argumentName] === $registeredArgument->getDefaultValue()) continue;
00355 
00356                 if ($type === 'array') {
00357                     if (!is_array($this->arguments[$argumentName]) && !$this->arguments[$argumentName] instanceof ArrayAccess && !$this->arguments[$argumentName] instanceof Traversable) {
00358                         throw new InvalidArgumentException('The argument "' . $argumentName . '" was registered with type "array", but is of type "' . gettype($this->arguments[$argumentName]) . '" in view helper "' . get_class($this) . '"', 1237900529);
00359                     }
00360                 } elseif ($type === 'boolean') {
00361                     if (!is_bool($this->arguments[$argumentName])) {
00362                         throw new InvalidArgumentException('The argument "' . $argumentName . '" was registered with type "boolean", but is of type "' . gettype($this->arguments[$argumentName]) . '" in view helper "' . get_class($this) . '".', 1240227732);
00363                     }
00364                 } elseif (class_exists($type, FALSE)) {
00365                     if (! ($this->arguments[$argumentName] instanceof $type)) {
00366                         if (is_object($this->arguments[$argumentName])) {
00367                             throw new InvalidArgumentException('The argument "' . $argumentName . '" was registered with type "' . $type . '", but is of type "' . get_class($this->arguments[$argumentName]) . '" in view helper "' . get_class($this) . '".', 1256475114);
00368                         } else {
00369                             throw new InvalidArgumentException('The argument "' . $argumentName . '" was registered with type "' . $type . '", but is of type "' . gettype($this->arguments[$argumentName]) . '" in view helper "' . get_class($this) . '".', 1256475113);
00370                         }
00371                     }
00372                 }
00373             }
00374         }
00375     }
00376 
00377     /**
00378      * Initialize all arguments. You need to override this method and call
00379      * $this->registerArgument(...) inside this method, to register all your arguments.
00380      *
00381      * @return void
00382      * @author Sebastian Kurfürst <sebastian@typo3.org>
00383      * @api
00384      */
00385     public function initializeArguments() {
00386     }
00387 
00388     /**
00389      * Render method you need to implement for your custom view helper.
00390      * Available objects at this point are $this->arguments, and $this->templateVariableContainer.
00391      *
00392      * Besides, you often need $this->renderChildren().
00393      *
00394      * @return string rendered string, view helper specific
00395      * @author Sebastian Kurfürst <sebastian@typo3.org>
00396      * @api
00397      */
00398     //abstract public function render();
00399 
00400     /**
00401      * Get the rendering context interface.
00402      * THIS METHOD IS NO PUBLIC API AND ONLY CALLABLE INSIDE THE FRAMEWORK!
00403      *
00404      * @return Tx_Fluid_Core_Rendering_RenderingContextInterface
00405      * @author Sebastian Kurfürst <sebastian@typo3.org>
00406      */
00407     public function getRenderingContext() {
00408         if ($this instanceof Tx_Fluid_Core_ViewHelper_Facets_ChildNodeAccessInterface) {
00409             return $this->renderingContext;
00410         } else {
00411             throw new Tx_Fluid_Core_ViewHelper_Exception_RenderingContextNotAccessibleException('It is forbidden to call getRenderingContext() if you do not implement Tx_Fluid_Core_ViewHelper_Facets_ChildNodeAccessInterface. But beware, this interface is NO PUBLIC API! If you want to implement conditions, you should subclass Tx_Fluid_Core_ViewHelper_AbstractConditionViewHelper.', 127895038);
00412         }
00413     }
00414 }
00415 
00416 ?>