f020886ed58b6cb5566c8863b314101a07798f8f
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Classes / Core / ViewHelper / AbstractConditionViewHelper.php
1 <?php
2 namespace TYPO3\CMS\Fluid\Core\ViewHelper;
3
4 /* *
5 * This script is backported from the TYPO3 Flow package "TYPO3.Fluid". *
6 * *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU Lesser General Public License, either version 3 *
9 * of the License, or (at your option) any later version. *
10 * *
11 * The TYPO3 project - inspiring people to share! *
12 * */
13 use TYPO3\CMS\Fluid\ViewHelpers\ThenViewHelper;
14 use TYPO3\CMS\Fluid\ViewHelpers\ElseViewHelper;
15 use TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
16
17 /**
18 * This view helper is an abstract ViewHelper which implements an if/else condition.
19 * @see TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ViewHelperNode::convertArgumentValue() to find see how boolean arguments are evaluated
20 *
21 * = Usage =
22 *
23 * To create a custom Condition ViewHelper, you need to subclass this class, and
24 * implement your own render() method. Inside there, you should call $this->renderThenChild()
25 * if the condition evaluated to TRUE, and $this->renderElseChild() if the condition evaluated
26 * to FALSE.
27 *
28 * Every Condition ViewHelper has a "then" and "else" argument, so it can be used like:
29 * <[aConditionViewHelperName] .... then="condition true" else="condition false" />,
30 * or as well use the "then" and "else" child nodes.
31 *
32 * @see TYPO3\CMS\Fluid\ViewHelpers\IfViewHelper for a more detailed explanation and a simple usage example.
33 * Make sure to NOT OVERRIDE the constructor.
34 *
35 * @api
36 */
37 abstract class AbstractConditionViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper implements \TYPO3\CMS\Fluid\Core\ViewHelper\Facets\ChildNodeAccessInterface, \TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface {
38
39 /**
40 * An array containing child nodes
41 *
42 * @var array<\TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode>
43 */
44 private $childNodes = array();
45
46 /**
47 * Setter for ChildNodes - as defined in ChildNodeAccessInterface
48 *
49 * @param array $childNodes Child nodes of this syntax tree node
50 * @return void
51 */
52 public function setChildNodes(array $childNodes) {
53 $this->childNodes = $childNodes;
54 }
55
56 /**
57 * Initializes the "then" and "else" arguments
58 */
59 public function __construct() {
60 $this->registerArgument('then', 'mixed', 'Value to be returned if the condition if met.', FALSE);
61 $this->registerArgument('else', 'mixed', 'Value to be returned if the condition if not met.', FALSE);
62 }
63
64 /**
65 * renders <f:then> child if $condition is true, otherwise renders <f:else> child.
66 *
67 * @return string the rendered string
68 * @api
69 */
70 public function render() {
71 if (static::evaluateCondition($this->arguments)) {
72 return $this->renderThenChild();
73 } else {
74 return $this->renderElseChild();
75 }
76 }
77
78 /**
79 * Returns value of "then" attribute.
80 * If then attribute is not set, iterates through child nodes and renders ThenViewHelper.
81 * If then attribute is not set and no ThenViewHelper and no ElseViewHelper is found, all child nodes are rendered
82 *
83 * @return string rendered ThenViewHelper or contents of <f:if> if no ThenViewHelper was found
84 * @api
85 */
86 protected function renderThenChild() {
87 $hasEvaluated = TRUE;
88 $result = static::renderStaticThenChild($this->arguments, $hasEvaluated);
89 if ($hasEvaluated) {
90 return $result;
91 }
92
93 $elseViewHelperEncountered = FALSE;
94 foreach ($this->childNodes as $childNode) {
95 if ($childNode instanceof ViewHelperNode
96 && $childNode->getViewHelperClassName() === ThenViewHelper::class) {
97 $data = $childNode->evaluate($this->renderingContext);
98 return $data;
99 }
100 if ($childNode instanceof ViewHelperNode
101 && $childNode->getViewHelperClassName() === ElseViewHelper::class) {
102 $elseViewHelperEncountered = TRUE;
103 }
104 }
105
106 if ($elseViewHelperEncountered) {
107 return '';
108 } else {
109 return $this->renderChildren();
110 }
111 }
112
113 /**
114 * Statically evalute "then" children.
115 * The "$hasEvaluated" argument is there to distinguish the case that "then" returned NULL or was not evaluated.
116 *
117 * @param array $arguments ViewHelper arguments
118 * @param bool $hasEvaluated Can be used to check if the "then" child was actually evaluated by this method.
119 * @return string
120 */
121 protected static function renderStaticThenChild($arguments, &$hasEvaluated) {
122 if (isset($arguments['then'])) {
123 return $arguments['then'];
124 }
125 if (isset($arguments['__thenClosure'])) {
126 $thenClosure = $arguments['__thenClosure'];
127 return $thenClosure();
128 } elseif (isset($arguments['__elseClosure'])) {
129 return '';
130 }
131
132 $hasEvaluated = FALSE;
133 }
134
135 /**
136 * Returns value of "else" attribute.
137 * If else attribute is not set, iterates through child nodes and renders ElseViewHelper.
138 * If else attribute is not set and no ElseViewHelper is found, an empty string will be returned.
139 *
140 * @return string rendered ElseViewHelper or an empty string if no ThenViewHelper was found
141 * @api
142 */
143 protected function renderElseChild() {
144 $hasEvaluated = TRUE;
145 $result = static::renderStaticElseChild($this->arguments, $hasEvaluated);
146 if ($hasEvaluated) {
147 return $result;
148 }
149
150 foreach ($this->childNodes as $childNode) {
151 if ($childNode instanceof ViewHelperNode
152 && $childNode->getViewHelperClassName() === ElseViewHelper::class) {
153 return $childNode->evaluate($this->renderingContext);
154 }
155 }
156
157 return '';
158 }
159
160
161 /**
162 * Statically evalute "else" children.
163 * The "$hasEvaluated" argument is there to distinguish the case that "else" returned NULL or was not evaluated.
164 *
165 * @param array $arguments ViewHelper arguments
166 * @param bool $hasEvaluated Can be used to check if the "else" child was actually evaluated by this method.
167 * @return string
168 */
169 protected static function renderStaticElseChild($arguments, &$hasEvaluated) {
170 if (isset($arguments['else'])) {
171 return $arguments['else'];
172 }
173 if (isset($arguments['__elseClosure'])) {
174 $elseClosure = $arguments['__elseClosure'];
175 return $elseClosure();
176 }
177
178 $hasEvaluated = FALSE;
179 }
180
181 /**
182 * The compiled ViewHelper adds two new ViewHelper arguments: __thenClosure and __elseClosure.
183 * These contain closures which are be executed to render the then(), respectively else() case.
184 *
185 * @param string $argumentsVariableName
186 * @param string $renderChildrenClosureVariableName
187 * @param string $initializationPhpCode
188 * @param \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode $syntaxTreeNode
189 * @param \TYPO3\CMS\Fluid\Core\Compiler\TemplateCompiler $templateCompiler
190 * @return string
191 * @internal
192 */
193 public function compile($argumentsVariableName, $renderChildrenClosureVariableName, &$initializationPhpCode, \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode $syntaxTreeNode, \TYPO3\CMS\Fluid\Core\Compiler\TemplateCompiler $templateCompiler) {
194 foreach ($syntaxTreeNode->getChildNodes() as $childNode) {
195 if ($childNode instanceof ViewHelperNode
196 && $childNode->getViewHelperClassName() === ThenViewHelper::class) {
197
198 $childNodesAsClosure = $templateCompiler->wrapChildNodesInClosure($childNode);
199 $initializationPhpCode .= sprintf('%s[\'__thenClosure\'] = %s;', $argumentsVariableName, $childNodesAsClosure) . LF;
200 }
201 if ($childNode instanceof ViewHelperNode
202 && $childNode->getViewHelperClassName() === ElseViewHelper::class) {
203
204 $childNodesAsClosure = $templateCompiler->wrapChildNodesInClosure($childNode);
205 $initializationPhpCode .= sprintf('%s[\'__elseClosure\'] = %s;', $argumentsVariableName, $childNodesAsClosure) . LF;
206 }
207 }
208
209 return sprintf('%s::renderStatic(%s, %s, $renderingContext)',
210 get_class($this), $argumentsVariableName, $renderChildrenClosureVariableName);
211 }
212
213 /**
214 * Default implementation for CompilableInterface. See CompilableInterface
215 * for a detailed description of this method.
216 *
217 * @param array $arguments
218 * @param \Closure $renderChildrenClosure
219 * @param \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
220 * @return mixed
221 * @see \TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface
222 */
223 static public function renderStatic(array $arguments, \Closure $renderChildrenClosure, \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext) {
224 $hasEvaluated = TRUE;
225 if (static::evaluateCondition($arguments)) {
226 $result = static::renderStaticThenChild($arguments, $hasEvaluated);
227 if ($hasEvaluated) {
228 return $result;
229 }
230
231 return $renderChildrenClosure();
232 } else {
233 $result = static::renderStaticElseChild($arguments, $hasEvaluated);
234 if ($hasEvaluated) {
235 return $result;
236 }
237 }
238
239 return '';
240 }
241
242 /**
243 * This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
244 *
245 * @param array $arguments ViewHelper arguments to evaluate the condition for this ViewHelper, allows for flexiblity in overriding this method.
246 * @return bool
247 */
248 static protected function evaluateCondition($arguments = NULL) {
249 return (isset($arguments['condition']) && $arguments['condition']);
250 }
251 }