/** * Compiles the ExpressionNode, returning an array with * exactly two keys which contain strings: * * - "initialization" which contains variable initializations * - "execution" which contains the execution (that uses the variables) * * The expression and matches can be read from the local * instance - and the RenderingContext and other APIs * can be accessed via the TemplateCompiler. * * @param TemplateCompiler $templateCompiler * @return array */ public function compile(TemplateCompiler $templateCompiler) { $handlerClass = get_class($this); $expressionVariable = $templateCompiler->variableName('string'); $matchesVariable = $templateCompiler->variableName('array'); $initializationPhpCode = sprintf('// Rendering %s node' . chr(10), $handlerClass); $initializationPhpCode .= sprintf('%s = \'%s\';', $expressionVariable, $this->getExpression()) . chr(10); $initializationPhpCode .= sprintf('%s = %s;', $matchesVariable, var_export($this->getMatches(), true)) . chr(10); return ['initialization' => $initializationPhpCode, 'execution' => sprintf('\\%s::evaluateExpression($renderingContext, %s, %s)', $handlerClass, $expressionVariable, $matchesVariable)]; }
/** * Convert a single ViewHelperNode into its cached representation. If the ViewHelper implements the "Compilable" facet, * the ViewHelper itself is asked for its cached PHP code representation. If not, a ViewHelper is built and then invoked. * * @param ViewHelperNode $node * @return array * @see convert() */ protected function convertViewHelperNode(ViewHelperNode $node) { $initializationPhpCode = '// Rendering ViewHelper ' . $node->getViewHelperClassName() . chr(10); // Build up $arguments array $argumentsVariableName = $this->variableName('arguments'); $initializationPhpCode .= sprintf('%s = array();', $argumentsVariableName) . chr(10); $alreadyBuiltArguments = array(); foreach ($node->getArguments() as $argumentName => $argumentValue) { if ($argumentValue instanceof NodeInterface) { $converted = $this->convert($argumentValue); } else { $converted = array('initialization' => '', 'execution' => $argumentValue); } $initializationPhpCode .= $converted['initialization']; $initializationPhpCode .= sprintf('%s[\'%s\'] = %s;', $argumentsVariableName, $argumentName, $converted['execution']) . chr(10); $alreadyBuiltArguments[$argumentName] = TRUE; } $arguments = $node->getArgumentDefinitions(); foreach ($arguments as $argumentName => $argumentDefinition) { if (!isset($alreadyBuiltArguments[$argumentName])) { $initializationPhpCode .= sprintf('%s[\'%s\'] = %s;', $argumentsVariableName, $argumentName, var_export($argumentDefinition->getDefaultValue(), TRUE)) . chr(10); } } // Build up closure which renders the child nodes $renderChildrenClosureVariableName = $this->variableName('renderChildrenClosure'); $initializationPhpCode .= sprintf('%s = %s;', $renderChildrenClosureVariableName, $this->templateCompiler->wrapChildNodesInClosure($node)) . chr(10); $viewHelperInitializationPhpCode = ''; $convertedViewHelperExecutionCode = $node->getUninitializedViewHelper()->compile($argumentsVariableName, $renderChildrenClosureVariableName, $viewHelperInitializationPhpCode, $node, $this->templateCompiler); $initializationPhpCode .= $viewHelperInitializationPhpCode; $initializationArray = array('initialization' => $initializationPhpCode, 'execution' => $convertedViewHelperExecutionCode === NULL ? 'NULL' : $convertedViewHelperExecutionCode); return $initializationArray; }
/** * @param string $templateIdentifier * @param \Closure $templateSourceClosure Closure which returns the template source if needed * @return ParsedTemplateInterface */ protected function getOrParseAndStoreTemplate($templateIdentifier, $templateSourceClosure) { if ($this->templateCompiler->has($templateIdentifier)) { $parsedTemplate = $this->templateCompiler->get($templateIdentifier); } else { $parsedTemplate = $this->templateParser->parse($templateSourceClosure($this, $this->templatePaths)); if ($parsedTemplate->isCompilable()) { $this->templateCompiler->store($templateIdentifier, $parsedTemplate); } } return $parsedTemplate; }
/** * This ViewHelper is used a *lot* because it is used by the escape interceptor. * Therefore we render it to raw PHP code during compilation * * @param string $argumentsName * @param string $closureName * @param string $initializationPhpCode * @param ViewHelperNode $node * @param TemplateCompiler $compiler * @return string */ public function compile($argumentsName, $closureName, &$initializationPhpCode, ViewHelperNode $node, TemplateCompiler $compiler) { $valueVariableName = $compiler->variableName('value'); $initializationPhpCode .= sprintf('%1$s = (%2$s[\'value\'] !== NULL ? %2$s[\'value\'] : %3$s());', $valueVariableName, $argumentsName, $closureName) . chr(10); return sprintf('!is_string(%1$s) && !(is_object(%1$s) && method_exists(%1$s, \'__toString\')) ? %1$s : htmlspecialchars(%1$s, (%2$s[\'keepQuotes\'] ? ENT_NOQUOTES : ENT_QUOTES), %2$s[\'encoding\'], %2$s[\'doubleEncode\'])', $valueVariableName, $argumentsName); }
/** * @param string $argumentsName * @param string $closureName * @param string $initializationPhpCode * @param ViewHelperNode $node * @param TemplateCompiler $compiler * @return null */ public function compile($argumentsName, $closureName, &$initializationPhpCode, ViewHelperNode $node, TemplateCompiler $compiler) { // @TODO: replace with a true compiling method to make compilable! $compiler->disable(); return null; }
/** * @test */ public function testGetRenderingContextGetsRenderingContext() { $context = new RenderingContextFixture(); $instance = new TemplateCompiler(); $instance->setRenderingContext($context); $this->assertSame($context, $instance->getRenderingContext()); }
/** * @param string $argumentsName * @param string $closureName * @param string $initializationPhpCode * @param ViewHelperNode $node * @param TemplateCompiler $compiler * @return string */ public function compile($argumentsName, $closureName, &$initializationPhpCode, ViewHelperNode $node, TemplateCompiler $compiler) { $compiler->disable(); return "''"; }
/** * The compiled ViewHelper adds two new ViewHelper arguments: __thenClosure and __elseClosure. * These contain closures which are be executed to render the then(), respectively else() case. * * @param string $argumentsName * @param string $closureName * @param string $initializationPhpCode * @param ViewHelperNode $node * @param TemplateCompiler $compiler * @return string */ public function compile($argumentsName, $closureName, &$initializationPhpCode, ViewHelperNode $node, TemplateCompiler $compiler) { $thenViewHelperEncountered = $elseViewHelperEncountered = false; foreach ($node->getChildNodes() as $childNode) { if ($childNode instanceof ViewHelperNode) { $viewHelperClassName = $childNode->getViewHelperClassName(); if (substr($viewHelperClassName, -14) === 'ThenViewHelper') { $thenViewHelperEncountered = true; $childNodesAsClosure = $compiler->wrapChildNodesInClosure($childNode); $initializationPhpCode .= sprintf('%s[\'__thenClosure\'] = %s;', $argumentsName, $childNodesAsClosure) . chr(10); } elseif (substr($viewHelperClassName, -14) === 'ElseViewHelper') { $elseViewHelperEncountered = true; $childNodesAsClosure = $compiler->wrapChildNodesInClosure($childNode); $initializationPhpCode .= sprintf('%s[\'__elseClosures\'][] = %s;', $argumentsName, $childNodesAsClosure) . chr(10); $arguments = $childNode->getArguments(); if (isset($arguments['if'])) { // The "else" has an argument, indicating it has a secondary (elseif) condition. // Compile a closure which will evaluate the condition. $elseIfConditionAsClosure = $compiler->wrapViewHelperNodeArgumentEvaluationInClosure($childNode, 'if'); $initializationPhpCode .= sprintf('%s[\'__elseifClosures\'][] = %s;', $argumentsName, $elseIfConditionAsClosure) . chr(10); } } } } if (!$thenViewHelperEncountered && !$elseViewHelperEncountered && !isset($node->getArguments()['then'])) { $initializationPhpCode .= sprintf('%s[\'__thenClosure\'] = %s;', $argumentsName, $closureName) . chr(10); } return parent::compile($argumentsName, $closureName, $initializationPhpCode, $node, $compiler); }
/** * @test */ public function testStoreWhenDisabledFlushesCache() { $cache = $this->getMock('TYPO3Fluid\\Fluid\\Core\\Cache\\SimpleFileCache', array('flush', 'store')); $cache->expects($this->never())->method('store'); $cache->expects($this->once())->method('flush')->with('fakeidentifier'); $state = new ParsingState(); $instance = new TemplateCompiler(); $instance->disable(); $instance->setTemplateCache($cache); $instance->store('fakeidentifier', $state); }
/** * @return ExposedTemplateCompiler */ protected function getTemplateCompiler() { if ($this->assertCoreVersionAtLeast(8)) { $compiler = new TemplateCompiler(); $compiler->setRenderingContext(new RenderingContext()); return $compiler; } return $this->objectManager->get('FluidTYPO3\\Builder\\Parser\\ExposedTemplateCompiler'); }
/** * @param TemplateCompiler $templateCompiler * @return void */ public function setTemplateCompiler(TemplateCompiler $templateCompiler) { $this->templateCompiler = $templateCompiler; $this->templateCompiler->setRenderingContext($this); }
/** * Compiles the ExpressionNode, returning an array with * exactly two keys which contain strings: * * - "initialization" which contains variable initializations * - "execution" which contains the execution (that uses the variables) * * The expression and matches can be read from the local * instance - and the RenderingContext and other APIs * can be accessed via the TemplateCompiler. * * @param TemplateCompiler $templateCompiler * @return string */ public function compile(TemplateCompiler $templateCompiler) { $parts = preg_split('/([\\?:])/s', $this->getExpression()); $parts = array_map([__CLASS__, 'trimPart'], $parts); list($check, $then, $else) = $parts; $matchesVariable = $templateCompiler->variableName('array'); $initializationPhpCode = '// Rendering TernaryExpression node' . chr(10); $initializationPhpCode .= sprintf('%s = %s;', $matchesVariable, var_export($this->getMatches(), true)) . chr(10); $parser = new BooleanParser(); $compiledExpression = $parser->compile($check); $functionName = $templateCompiler->variableName('ternaryExpression'); $initializationPhpCode .= sprintf('%s = function($context, $renderingContext) { if (%s::convertToBoolean(' . $compiledExpression . ', $renderingContext) === TRUE) { return %s::getTemplateVariableOrValueItself(%s, $renderingContext); } else { return %s::getTemplateVariableOrValueItself(%s, $renderingContext); } };' . chr(10), $functionName, BooleanNode::class, static::class, var_export($then, true), static::class, var_export($else, true)); return ['initialization' => $initializationPhpCode, 'execution' => sprintf('%s(%s::gatherContext($renderingContext, %s[1]), $renderingContext)', $functionName, static::class, $matchesVariable)]; }
/** * Compiles the node structure to a native switch * statement which evaluates closures for each * case comparison and renders child node closures * only when value matches. * * @param string $argumentsName * @param string $closureName * @param string $initializationPhpCode * @param ViewHelperNode $node * @param TemplateCompiler $compiler * @return string */ public function compile($argumentsName, $closureName, &$initializationPhpCode, ViewHelperNode $node, TemplateCompiler $compiler) { $phpCode = 'call_user_func_array(function($arguments) use ($renderingContext, $self) {' . PHP_EOL . 'switch ($arguments[\'expression\']) {' . PHP_EOL; foreach ($node->getChildNodes() as $childNode) { if ($this->isDefaultCaseNode($childNode)) { $childrenClosure = $compiler->wrapChildNodesInClosure($childNode); $phpCode .= sprintf('default: return call_user_func(%s);', $childrenClosure) . PHP_EOL; } elseif ($this->isCaseNode($childNode)) { /** @var ViewHelperNode $childNode */ $valueClosure = $compiler->wrapViewHelperNodeArgumentEvaluationInClosure($childNode, 'value'); $childrenClosure = $compiler->wrapChildNodesInClosure($childNode); $phpCode .= sprintf('case call_user_func(%s): return call_user_func(%s);', $valueClosure, $childrenClosure, $compiler->getNodeConverter()->convert($childNode)) . PHP_EOL; } } $phpCode .= '}' . PHP_EOL; $phpCode .= sprintf('}, array(%s))', $argumentsName); return $phpCode; }