/** * Loads the template source and render the template. * If "layoutName" is set in a PostParseFacet callback, it will render the file with the given layout. * * @param string $actionName If set, this action's template will be rendered instead of the one defined in the context. * @return string Rendered Template * @api */ public function render($actionName = NULL) { $this->templateParser->setConfiguration($this->buildParserConfiguration()); $this->templateParser->setVariableProvider($this->baseRenderingContext->getVariableProvider()); $this->templateParser->setViewHelperResolver($this->viewHelperResolver); $this->baseRenderingContext->setViewHelperResolver($this->viewHelperResolver); $controllerName = $this->baseRenderingContext->getControllerName(); if (!$actionName) { $actionName = $this->baseRenderingContext->getControllerAction(); } $actionName = ucfirst($actionName); if (empty($templateIdentifier)) { $templateIdentifier = $this->templatePaths->getTemplateIdentifier($controllerName, $actionName); } $parsedTemplate = $this->getOrParseAndStoreTemplate($templateIdentifier, function ($parent, TemplatePaths $paths) use($controllerName, $actionName) { return $paths->getTemplateSource($controllerName, $actionName); }); $parsedTemplate->addCompiledNamespaces($this->baseRenderingContext); if (!$parsedTemplate->hasLayout()) { $this->startRendering(self::RENDERING_TEMPLATE, $parsedTemplate, $this->baseRenderingContext); $output = $parsedTemplate->render($this->baseRenderingContext); $this->stopRendering(); } else { $layoutName = $parsedTemplate->getLayoutName($this->baseRenderingContext); $layoutIdentifier = $this->templatePaths->getLayoutIdentifier($layoutName); $parsedLayout = $this->getOrParseAndStoreTemplate($layoutIdentifier, function ($parent, TemplatePaths $paths) use($layoutName) { return $paths->getLayoutSource($layoutName); }); $this->startRendering(self::RENDERING_LAYOUT, $parsedTemplate, $this->baseRenderingContext); $output = $parsedLayout->render($this->baseRenderingContext); $this->stopRendering(); } return $output; }
/** * Assigns multiple values to the JSON output. * However, only the key "value" is accepted. * * @param array $values Keys and values - only a value with key "value" is considered * @return $this * @api */ public function assignMultiple(array $values) { $templateVariableContainer = $this->baseRenderingContext->getVariableProvider(); foreach ($values as $key => $value) { $templateVariableContainer->add($key, $value); } return $this; }
/** * @param RenderingContextInterface $renderingContext * @return void */ public function setRenderingContext(RenderingContextInterface $renderingContext) { $this->renderingContext = $renderingContext; $this->templateVariableContainer = $renderingContext->getVariableProvider(); $this->viewHelperVariableContainer = $renderingContext->getViewHelperVariableContainer(); if ($renderingContext instanceof FlowAwareRenderingContextInterface) { $this->controllerContext = $renderingContext->getControllerContext(); } }
/** * Evaluate this node and return the correct object. * * Handles each part (denoted by .) in $this->objectPath in the following order: * - call appropriate getter * - call public property, if exists * - fail * * The first part of the object path has to be a variable in the * VariableProvider. * * @param RenderingContextInterface $renderingContext * @return mixed The evaluated object, can be any object type. */ public function evaluate(RenderingContextInterface $renderingContext) { $objectPath = strtolower($this->objectPath); $variableProvider = $renderingContext->getVariableProvider(); if ($objectPath === '_all') { return $variableProvider->getAll(); } return VariableExtractor::extract($variableProvider, $this->objectPath, $this->accessors); }
/** * @return ParsingState */ protected function getParsingState() { $rootNode = new RootNode(); $variableProvider = $this->renderingContext->getVariableProvider(); $state = new ParsingState(); $state->setRootNode($rootNode); $state->pushNodeToStack($rootNode); $state->setVariableProvider($variableProvider->getScopeCopy($variableProvider->getAll())); return $state; }
/** * @param mixed $candidate * @param RenderingContextInterface $renderingContext * @return mixed */ protected static function getTemplateVariableOrValueItself($candidate, RenderingContextInterface $renderingContext) { $variables = $renderingContext->getVariableProvider()->getAll(); $extractor = new VariableExtractor(); $suspect = $extractor->getByPath($variables, $candidate); if (NULL === $suspect) { return $candidate; } return $suspect; }
/** * @param array $arguments * @param \Closure $renderChildrenClosure * @param RenderingContextInterface $renderingContext * @return mixed */ public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) { $values = $arguments['values']; $as = $arguments['as']; if ($values === null) { return $renderChildrenClosure(); } $values = static::initializeValues($values); $index = static::initializeIndex($as, $renderingContext->getViewHelperVariableContainer()); $currentValue = isset($values[$index]) ? $values[$index] : null; $renderingContext->getVariableProvider()->add($as, $currentValue); $output = $renderChildrenClosure(); $renderingContext->getVariableProvider()->remove($as); $index++; if (!isset($values[$index])) { $index = 0; } $renderingContext->getViewHelperVariableContainer()->addOrUpdate(static::class, $as, $index); return $output; }
/** * @param array $arguments * @param \Closure $renderChildrenClosure * @param RenderingContextInterface $renderingContext * @return mixed */ public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) { $templateVariableContainer = $renderingContext->getVariableProvider(); $map = $arguments['map']; foreach ($map as $aliasName => $value) { $templateVariableContainer->add($aliasName, $value); } $output = $renderChildrenClosure(); foreach ($map as $aliasName => $value) { $templateVariableContainer->remove($aliasName); } return $output; }
/** * Evaluate this node and return the correct object. * * Handles each part (denoted by .) in $this->objectPath in the following order: * - call appropriate getter * - call public property, if exists * - fail * * The first part of the object path has to be a variable in the * VariableProvider. * * @param RenderingContextInterface $renderingContext * @return object The evaluated object, can be any object type. */ public function evaluate(RenderingContextInterface $renderingContext) { $variableProvider = $renderingContext->getVariableProvider(); switch (strtolower($this->objectPath)) { case '_all': return $variableProvider->getAll(); case 'true': case 'on': case 'yes': return TRUE; case 'false': case 'off': case 'no': return FALSE; default: return VariableExtractor::extract($variableProvider, $this->objectPath, $this->accessors); } }
/** * @param RenderingContextInterface $renderingContext * @param string $expression * @param array $matches * @return integer|float */ public static function evaluateExpression(RenderingContextInterface $renderingContext, $expression, array $matches) { // Split the expression on all recognized operators $matches = array(); preg_match_all('/([+\\-*\\^\\/\\%]|[a-z0-9\\.]+)/s', $expression, $matches); $matches[0] = array_map('trim', $matches[0]); // Like the BooleanNode, we dumb down the processing logic to not apply // any special precedence on the priority of operators. We simply process // them in order. $variables = $renderingContext->getVariableProvider()->getAll(); $result = array_shift($matches[0]); $result = parent::getTemplateVariableOrValueItself($result, $renderingContext); $operator = NULL; $operators = array('*', '^', '-', '+', '/', '%'); foreach ($matches[0] as $part) { if (in_array($part, $operators)) { $operator = $part; } else { $part = parent::getTemplateVariableOrValueItself($part, $renderingContext); $result = self::evaluateOperation($result, $operator, $part); } } return $result; }
/** * Overlay variables by replacing the VariableProvider with a * ChainedVariableProvider using dual data sources. Returns the * original VariableProvider which must replace the temporary * one again once the rendering/compiling is done. * * @param RenderingContextInterface $renderingContext * @param array $variables * @return VariableProviderInterface */ protected static function overlayVariablesIfNotSet(RenderingContextInterface $renderingContext, array $variables) { $currentProvider = $renderingContext->getVariableProvider(); $chainedVariableProvider = new ChainedVariableProvider([$currentProvider, new StandardVariableProvider($variables)]); $renderingContext->setVariableProvider($chainedVariableProvider); return $currentProvider; }
/** * @param RenderingContextInterface $renderingContext * @return void */ public function setRenderingContext(RenderingContextInterface $renderingContext) { $this->renderingContext = $renderingContext; $this->templateVariableContainer = $renderingContext->getVariableProvider(); $this->viewHelperVariableContainer = $renderingContext->getViewHelperVariableContainer(); }
/** * Returns the name of the layout that is defined within the current template via <f:layout name="..." /> * If no layout is defined, this returns NULL * This requires the current rendering context in order to be able to evaluate the layout name * * @param RenderingContextInterface $renderingContext * @return string * @throws View\Exception */ public function getLayoutName(RenderingContextInterface $renderingContext) { return $renderingContext->getVariableProvider()->get('layoutName'); }
/** * @param array $arguments * @param \Closure $renderChildrenClosure * @param RenderingContextInterface $renderingContext * @return string * @throws ViewHelper\Exception */ public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) { $templateVariableContainer = $renderingContext->getVariableProvider(); if ($arguments['each'] === NULL) { return ''; } if (is_object($arguments['each']) && !$arguments['each'] instanceof \Traversable) { throw new ViewHelper\Exception('ForViewHelper only supports arrays and objects implementing \\Traversable interface', 1248728393); } if ($arguments['reverse'] === TRUE) { // array_reverse only supports arrays if (is_object($arguments['each'])) { /** @var $each \Traversable */ $each = $arguments['each']; $arguments['each'] = iterator_to_array($each); } $arguments['each'] = array_reverse($arguments['each']); } $iterationData = array('index' => 0, 'cycle' => 1, 'total' => count($arguments['each'])); $output = ''; foreach ($arguments['each'] as $keyValue => $singleElement) { $templateVariableContainer->add($arguments['as'], $singleElement); if ($arguments['key'] !== '') { $templateVariableContainer->add($arguments['key'], $keyValue); } if ($arguments['iteration'] !== NULL) { $iterationData['isFirst'] = $iterationData['cycle'] === 1; $iterationData['isLast'] = $iterationData['cycle'] === $iterationData['total']; $iterationData['isEven'] = $iterationData['cycle'] % 2 === 0; $iterationData['isOdd'] = !$iterationData['isEven']; $templateVariableContainer->add($arguments['iteration'], $iterationData); $iterationData['index']++; $iterationData['cycle']++; } $output .= $renderChildrenClosure(); $templateVariableContainer->remove($arguments['as']); if ($arguments['key'] !== '') { $templateVariableContainer->remove($arguments['key']); } if ($arguments['iteration'] !== NULL) { $templateVariableContainer->remove($arguments['iteration']); } } return $output; }
/** * @test */ public function templateVariableContainerCanBeReadCorrectly() { $templateVariableContainer = $this->getMock(StandardVariableProvider::class); $this->renderingContext->setVariableProvider($templateVariableContainer); $this->assertSame($this->renderingContext->getVariableProvider(), $templateVariableContainer, 'Template Variable Container could not be read out again.'); }
/** * @param array $arguments * @param \Closure $renderChildrenClosure * @param RenderingContextInterface $renderingContext * @return mixed */ public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) { $each = $arguments['each']; $as = $arguments['as']; $groupBy = $arguments['groupBy']; $groupKey = $arguments['groupKey']; $output = ''; if ($each === NULL) { return ''; } if (is_object($each)) { if (!$each instanceof \Traversable) { throw new ViewHelper\Exception('GroupedForViewHelper only supports arrays and objects implementing \\Traversable interface', 1253108907); } $each = iterator_to_array($each); } $groups = static::groupElements($each, $groupBy); $templateVariableContainer = $renderingContext->getVariableProvider(); foreach ($groups['values'] as $currentGroupIndex => $group) { $templateVariableContainer->add($groupKey, $groups['keys'][$currentGroupIndex]); $templateVariableContainer->add($as, $group); $output .= $renderChildrenClosure(); $templateVariableContainer->remove($groupKey); $templateVariableContainer->remove($as); } return $output; }
/** * @param \TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface $renderingContext * @return void */ public function setRenderingContext(\TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface $renderingContext) { $this->renderingContext = $renderingContext; $this->templateVariableContainer = $renderingContext->getVariableProvider(); $this->viewHelperVariableContainer = $renderingContext->getViewHelperVariableContainer(); if ($renderingContext instanceof \TYPO3\CMS\Fluid\Core\Rendering\RenderingContext) { $this->controllerContext = $renderingContext->getControllerContext(); } }
/** * Warm up a single template file. * * Performs reading, parsing and attempts compiling of a single * template file. Catches errors that may occur and reports them * in a FailedCompilingState (which can then be `add()`'ed to * the FluidCacheWarmupResult to assimilate the information within. * * Adds basic mitigation suggestions for each specific type of error, * giving hints to developers if a certain template fails to compile. * * @param string $templatePathAndFilename * @param string $identifier * @param RenderingContextInterface $renderingContext * @return ParsedTemplateInterface */ protected function warmSingleFile($templatePathAndFilename, $identifier, RenderingContextInterface $renderingContext) { $parsedTemplate = new FailedCompilingState(); $parsedTemplate->setVariableProvider($renderingContext->getVariableProvider()); $parsedTemplate->setCompilable(false); $parsedTemplate->setIdentifier($identifier); try { $parsedTemplate = $renderingContext->getTemplateParser()->getOrParseAndStoreTemplate($identifier, function (TemplateParser $parser, TemplatePaths $templatePaths) use($templatePathAndFilename) { return file_get_contents($templatePathAndFilename, FILE_TEXT); }); } catch (StopCompilingException $error) { $parsedTemplate->setFailureReason(sprintf('Compiling is intentionally disabled. Specific reason unknown. Message: "%s"', $error->getMessage())); $parsedTemplate->setMitigations(['Can be caused by specific ViewHelpers. If this is is not intentional: avoid ViewHelpers which disable caches.', 'If cache is intentionally disabled: consider using `f:cache.static` to cause otherwise uncompilable ViewHelpers\' output to be replaced with a static string in compiled templates.']); } catch (ExpressionException $error) { $parsedTemplate->setFailureReason(sprintf('ExpressionNode evaluation error: %s', $error->getMessage())); $parsedTemplate->setMitigations(['Emulate variables used in ExpressionNode using `f:cache.warmup` or assign in warming RenderingContext']); } catch (\TYPO3Fluid\Fluid\Core\Parser\Exception $error) { $parsedTemplate->setFailureReason($error->getMessage()); $parsedTemplate->setMitigations(['Fix possible syntax errors.', 'Check that all ViewHelpers are correctly referenced and namespaces loaded (note: namespaces may be added externally!)', 'Check that all ExpressionNode types used by the template are loaded (note: may depend on RenderingContext implementation!)', 'Emulate missing variables used in expressions by using `f:cache.warmup` around your template code.']); } catch (\TYPO3Fluid\Fluid\Core\ViewHelper\Exception $error) { $parsedTemplate->setFailureReason(sprintf('ViewHelper threw Exception: %s', $error->getMessage())); $parsedTemplate->setMitigations(['Emulate missing variables using `f:cache.warmup` around failing ViewHelper.', 'Emulate globals / context required by ViewHelper.', 'Disable caching for template if ViewHelper depends on globals / context that cannot be emulated.']); } catch (\TYPO3Fluid\Fluid\Core\Exception $error) { $parsedTemplate->setFailureReason(sprintf('Fluid engine error: %s', $error->getMessage())); $parsedTemplate->setMitigations(['Search online for additional information about specific error.']); } catch (Exception $error) { $parsedTemplate->setFailureReason(sprintf('Fluid view error: %s', $error->getMessage())); $parsedTemplate->setMitigations(['Investigate reported error in View class for missing variable checks, missing configuration etc.', 'Consider using a different View class for rendering in warmup mode (a custom rendering context can provide it)']); } catch (\RuntimeException $error) { $parsedTemplate->setFailureReason(sprintf('General error: %s line %s threw %s (code: %d)', get_class($error), $error->getFile(), $error->getLine(), $error->getMessage(), $error->getCode())); $parsedTemplate->setMitigations(['There are no automated suggestions for mitigating this issue. An online search may yield more information.']); } return $parsedTemplate; }