Completed
Pull Request — master (#332)
by Claus
02:05
created

AbstractViewHelper::setArguments()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
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;
11
use TYPO3Fluid\Fluid\Core\Event\EventInterface;
12
use TYPO3Fluid\Fluid\Core\Event\PostParseEvent;
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
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\DeprecatedViewHelper;
19
20
/**
21
 * The abstract base class for all view helpers.
22
 *
23
 * @api
24
 */
25
abstract class AbstractViewHelper implements ViewHelperInterface, DeprecatedViewHelperInterface
26
{
27
    use DeprecatedViewHelper;
28
29
    /**
30
     * Stores all \TYPO3Fluid\Fluid\ArgumentDefinition instances
31
     * @var ArgumentDefinition[]
32
     */
33
    protected $argumentDefinitions = [];
34
35
    /**
36
     * Cache of argument definitions; the key is the ViewHelper class name, and the
37
     * value is the array of argument definitions.
38
     *
39
     * In our benchmarks, this cache leads to a 40% improvement when using a certain
40
     * ViewHelper class many times throughout the rendering process.
41
     * @var array
42
     */
43
    static private $argumentDefinitionCache = [];
44
45
    /**
46
     * Current view helper node
47
     * @var ViewHelperNode
48
     */
49
    protected $viewHelperNode;
50
51
    /**
52
     * Arguments array.
53
     * @var array
54
     * @api
55
     */
56
    protected $arguments = [];
57
58
    /**
59
     * @var RenderingContextInterface
60
     */
61
    protected $renderingContext;
62
63
    /**
64
     * @var \Closure
65
     */
66
    protected $renderChildrenClosure = null;
67
68
    /**
69
     * Specifies whether the escaping interceptors should be disabled or enabled for the result of renderChildren() calls within this ViewHelper
70
     * @see isChildrenEscapingEnabled()
71
     *
72
     * Note: If this is NULL the value of $this->escapingInterceptorEnabled is considered for backwards compatibility
73
     *
74
     * @var boolean
75
     * @api
76
     */
77
    protected $escapeChildren = null;
78
79
    /**
80
     * Specifies whether the escaping interceptors should be disabled or enabled for the render-result of this ViewHelper
81
     * @see isOutputEscapingEnabled()
82
     *
83
     * @var boolean
84
     * @api
85
     */
86
    protected $escapeOutput = null;
87
88
    /**
89
     * @param array $arguments
90
     * @return void
91
     */
92
    public function setArguments(array $arguments)
93
    {
94
        $this->arguments = $arguments;
95
    }
96
97
    /**
98
     * @param RenderingContextInterface $renderingContext
99
     * @return void
100
     */
101
    public function setRenderingContext(RenderingContextInterface $renderingContext)
102
    {
103
        $this->renderingContext = $renderingContext;
104
    }
105
106
    /**
107
     * Returns whether the escaping interceptors should be disabled or enabled for the result of renderChildren() calls within this ViewHelper
108
     *
109
     * Note: This method is no public API, use $this->escapeChildren instead!
110
     *
111
     * @return boolean
112
     */
113
    public function isChildrenEscapingEnabled()
114
    {
115
        if ($this->escapeChildren === null) {
116
            // Disable children escaping automatically, if output escaping is on anyway.
117
            return !$this->isOutputEscapingEnabled();
118
        }
119
        return $this->escapeChildren;
120
    }
121
122
    /**
123
     * Returns whether the escaping interceptors should be disabled or enabled for the render-result of this ViewHelper
124
     *
125
     * Note: This method is no public API, use $this->escapeChildren instead!
126
     *
127
     * @return boolean
128
     */
129
    public function isOutputEscapingEnabled()
130
    {
131
        return $this->escapeOutput !== false;
132
    }
133
134
    /**
135
     * Register a new argument. Call this method from your ViewHelper subclass
136
     * inside the initializeArguments() method.
137
     *
138
     * @param string $name Name of the argument
139
     * @param string $type Type of the argument
140
     * @param string $description Description of the argument
141
     * @param boolean $required If TRUE, argument is required. Defaults to FALSE.
142
     * @param mixed $defaultValue Default value of argument
143
     * @return \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper $this, to allow chaining.
144
     * @throws Exception
145
     * @api
146
     */
147 View Code Duplication
    protected function registerArgument($name, $type, $description, $required = false, $defaultValue = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
148
    {
149
        if (array_key_exists($name, $this->argumentDefinitions)) {
150
            throw new Exception(
151
                'Argument "' . $name . '" has already been defined, thus it should not be defined again.',
152
                1253036401
153
            );
154
        }
155
        $this->argumentDefinitions[$name] = new ArgumentDefinition($name, $type, $description, $required, $defaultValue);
156
        return $this;
157
    }
158
159
    /**
160
     * Overrides a registered argument. Call this method from your ViewHelper subclass
161
     * inside the initializeArguments() method if you want to override a previously registered argument.
162
     * @see registerArgument()
163
     *
164
     * @param string $name Name of the argument
165
     * @param string $type Type of the argument
166
     * @param string $description Description of the argument
167
     * @param boolean $required If TRUE, argument is required. Defaults to FALSE.
168
     * @param mixed $defaultValue Default value of argument
169
     * @return \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper $this, to allow chaining.
170
     * @throws Exception
171
     * @api
172
     */
173 View Code Duplication
    protected function overrideArgument($name, $type, $description, $required = false, $defaultValue = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
174
    {
175
        if (!array_key_exists($name, $this->argumentDefinitions)) {
176
            throw new Exception(
177
                'Argument "' . $name . '" has not been defined, thus it can\'t be overridden.',
178
                1279212461
179
            );
180
        }
181
        $this->argumentDefinitions[$name] = new ArgumentDefinition($name, $type, $description, $required, $defaultValue);
182
        return $this;
183
    }
184
185
    /**
186
     * Sets all needed attributes needed for the rendering. Called by the
187
     * framework. Populates $this->viewHelperNode.
188
     * This is PURELY INTERNAL! Never override this method!!
189
     *
190
     * @param ViewHelperNode $node View Helper node to be set.
191
     * @return void
192
     */
193
    public function setViewHelperNode(ViewHelperNode $node)
194
    {
195
        $this->viewHelperNode = $node;
196
    }
197
198
    /**
199
     * Called when being inside a cached template.
200
     *
201
     * @param \Closure $renderChildrenClosure
202
     * @return void
203
     */
204
    public function setRenderChildrenClosure(\Closure $renderChildrenClosure)
205
    {
206
        $this->renderChildrenClosure = $renderChildrenClosure;
207
    }
208
209
    /**
210
     * Initialize the arguments of the ViewHelper, and call the render() method of the ViewHelper.
211
     *
212
     * @return string the rendered ViewHelper.
213
     */
214
    public function initializeArgumentsAndRender()
215
    {
216
        $this->validateArguments();
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Vi...er::validateArguments() has been deprecated.

This method has been deprecated.

Loading history...
217
        $this->initialize();
218
219
        return $this->callRenderMethod();
220
    }
221
222
    /**
223
     * Call the render() method and handle errors.
224
     *
225
     * @return string the rendered ViewHelper
226
     * @throws Exception
227
     */
228
    protected function callRenderMethod()
229
    {
230
        return call_user_func([$this, 'render']);
231
    }
232
233
    /**
234
     * Initializes the view helper before invoking the render method.
235
     *
236
     * Override this method to solve tasks before the view helper content is rendered.
237
     *
238
     * @return void
239
     * @api
240
     */
241
    public function initialize()
242
    {
243
    }
244
245
    /**
246
     * Helper method which triggers the rendering of everything between the
247
     * opening and the closing tag.
248
     *
249
     * @return mixed The finally rendered child nodes.
250
     * @api
251
     */
252
    public function renderChildren()
253
    {
254
        if ($this->renderChildrenClosure !== null) {
255
            $closure = $this->renderChildrenClosure;
256
            return $closure();
257
        }
258
        return $this->viewHelperNode->evaluateChildNodes($this->renderingContext);
259
    }
260
261
    /**
262
     * Helper which is mostly needed when calling renderStatic() from within
263
     * render().
264
     *
265
     * No public API yet.
266
     *
267
     * @return \Closure
268
     */
269
    protected function buildRenderChildrenClosure()
270
    {
271
        $self = clone $this;
272
        return function() use ($self) {
273
            return $self->renderChildren();
274
        };
275
    }
276
277
    /**
278
     * Initialize all arguments and return them
279
     *
280
     * @return ArgumentDefinition[]
281
     */
282
    public function prepareArguments()
283
    {
284
        $thisClassName = get_class($this);
285
        if (isset(self::$argumentDefinitionCache[$thisClassName])) {
286
            $this->argumentDefinitions = self::$argumentDefinitionCache[$thisClassName];
287
        } else {
288
            $this->initializeArguments();
289
            self::$argumentDefinitionCache[$thisClassName] = $this->argumentDefinitions;
290
        }
291
        return $this->argumentDefinitions;
292
    }
293
294
    /**
295
     * Initialize all arguments. You need to override this method and call
296
     * $this->registerArgument(...) inside this method, to register all your arguments.
297
     *
298
     * @return void
299
     * @api
300
     */
301
    public function initializeArguments()
302
    {
303
    }
304
305
    /**
306
     * Tests if the given $argumentName is set, and not NULL.
307
     * The isset() test used fills both those requirements.
308
     *
309
     * @param string $argumentName
310
     * @return boolean TRUE if $argumentName is found, FALSE otherwise
311
     * @api
312
     */
313
    protected function hasArgument($argumentName)
314
    {
315
        return isset($this->arguments[$argumentName]);
316
    }
317
318
    /**
319
     * Default implementation of "handling" additional, undeclared arguments.
320
     * In this implementation the behavior is to consistently throw an error
321
     * about NOT supporting any additional arguments. This method MUST be
322
     * overridden by any ViewHelper that desires this support and this inherited
323
     * method must not be called, obviously.
324
     *
325
     * @throws Exception
326
     * @param array $arguments
327
     * @return void
328
     */
329
    public function handleAdditionalArguments(array $arguments)
330
    {
331
    }
332
333
    /**
334
     * Default implementation of validating additional, undeclared arguments.
335
     * In this implementation the behavior is to consistently throw an error
336
     * about NOT supporting any additional arguments. This method MUST be
337
     * overridden by any ViewHelper that desires this support and this inherited
338
     * method must not be called, obviously.
339
     *
340
     * @throws Exception
341
     * @param array $arguments
342
     * @return void
343
     */
344
    public function validateAdditionalArguments(array $arguments)
345
    {
346
        if (!empty($arguments)) {
347
            throw new Exception(
348
                sprintf(
349
                    'Undeclared arguments passed to ViewHelper %s: %s. Valid arguments are: %s',
350
                    get_class($this),
351
                    implode(', ', array_keys($arguments)),
352
                    implode(', ', array_keys($this->argumentDefinitions))
353
                )
354
            );
355
        }
356
    }
357
358
    /**
359
     * You only should override this method *when you absolutely know what you
360
     * are doing*, and really want to influence the generated PHP code during
361
     * template compilation directly.
362
     *
363
     * @param string $argumentsName
364
     * @param string $closureName
365
     * @param string $initializationPhpCode
366
     * @param ViewHelperNode $node
367
     * @param TemplateCompiler $compiler
368
     * @return string
369
     */
370
    public function compile($argumentsName, $closureName, &$initializationPhpCode, ViewHelperNode $node, TemplateCompiler $compiler)
371
    {
372
        return sprintf(
373
            '%s::renderStatic(%s, %s, $renderingContext)',
374
            get_class($this),
375
            $argumentsName,
376
            $closureName
377
        );
378
    }
379
380
    /**
381
     * Default implementation of static rendering; useful API method if your ViewHelper
382
     * when compiled is able to render itself statically to increase performance. This
383
     * default implementation will simply delegate to the ViewHelperInvoker.
384
     *
385
     * @param array $arguments
386
     * @param \Closure $renderChildrenClosure
387
     * @param RenderingContextInterface $renderingContext
388
     * @return mixed
389
     */
390
    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
391
    {
392
        $viewHelperClassName = get_called_class();
393
        return $renderingContext->getViewHelperInvoker()->invoke($viewHelperClassName, $arguments, $renderingContext, $renderChildrenClosure);
394
    }
395
396
    /**
397
     * Save the associated ViewHelper node in a static public class variable.
398
     * called directly after the ViewHelper was built.
399
     *
400
     * @param ViewHelperNode $node
401
     * @param TextNode[] $arguments
402
     * @param VariableProviderInterface $variableContainer
403
     * @return void
404
     */
405
    public static function postParseEvent(ViewHelperNode $node, array $arguments, VariableProviderInterface $variableContainer)
406
    {
407
    }
408
}
409