Completed
Pull Request — master (#270)
by Marc
03:17 queued 55s
created

ViewHelperNode::callArgumentInterceptors()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 3
nop 1
dl 0
loc 13
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace TYPO3Fluid\Fluid\Core\Parser\SyntaxTree;
4
5
/*
6
 * This file belongs to the package "TYPO3 Fluid".
7
 * See LICENSE.txt that was shipped with this package.
8
 */
9
10
use TYPO3Fluid\Fluid\Core\Parser\Exception;
11
use TYPO3Fluid\Fluid\Core\Parser\InterceptorInterface;
12
use TYPO3Fluid\Fluid\Core\Parser\ParsingState;
13
use TYPO3Fluid\Fluid\Core\Parser\TemplateParser;
14
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
15
use TYPO3Fluid\Fluid\Core\ViewHelper\ArgumentDefinition;
16
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperInterface;
17
18
/**
19
 * Node which will call a ViewHelper associated with this node.
20
 */
21
class ViewHelperNode extends AbstractNode
22
{
23
24
    /**
25
     * @var string
26
     */
27
    protected $viewHelperClassName;
28
29
    /**
30
     * @var NodeInterface[]
31
     */
32
    protected $arguments = [];
33
34
    /**
35
     * @var ViewHelperInterface
36
     */
37
    protected $uninitializedViewHelper = null;
38
39
    /**
40
     * @var ArgumentDefinition[]
41
     */
42
    protected $argumentDefinitions = [];
43
44
    /**
45
     * @var string
46
     */
47
    protected $pointerTemplateCode = null;
48
49
    /**
50
     * Constructor.
51
     *
52
     * @param RenderingContextInterface $renderingContext a RenderingContext, provided by invoker
53
     * @param string $namespace the namespace identifier of the ViewHelper.
54
     * @param string $identifier the name of the ViewHelper to render, inside the namespace provided.
55
     * @param NodeInterface[] $arguments Arguments of view helper - each value is a RootNode.
56
     * @param ParsingState $state
57
     */
58
    public function __construct(
59
        RenderingContextInterface $renderingContext,
60
        $namespace,
61
        $identifier,
62
        array $arguments,
63
        ParsingState $state
0 ignored issues
show
Unused Code introduced by
The parameter $state is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
64
    ) {
65
        $resolver = $renderingContext->getViewHelperResolver();
66
        $this->arguments = $arguments;
67
        $this->viewHelperClassName = $resolver->resolveViewHelperClassName($namespace, $identifier);
68
        $this->uninitializedViewHelper = $resolver->createViewHelperInstanceFromClassName($this->viewHelperClassName);
69
        $this->uninitializedViewHelper->setViewHelperNode($this);
70
        // Note: RenderingContext required here though replaced later. See https://github.com/TYPO3Fluid/Fluid/pull/93
71
        $this->uninitializedViewHelper->setRenderingContext($renderingContext);
72
        $this->argumentDefinitions = $resolver->getArgumentDefinitionsForViewHelper($this->uninitializedViewHelper);
73
        $this->rewriteBooleanNodesInArgumentsObjectTree($this->argumentDefinitions, $this->arguments);
74
        $this->validateArguments($this->argumentDefinitions, $this->arguments);
75
    }
76
77
    /**
78
     * @return ArgumentDefinition[]
79
     */
80
    public function getArgumentDefinitions()
81
    {
82
        return $this->argumentDefinitions;
83
    }
84
85
    /**
86
     * Returns the attached (but still uninitialized) ViewHelper for this ViewHelperNode.
87
     * We need this method because sometimes Interceptors need to ask some information from the ViewHelper.
88
     *
89
     * @return ViewHelperInterface
90
     */
91
    public function getUninitializedViewHelper()
92
    {
93
        return $this->uninitializedViewHelper;
94
    }
95
96
    /**
97
     * Get class name of view helper
98
     *
99
     * @return string Class Name of associated view helper
100
     */
101
    public function getViewHelperClassName()
102
    {
103
        return $this->viewHelperClassName;
104
    }
105
106
    /**
107
     * INTERNAL - only needed for compiling templates
108
     *
109
     * @return NodeInterface[]
110
     */
111
    public function getArguments()
112
    {
113
        return $this->arguments;
114
    }
115
116
    /**
117
     * INTERNAL - only needed for compiling templates
118
     *
119
     * @param string $argumentName
120
     * @return ArgumentDefinition
121
     */
122
    public function getArgumentDefinition($argumentName)
123
    {
124
        return $this->argumentDefinitions[$argumentName];
125
    }
126
127
    /**
128
     * @param NodeInterface $childNode
129
     * @return void
130
     */
131
    public function addChildNode(NodeInterface $childNode)
132
    {
133
        parent::addChildNode($childNode);
134
        $this->uninitializedViewHelper->setChildNodes($this->childNodes);
135
    }
136
137
    /**
138
     * @param string $pointerTemplateCode
139
     * @return void
140
     */
141
    public function setPointerTemplateCode($pointerTemplateCode)
142
    {
143
        $this->pointerTemplateCode = $pointerTemplateCode;
144
    }
145
146
    /**
147
     * Call the view helper associated with this object.
148
     *
149
     * First, it evaluates the arguments of the view helper.
150
     *
151
     * If the view helper implements \TYPO3Fluid\Fluid\Core\ViewHelper\ChildNodeAccessInterface,
152
     * it calls setChildNodes(array childNodes) on the view helper.
153
     *
154
     * Afterwards, checks that the view helper did not leave a variable lying around.
155
     *
156
     * @param RenderingContextInterface $renderingContext
157
     * @return string evaluated node after the view helper has been called.
158
     */
159
    public function evaluate(RenderingContextInterface $renderingContext)
160
    {
161
        return $renderingContext->getViewHelperInvoker()->invoke($this->uninitializedViewHelper, $this->arguments,
162
            $renderingContext);
163
    }
164
165
    /**
166
     * Wraps the argument tree, if a node is boolean, into a Boolean syntax tree node
167
     *
168
     * @param ArgumentDefinition[] $argumentDefinitions the argument definitions, key is the argument name, value is the ArgumentDefinition object
169
     * @param NodeInterface[] $argumentsObjectTree the arguments syntax tree, key is the argument name, value is an AbstractNode
170
     * @return void
171
     */
172
    protected function rewriteBooleanNodesInArgumentsObjectTree($argumentDefinitions, &$argumentsObjectTree)
173
    {
174
        /** @var $argumentDefinition ArgumentDefinition */
175
        foreach ($argumentDefinitions as $argumentName => $argumentDefinition) {
176
            if (($argumentDefinition->getType() === 'boolean' || $argumentDefinition->getType() === 'bool')
177
                && isset($argumentsObjectTree[$argumentName])
178
            ) {
179
                $argumentsObjectTree[$argumentName] = new BooleanNode($argumentsObjectTree[$argumentName]);
180
            }
181
        }
182
    }
183
184
    /**
185
     * Call interceptors for viewHelper arguments
186
     *
187
     * @param TemplateParser $templateParser
188
     * @return void
189
     */
190
    public function callArgumentInterceptors(TemplateParser $templateParser)
191
    {
192
        foreach ($this->argumentDefinitions as $argumentName => $argumentDefinition) {
193
            /** @var ArgumentDefinition $argumentDefinition */
194
            if (isset($this->arguments[$argumentName]) && $this->arguments[$argumentName] instanceof NodeInterface) {
195
                $this->arguments[$argumentName]->setEscapeOutput($argumentDefinition->isEscaped());
196
                $this->arguments[$argumentName] = $templateParser->callInterceptor(
197
                    $this->arguments[$argumentName],
198
                    InterceptorInterface::INTERCEPT_VIEWHELPER_ARGUMENT
199
                );
200
            }
201
        }
202
    }
203
204
    /**
205
     * @param ArgumentDefinition[] $argumentDefinitions
206
     * @param NodeInterface[] $argumentsObjectTree
207
     * @throws Exception
208
     */
209
    protected function validateArguments(array $argumentDefinitions, array $argumentsObjectTree)
210
    {
211
        $additionalArguments = [];
212
        foreach ($argumentsObjectTree as $argumentName => $value) {
213
            if (!array_key_exists($argumentName, $argumentDefinitions)) {
214
                $additionalArguments[$argumentName] = $value;
215
            }
216
        }
217
        foreach ($argumentDefinitions as $argumentDefinition) {
218
            if ($argumentDefinition->isRequired() && $argumentDefinition->getDefaultValue() === null) {
219
                $name = $argumentDefinition->getName();
220
                if (!array_key_exists($name, $argumentsObjectTree)) {
221
                    throw new Exception(sprintf('Required argument %s for ViewHelper %s was not provided', $name,
222
                        $this->viewHelperClassName));
223
                }
224
            }
225
        }
226
        $this->abortIfRequiredArgumentsAreMissing($argumentDefinitions, $argumentsObjectTree);
227
        $this->uninitializedViewHelper->validateAdditionalArguments($additionalArguments);
228
    }
229
230
    /**
231
     * Throw an exception if required arguments are missing
232
     *
233
     * @param ArgumentDefinition[] $expectedArguments Array of all expected arguments
234
     * @param NodeInterface[] $actualArguments Actual arguments
235
     * @throws Exception
236
     */
237
    protected function abortIfRequiredArgumentsAreMissing($expectedArguments, $actualArguments)
238
    {
239
        $actualArgumentNames = array_keys($actualArguments);
240
        foreach ($expectedArguments as $expectedArgument) {
241
            if ($expectedArgument->isRequired() && !in_array($expectedArgument->getName(), $actualArgumentNames)) {
242
                throw new Exception('Required argument "' . $expectedArgument->getName() . '" was not supplied.',
243
                    1237823699);
244
            }
245
        }
246
    }
247
}
248