[TASK] Import forward-compatible Fluid compiling Traits
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Resources / PHP / Traits / CompileWithContentArgumentAndRenderStatic.php
1 <?php
2 namespace TYPO3Fluid\Fluid\Core\ViewHelper\Traits;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Fluid\Core\Compiler\TemplateCompiler;
18 use TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode;
19 use TYPO3\CMS\Fluid\Core\ViewHelper\Exception;
20
21 /**
22 * Trait CompilableWithContentArgumentAndRenderStatic
23 *
24 * Provides default methods for rendering and compiling
25 * any ViewHelper that conforms to the `renderStatic`
26 * method pattern but has the added common use case that
27 * an argument value must be checked and used instead of
28 * the normal render children closure, if that named
29 * argument is specified and not empty.
30 */
31 trait CompileWithContentArgumentAndRenderStatic {
32
33 /**
34 * Name of variable that contains the value to use
35 * instead of render children closure, if specified.
36 * If no name is provided here, the first variable
37 * registered in `initializeArguments` of the ViewHelper
38 * will be used.
39 *
40 * Note: it is significantly better practice to define
41 * this property in your ViewHelper class and so fix it
42 * to one particular argument instead of resolving,
43 * especially when your ViewHelper is called multiple
44 * times within an uncompiled template!
45 *
46 * @var string
47 */
48 protected $contentArgumentName;
49
50 /**
51 * @return \Closure
52 */
53 abstract protected function buildRenderChildrenClosure();
54
55 /**
56 * @return mixed
57 */
58 abstract public function prepareArguments();
59
60 /**
61 * Default render method to render ViewHelper with
62 * first defined optional argument as content.
63 *
64 * @return string Rendered string
65 * @api
66 */
67 public function render() {
68 $argumentName = $this->resolveContentArgumentName();
69 $arguments = $this->arguments;
70 if (!empty($argumentName) && isset($arguments[$argumentName])) {
71 $renderChildrenClosure = function() use ($arguments, $argumentName) { return $arguments[$argumentName]; };
72 } else {
73 $renderChildrenClosure = $this->buildRenderChildrenClosure();
74 }
75 return static::renderStatic(
76 $arguments,
77 $renderChildrenClosure,
78 $this->renderingContext
79 );
80 }
81
82 /**
83 * @param string $argumentsName
84 * @param string $closureName
85 * @param string $initializationPhpCode
86 * @param AbstractNode $node
87 * @param TemplateCompiler $compiler
88 * @return string
89 */
90 public function compile(
91 $argumentsName,
92 $closureName,
93 &$initializationPhpCode,
94 AbstractNode $node,
95 TemplateCompiler $compiler
96 ) {
97 $contentArgumentName = $this->resolveContentArgumentName();
98 return sprintf(
99 '%s::renderStatic(%s, isset(%s[\'%s\']) ? function() use (%s); return %s[\'%s\']; } : %s, $renderingContext)',
100 static::class,
101 $argumentsName,
102 $argumentsName,
103 $contentArgumentName,
104 $argumentsName,
105 $argumentsName,
106 $contentArgumentName,
107 $closureName
108 );
109 }
110
111 /**
112 * @return string
113 * @throws Exception
114 */
115 protected function resolveContentArgumentName() {
116 if (empty($this->contentArgumentName)) {
117 foreach ($this->prepareArguments() as $registeredArgument) {
118 if (!$registeredArgument->isRequired()) {
119 return $registeredArgument->getName();
120 }
121 }
122 throw new Exception(
123 'Attempting to compile %s failed. Chosen compile method requires that ViewHelper has ' .
124 'at least one registered and optional argument'
125 );
126 }
127 return $this->contentArgumentName;
128 }
129 }