1
|
|
|
<?php |
2
|
|
|
namespace TYPO3Fluid\Fluid\Core\ViewHelper; |
3
|
|
|
|
4
|
|
|
/* |
5
|
|
|
* This file belongs to the package "TYPO3 Fluid". |
6
|
|
|
* See LICENSE.txt that was shipped with this package. |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
use TYPO3Fluid\Fluid\Core\Compiler\TemplateCompiler; |
10
|
|
|
use TYPO3Fluid\Fluid\Core\Parser\ParsedTemplateInterface; |
11
|
|
|
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\AbstractNode; |
12
|
|
|
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\BooleanNode; |
13
|
|
|
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\NodeInterface; |
14
|
|
|
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\TextNode; |
15
|
|
|
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode; |
16
|
|
|
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; |
17
|
|
|
use TYPO3Fluid\Fluid\Core\Variables\VariableProviderInterface; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* The abstract base class for all view helpers. |
21
|
|
|
* |
22
|
|
|
* @api |
23
|
|
|
*/ |
24
|
|
|
abstract class AbstractViewHelper extends AbstractNode implements ViewHelperInterface |
25
|
|
|
{ |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Stores all \TYPO3Fluid\Fluid\ArgumentDefinition instances |
29
|
|
|
* @var ArgumentDefinition[] |
30
|
|
|
*/ |
31
|
|
|
protected $argumentDefinitions = []; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Cache of argument definitions; the key is the ViewHelper class name, and the |
35
|
|
|
* value is the array of argument definitions. |
36
|
|
|
* |
37
|
|
|
* In our benchmarks, this cache leads to a 40% improvement when using a certain |
38
|
|
|
* ViewHelper class many times throughout the rendering process. |
39
|
|
|
* @var array |
40
|
|
|
*/ |
41
|
|
|
static private $argumentDefinitionCache = []; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Current view helper node |
45
|
|
|
* @var ViewHelperNode |
46
|
|
|
*/ |
47
|
|
|
protected $viewHelperNode; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Arguments array. |
51
|
|
|
* @var array |
52
|
|
|
* @api |
53
|
|
|
*/ |
54
|
|
|
protected $arguments = []; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var array |
58
|
|
|
*/ |
59
|
|
|
protected $parsedArguments = []; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var NodeInterface[] array |
63
|
|
|
* @api |
64
|
|
|
*/ |
65
|
|
|
protected $childNodes = []; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Current variable container reference. |
69
|
|
|
* @var VariableProviderInterface |
70
|
|
|
* @api |
71
|
|
|
*/ |
72
|
|
|
protected $templateVariableContainer; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @var RenderingContextInterface |
76
|
|
|
*/ |
77
|
|
|
protected $renderingContext; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @var \Closure |
81
|
|
|
*/ |
82
|
|
|
protected $renderChildrenClosure = null; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* ViewHelper Variable Container |
86
|
|
|
* @var ViewHelperVariableContainer |
87
|
|
|
* @api |
88
|
|
|
*/ |
89
|
|
|
protected $viewHelperVariableContainer; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Specifies whether the escaping interceptors should be disabled or enabled for the result of renderChildren() calls within this ViewHelper |
93
|
|
|
* @see isChildrenEscapingEnabled() |
94
|
|
|
* |
95
|
|
|
* Note: If this is NULL the value of $this->escapingInterceptorEnabled is considered for backwards compatibility |
96
|
|
|
* |
97
|
|
|
* @var boolean |
98
|
|
|
* @api |
99
|
|
|
*/ |
100
|
|
|
protected $escapeChildren = null; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Specifies whether the escaping interceptors should be disabled or enabled for the render-result of this ViewHelper |
104
|
|
|
* @see isOutputEscapingEnabled() |
105
|
|
|
* |
106
|
|
|
* @var boolean |
107
|
|
|
* @api |
108
|
|
|
*/ |
109
|
|
|
protected $escapeOutput = null; |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* @param array $arguments |
113
|
|
|
* @param ParsedTemplateInterface $parsedTemplate |
114
|
|
|
* @param RenderingContextInterface $renderingContext |
115
|
|
|
* @return NodeInterface |
116
|
|
|
*/ |
117
|
|
|
public function postParse(array $arguments, ParsedTemplateInterface $parsedTemplate, RenderingContextInterface $renderingContext): NodeInterface |
118
|
|
|
{ |
119
|
|
|
$this->renderingContext = $renderingContext; |
120
|
|
|
$this->templateVariableContainer = $renderingContext->getVariableProvider(); |
121
|
|
|
$this->viewHelperVariableContainer = $renderingContext->getViewHelperVariableContainer(); |
122
|
|
|
$this->validateParsedArguments($arguments); |
123
|
|
|
return $this->setParsedArguments($arguments); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
public function setParsedArguments(array $parsedArguments): NodeInterface |
127
|
|
|
{ |
128
|
|
|
$this->parsedArguments = $this->createArguments($parsedArguments); |
129
|
|
|
return $this; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
public function getParsedArguments(): array |
133
|
|
|
{ |
134
|
|
|
return $this->parsedArguments; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
public function evaluate(RenderingContextInterface $renderingContext) |
138
|
|
|
{ |
139
|
|
|
$this->setRenderingContext($renderingContext); |
140
|
|
|
|
141
|
|
|
$arguments = $this->parsedArguments; |
142
|
|
View Code Duplication |
foreach ($this->prepareArguments() as $argumentName => $argumentDefinition) { |
|
|
|
|
143
|
|
|
$argumentValue = $arguments[$argumentName] ?? null; |
144
|
|
|
$arguments[$argumentName] = $argumentValue instanceof NodeInterface ? $argumentValue->evaluate($renderingContext) : $argumentValue; |
145
|
|
|
} |
146
|
|
|
$this->setArguments($arguments); |
147
|
|
|
return $this->initializeArgumentsAndRender(); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* @param NodeInterface[]|mixed[] $arguments |
152
|
|
|
* @throws Exception |
153
|
|
|
*/ |
154
|
|
View Code Duplication |
protected function validateParsedArguments(array $arguments) |
|
|
|
|
155
|
|
|
{ |
156
|
|
|
$additionalArguments = []; |
157
|
|
|
$argumentDefinitions = $this->prepareArguments(); |
158
|
|
|
foreach ($arguments as $argumentName => $value) { |
159
|
|
|
if (!array_key_exists($argumentName, $argumentDefinitions)) { |
160
|
|
|
$additionalArguments[$argumentName] = $value; |
161
|
|
|
} |
162
|
|
|
} |
163
|
|
|
$this->validateAdditionalArguments($additionalArguments); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Creates arguments by padding with missing+optional arguments |
168
|
|
|
* and casting or creating BooleanNode where appropriate. Input |
169
|
|
|
* array may not contain all arguments - output array will. |
170
|
|
|
* |
171
|
|
|
* @param array $arguments |
172
|
|
|
* @return array |
173
|
|
|
*/ |
174
|
|
|
protected function createArguments(array $arguments): array |
175
|
|
|
{ |
176
|
|
|
$definitions = $this->prepareArguments(); |
177
|
|
|
$missingArguments = []; |
178
|
|
|
foreach ($definitions as $name => $definition) { |
179
|
|
|
$argument = &$arguments[$name] ?? null; |
180
|
|
|
if ($definition->isRequired() && !isset($argument)) { |
181
|
|
|
// Required but missing argument, causes failure (delayed, to report all missing arguments at once) |
182
|
|
|
$missingArguments[] = $name; |
183
|
|
|
} elseif (!isset($argument)) { |
184
|
|
|
// Argument is optional (required filtered out above), fit it with the default value |
185
|
|
|
$argument = $definition->getDefaultValue(); |
186
|
|
|
} elseif (($type = $definition->getType()) && ($type === 'bool' || $type === 'boolean')) { |
187
|
|
|
// Cast the value or create a BooleanNode |
188
|
|
|
$argument = is_bool($argument) || is_numeric($argument) ? (bool)$argument : new BooleanNode($argument); |
189
|
|
|
} |
190
|
|
|
$arguments[$name] = $argument; |
191
|
|
|
} |
192
|
|
|
if (!empty($missingArguments)) { |
193
|
|
|
throw new \TYPO3Fluid\Fluid\Core\Parser\Exception('Required argument(s) not provided: ' . implode(', ', $missingArguments), 1558533510); |
194
|
|
|
} |
195
|
|
|
return $arguments; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* @param array $arguments |
200
|
|
|
* @return void |
201
|
|
|
*/ |
202
|
|
|
public function setArguments(array $arguments) |
203
|
|
|
{ |
204
|
|
|
$this->arguments = $arguments; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* @param RenderingContextInterface $renderingContext |
209
|
|
|
* @return void |
210
|
|
|
*/ |
211
|
|
|
public function setRenderingContext(RenderingContextInterface $renderingContext) |
212
|
|
|
{ |
213
|
|
|
$this->renderingContext = $renderingContext; |
214
|
|
|
$this->templateVariableContainer = $renderingContext->getVariableProvider(); |
215
|
|
|
$this->viewHelperVariableContainer = $renderingContext->getViewHelperVariableContainer(); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Returns whether the escaping interceptors should be disabled or enabled for the result of renderChildren() calls within this ViewHelper |
220
|
|
|
* |
221
|
|
|
* Note: This method is no public API, use $this->escapeChildren instead! |
222
|
|
|
* |
223
|
|
|
* @return boolean |
224
|
|
|
*/ |
225
|
|
|
public function isChildrenEscapingEnabled() |
226
|
|
|
{ |
227
|
|
|
if ($this->escapeChildren === null) { |
228
|
|
|
// Disable children escaping automatically, if output escaping is on anyway. |
229
|
|
|
return !$this->isOutputEscapingEnabled(); |
230
|
|
|
} |
231
|
|
|
return $this->escapeChildren; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* Returns whether the escaping interceptors should be disabled or enabled for the render-result of this ViewHelper |
236
|
|
|
* |
237
|
|
|
* Note: This method is no public API, use $this->escapeChildren instead! |
238
|
|
|
* |
239
|
|
|
* @return boolean |
240
|
|
|
*/ |
241
|
|
|
public function isOutputEscapingEnabled() |
242
|
|
|
{ |
243
|
|
|
return $this->escapeOutput !== false; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Register a new argument. Call this method from your ViewHelper subclass |
248
|
|
|
* inside the initializeArguments() method. |
249
|
|
|
* |
250
|
|
|
* @param string $name Name of the argument |
251
|
|
|
* @param string $type Type of the argument |
252
|
|
|
* @param string $description Description of the argument |
253
|
|
|
* @param boolean $required If TRUE, argument is required. Defaults to FALSE. |
254
|
|
|
* @param mixed $defaultValue Default value of argument |
255
|
|
|
* @return \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper $this, to allow chaining. |
256
|
|
|
* @throws Exception |
257
|
|
|
* @api |
258
|
|
|
*/ |
259
|
|
View Code Duplication |
protected function registerArgument($name, $type, $description, $required = false, $defaultValue = null) |
|
|
|
|
260
|
|
|
{ |
261
|
|
|
if (array_key_exists($name, $this->argumentDefinitions)) { |
262
|
|
|
throw new Exception( |
263
|
|
|
'Argument "' . $name . '" has already been defined, thus it should not be defined again.', |
264
|
|
|
1253036401 |
265
|
|
|
); |
266
|
|
|
} |
267
|
|
|
$this->argumentDefinitions[$name] = new ArgumentDefinition($name, $type, $description, $required, $defaultValue); |
268
|
|
|
return $this; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* Overrides a registered argument. Call this method from your ViewHelper subclass |
273
|
|
|
* inside the initializeArguments() method if you want to override a previously registered argument. |
274
|
|
|
* @see registerArgument() |
275
|
|
|
* |
276
|
|
|
* @param string $name Name of the argument |
277
|
|
|
* @param string $type Type of the argument |
278
|
|
|
* @param string $description Description of the argument |
279
|
|
|
* @param boolean $required If TRUE, argument is required. Defaults to FALSE. |
280
|
|
|
* @param mixed $defaultValue Default value of argument |
281
|
|
|
* @return \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper $this, to allow chaining. |
282
|
|
|
* @throws Exception |
283
|
|
|
* @api |
284
|
|
|
*/ |
285
|
|
View Code Duplication |
protected function overrideArgument($name, $type, $description, $required = false, $defaultValue = null) |
|
|
|
|
286
|
|
|
{ |
287
|
|
|
if (!array_key_exists($name, $this->argumentDefinitions)) { |
288
|
|
|
throw new Exception( |
289
|
|
|
'Argument "' . $name . '" has not been defined, thus it can\'t be overridden.', |
290
|
|
|
1279212461 |
291
|
|
|
); |
292
|
|
|
} |
293
|
|
|
$this->argumentDefinitions[$name] = new ArgumentDefinition($name, $type, $description, $required, $defaultValue); |
294
|
|
|
return $this; |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
/** |
298
|
|
|
* Sets all needed attributes needed for the rendering. Called by the |
299
|
|
|
* framework. Populates $this->viewHelperNode. |
300
|
|
|
* This is PURELY INTERNAL! Never override this method!! |
301
|
|
|
* |
302
|
|
|
* @param ViewHelperNode $node View Helper node to be set. |
303
|
|
|
* @return void |
304
|
|
|
*/ |
305
|
|
|
public function setViewHelperNode(ViewHelperNode $node) |
306
|
|
|
{ |
307
|
|
|
$this->viewHelperNode = $node; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Sets all needed attributes needed for the rendering. Called by the |
312
|
|
|
* framework. Populates $this->viewHelperNode. |
313
|
|
|
* This is PURELY INTERNAL! Never override this method!! |
314
|
|
|
* |
315
|
|
|
* @param NodeInterface[] $childNodes |
316
|
|
|
* @return void |
317
|
|
|
*/ |
318
|
|
|
public function setChildNodes(array $childNodes) |
319
|
|
|
{ |
320
|
|
|
$this->childNodes = $childNodes; |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
/** |
324
|
|
|
* Called when being inside a cached template. |
325
|
|
|
* |
326
|
|
|
* @param \Closure $renderChildrenClosure |
327
|
|
|
* @return void |
328
|
|
|
*/ |
329
|
|
|
public function setRenderChildrenClosure(\Closure $renderChildrenClosure) |
330
|
|
|
{ |
331
|
|
|
$this->renderChildrenClosure = $renderChildrenClosure; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* Initialize the arguments of the ViewHelper, and call the render() method of the ViewHelper. |
336
|
|
|
* |
337
|
|
|
* @return string the rendered ViewHelper. |
338
|
|
|
*/ |
339
|
|
|
public function initializeArgumentsAndRender() |
340
|
|
|
{ |
341
|
|
|
$this->validateArguments(); |
342
|
|
|
$this->initialize(); |
343
|
|
|
|
344
|
|
|
return $this->callRenderMethod(); |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* Call the render() method and handle errors. |
349
|
|
|
* |
350
|
|
|
* @return string the rendered ViewHelper |
351
|
|
|
* @throws Exception |
352
|
|
|
*/ |
353
|
|
|
protected function callRenderMethod() |
354
|
|
|
{ |
355
|
|
|
if (method_exists($this, 'render')) { |
356
|
|
|
return call_user_func([$this, 'render']); |
357
|
|
|
} |
358
|
|
|
if ((new \ReflectionMethod($this, 'renderStatic'))->getDeclaringClass()->getName() !== AbstractViewHelper::class) { |
|
|
|
|
359
|
|
|
// Method is safe to call - will not recurse through ViewHelperInvoker via the default |
360
|
|
|
// implementation of renderStatic() on this class. |
361
|
|
|
return static::renderStatic($this->arguments, $this->buildRenderChildrenClosure(), $this->renderingContext); |
362
|
|
|
} |
363
|
|
|
throw new Exception( |
364
|
|
|
sprintf( |
365
|
|
|
'ViewHelper class "%s" does not declare a "render()" method and inherits the default "renderStatic". ' . |
366
|
|
|
'Exceuting this ViewHelper would cause infinite recursion - please either implement "render()" or ' . |
367
|
|
|
'"renderStatic()" on your ViewHelper class', |
368
|
|
|
get_class($this) |
369
|
|
|
) |
370
|
|
|
); |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* Initializes the view helper before invoking the render method. |
375
|
|
|
* |
376
|
|
|
* Override this method to solve tasks before the view helper content is rendered. |
377
|
|
|
* |
378
|
|
|
* @return void |
379
|
|
|
* @api |
380
|
|
|
*/ |
381
|
|
|
public function initialize() |
382
|
|
|
{ |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* Helper method which triggers the rendering of everything between the |
387
|
|
|
* opening and the closing tag. |
388
|
|
|
* |
389
|
|
|
* @return mixed The finally rendered child nodes. |
390
|
|
|
* @api |
391
|
|
|
*/ |
392
|
|
|
public function renderChildren() |
393
|
|
|
{ |
394
|
|
|
if ($this->renderChildrenClosure !== null) { |
395
|
|
|
$closure = $this->renderChildrenClosure; |
396
|
|
|
return $closure(); |
397
|
|
|
} |
398
|
|
|
return ($this->viewHelperNode ?? $this)->evaluateChildNodes($this->renderingContext); |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
/** |
402
|
|
|
* Helper which is mostly needed when calling renderStatic() from within |
403
|
|
|
* render(). |
404
|
|
|
* |
405
|
|
|
* No public API yet. |
406
|
|
|
* |
407
|
|
|
* @return \Closure |
408
|
|
|
*/ |
409
|
|
|
protected function buildRenderChildrenClosure() |
410
|
|
|
{ |
411
|
|
|
$self = clone $this; |
412
|
|
|
return function() use ($self) { |
413
|
|
|
return $self->renderChildren(); |
414
|
|
|
}; |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
/** |
418
|
|
|
* Initialize all arguments and return them |
419
|
|
|
* |
420
|
|
|
* @return ArgumentDefinition[] |
421
|
|
|
*/ |
422
|
|
|
public function prepareArguments() |
423
|
|
|
{ |
424
|
|
|
$thisClassName = get_class($this); |
425
|
|
|
if (isset(self::$argumentDefinitionCache[$thisClassName])) { |
426
|
|
|
$this->argumentDefinitions = self::$argumentDefinitionCache[$thisClassName]; |
427
|
|
|
} else { |
428
|
|
|
$this->initializeArguments(); |
429
|
|
|
self::$argumentDefinitionCache[$thisClassName] = $this->argumentDefinitions; |
430
|
|
|
} |
431
|
|
|
return $this->argumentDefinitions; |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
/** |
435
|
|
|
* Validate arguments, and throw exception if arguments do not validate. |
436
|
|
|
* |
437
|
|
|
* @return void |
438
|
|
|
* @throws \InvalidArgumentException |
439
|
|
|
*/ |
440
|
|
|
public function validateArguments() |
441
|
|
|
{ |
442
|
|
|
$argumentDefinitions = $this->prepareArguments(); |
443
|
|
|
foreach ($argumentDefinitions as $argumentName => $registeredArgument) { |
444
|
|
|
if ($this->hasArgument($argumentName)) { |
445
|
|
|
$value = $this->arguments[$argumentName]; |
446
|
|
|
$type = $registeredArgument->getType(); |
447
|
|
|
if ($value !== $registeredArgument->getDefaultValue() && $type !== 'mixed') { |
448
|
|
|
$givenType = is_object($value) ? get_class($value) : gettype($value); |
449
|
|
|
if (!$this->isValidType($type, $value)) { |
450
|
|
|
throw new \InvalidArgumentException( |
451
|
|
|
'The argument "' . $argumentName . '" was registered with type "' . $type . '", but is of type "' . |
452
|
|
|
$givenType . '" in view helper "' . get_class($this) . '". Value: ' . var_export($value, true), |
453
|
|
|
1256475113 |
454
|
|
|
); |
455
|
|
|
} |
456
|
|
|
} |
457
|
|
|
} |
458
|
|
|
} |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
/** |
462
|
|
|
* Check whether the defined type matches the value type |
463
|
|
|
* |
464
|
|
|
* @param string $type |
465
|
|
|
* @param mixed $value |
466
|
|
|
* @return boolean |
467
|
|
|
*/ |
468
|
|
|
protected function isValidType($type, $value) |
469
|
|
|
{ |
470
|
|
|
if ($type === 'object') { |
471
|
|
|
if (!is_object($value)) { |
472
|
|
|
return false; |
473
|
|
|
} |
474
|
|
|
} elseif ($type === 'array' || substr($type, -2) === '[]') { |
475
|
|
|
if (!is_array($value) && !$value instanceof \ArrayAccess && !$value instanceof \Traversable && !empty($value)) { |
476
|
|
|
return false; |
477
|
|
|
} elseif (substr($type, -2) === '[]') { |
478
|
|
|
$firstElement = $this->getFirstElementOfNonEmpty($value); |
479
|
|
|
if ($firstElement === null) { |
480
|
|
|
return true; |
481
|
|
|
} |
482
|
|
|
return $this->isValidType(substr($type, 0, -2), $firstElement); |
483
|
|
|
} |
484
|
|
|
} elseif ($type === 'string') { |
485
|
|
|
if (is_object($value) && !method_exists($value, '__toString')) { |
486
|
|
|
return false; |
487
|
|
|
} |
488
|
|
|
} elseif ($type === 'boolean' && !is_bool($value)) { |
489
|
|
|
return false; |
490
|
|
|
} elseif (class_exists($type) && $value !== null && !$value instanceof $type) { |
491
|
|
|
return false; |
492
|
|
|
} elseif (is_object($value) && !is_a($value, $type, true)) { |
493
|
|
|
return false; |
494
|
|
|
} |
495
|
|
|
return true; |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
/** |
499
|
|
|
* Return the first element of the given array, ArrayAccess or Traversable |
500
|
|
|
* that is not empty |
501
|
|
|
* |
502
|
|
|
* @param mixed $value |
503
|
|
|
* @return mixed |
504
|
|
|
*/ |
505
|
|
|
protected function getFirstElementOfNonEmpty($value) |
506
|
|
|
{ |
507
|
|
|
if (is_array($value)) { |
508
|
|
|
return reset($value); |
509
|
|
|
} elseif ($value instanceof \Traversable) { |
510
|
|
|
foreach ($value as $element) { |
511
|
|
|
return $element; |
512
|
|
|
} |
513
|
|
|
} |
514
|
|
|
return null; |
515
|
|
|
} |
516
|
|
|
|
517
|
|
|
/** |
518
|
|
|
* Initialize all arguments. You need to override this method and call |
519
|
|
|
* $this->registerArgument(...) inside this method, to register all your arguments. |
520
|
|
|
* |
521
|
|
|
* @return void |
522
|
|
|
* @api |
523
|
|
|
*/ |
524
|
|
|
public function initializeArguments() |
525
|
|
|
{ |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
/** |
529
|
|
|
* Tests if the given $argumentName is set, and not NULL. |
530
|
|
|
* The isset() test used fills both those requirements. |
531
|
|
|
* |
532
|
|
|
* @param string $argumentName |
533
|
|
|
* @return boolean TRUE if $argumentName is found, FALSE otherwise |
534
|
|
|
* @api |
535
|
|
|
*/ |
536
|
|
|
protected function hasArgument($argumentName) |
537
|
|
|
{ |
538
|
|
|
return isset($this->arguments[$argumentName]); |
539
|
|
|
} |
540
|
|
|
|
541
|
|
|
/** |
542
|
|
|
* Default implementation of "handling" additional, undeclared arguments. |
543
|
|
|
* In this implementation the behavior is to consistently throw an error |
544
|
|
|
* about NOT supporting any additional arguments. This method MUST be |
545
|
|
|
* overridden by any ViewHelper that desires this support and this inherited |
546
|
|
|
* method must not be called, obviously. |
547
|
|
|
* |
548
|
|
|
* @throws Exception |
549
|
|
|
* @param array $arguments |
550
|
|
|
* @return void |
551
|
|
|
*/ |
552
|
|
|
public function handleAdditionalArguments(array $arguments) |
553
|
|
|
{ |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
/** |
557
|
|
|
* Default implementation of validating additional, undeclared arguments. |
558
|
|
|
* In this implementation the behavior is to consistently throw an error |
559
|
|
|
* about NOT supporting any additional arguments. This method MUST be |
560
|
|
|
* overridden by any ViewHelper that desires this support and this inherited |
561
|
|
|
* method must not be called, obviously. |
562
|
|
|
* |
563
|
|
|
* @throws Exception |
564
|
|
|
* @param array $arguments |
565
|
|
|
* @return void |
566
|
|
|
*/ |
567
|
|
|
public function validateAdditionalArguments(array $arguments) |
568
|
|
|
{ |
569
|
|
|
if (!empty($arguments)) { |
570
|
|
|
throw new Exception( |
571
|
|
|
sprintf( |
572
|
|
|
'Undeclared arguments passed to ViewHelper %s: %s. Valid arguments are: %s', |
573
|
|
|
get_class($this), |
574
|
|
|
implode(', ', array_keys($arguments)), |
575
|
|
|
implode(', ', array_keys($this->argumentDefinitions)) |
576
|
|
|
) |
577
|
|
|
); |
578
|
|
|
} |
579
|
|
|
} |
580
|
|
|
|
581
|
|
|
/** |
582
|
|
|
* You only should override this method *when you absolutely know what you |
583
|
|
|
* are doing*, and really want to influence the generated PHP code during |
584
|
|
|
* template compilation directly. |
585
|
|
|
* |
586
|
|
|
* @param string $argumentsName |
587
|
|
|
* @param string $closureName |
588
|
|
|
* @param string $initializationPhpCode |
589
|
|
|
* @param ViewHelperNode $node |
590
|
|
|
* @param TemplateCompiler $compiler |
591
|
|
|
* @return string |
592
|
|
|
*/ |
593
|
|
|
public function compile($argumentsName, $closureName, &$initializationPhpCode, ViewHelperNode $node, TemplateCompiler $compiler) |
594
|
|
|
{ |
595
|
|
|
return sprintf( |
596
|
|
|
'%s::renderStatic(%s, %s, $renderingContext)', |
597
|
|
|
get_class($this), |
598
|
|
|
$argumentsName, |
599
|
|
|
$closureName |
600
|
|
|
); |
601
|
|
|
} |
602
|
|
|
|
603
|
|
|
/** |
604
|
|
|
* Default implementation of static rendering; useful API method if your ViewHelper |
605
|
|
|
* when compiled is able to render itself statically to increase performance. This |
606
|
|
|
* default implementation will simply delegate to the ViewHelperInvoker. |
607
|
|
|
* |
608
|
|
|
* @param array $arguments |
609
|
|
|
* @param \Closure $renderChildrenClosure |
610
|
|
|
* @param RenderingContextInterface $renderingContext |
611
|
|
|
* @return mixed |
612
|
|
|
*/ |
613
|
|
|
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) |
614
|
|
|
{ |
615
|
|
|
$viewHelperClassName = get_called_class(); |
616
|
|
|
return $renderingContext->getViewHelperInvoker()->invoke($viewHelperClassName, $arguments, $renderingContext, $renderChildrenClosure); |
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
/** |
620
|
|
|
* Save the associated ViewHelper node in a static public class variable. |
621
|
|
|
* called directly after the ViewHelper was built. |
622
|
|
|
* |
623
|
|
|
* @param ViewHelperNode $node |
624
|
|
|
* @param TextNode[] $arguments |
625
|
|
|
* @param VariableProviderInterface $variableContainer |
626
|
|
|
* @return void |
627
|
|
|
*/ |
628
|
|
|
public static function postParseEvent(ViewHelperNode $node, array $arguments, VariableProviderInterface $variableContainer) |
629
|
|
|
{ |
630
|
|
|
} |
631
|
|
|
|
632
|
|
|
/** |
633
|
|
|
* Resets the ViewHelper state. |
634
|
|
|
* |
635
|
|
|
* Overwrite this method if you need to get a clean state of your ViewHelper. |
636
|
|
|
* |
637
|
|
|
* @return void |
638
|
|
|
*/ |
639
|
|
|
public function resetState() |
640
|
|
|
{ |
641
|
|
|
} |
642
|
|
|
} |
643
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.