Completed
Pull Request — master (#470)
by Claus
06:03
created

FluidRenderer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
namespace TYPO3Fluid\Fluid\Core\Rendering;
4
5
use TYPO3Fluid\Fluid\Component\ComponentInterface;
6
use TYPO3Fluid\Fluid\Component\Error\ChildNotFoundException;
7
use TYPO3Fluid\Fluid\Core\Parser\PassthroughSourceException;
8
use TYPO3Fluid\Fluid\View\Exception;
9
use TYPO3Fluid\Fluid\View\Exception\InvalidTemplateResourceException;
10
11
class FluidRenderer
12
{
13
    /**
14
     * Constants defining possible rendering types
15
     */
16
    const RENDERING_TEMPLATE = 1;
17
    const RENDERING_PARTIAL = 2;
18
    const RENDERING_LAYOUT = 3;
19
20
    /**
21
     * The initial rendering context for this template view.
22
     * Due to the rendering stack, another rendering context might be active
23
     * at certain points while rendering the template.
24
     *
25
     * @var RenderingContextInterface
26
     */
27
    protected $baseRenderingContext;
28
29
    /**
30
     * Stack containing the current rendering type, the current rendering context, and the current parsed template
31
     * Do not manipulate directly, instead use the methods"getCurrent*()", "startRendering(...)" and "stopRendering()"
32
     *
33
     * @var array
34
     */
35
    protected $renderingStack = [];
36
37
    /**
38
     * @var callable|null
39
     * @deprecated Will be removed in Fluid 4.0
40
     */
41
    protected $baseTemplateClosure;
42
43
    /**
44
     * @var callable|null
45
     * @deprecated Will be removed in Fluid 4.0
46
     */
47
    protected $baseIdentifierClosure;
48
49
    public function __construct(RenderingContextInterface $renderingContext)
50
    {
51
        $this->baseRenderingContext = $renderingContext;
52
    }
53
54
    /**
55
     * @param callable|null $baseTemplateClosure
56
     * @return FluidRenderer
57
     * @deprecated Will be removed in Fluid 4.0
58
     */
59
    public function setBaseTemplateClosure(?callable $baseTemplateClosure): self
60
    {
61
        $this->baseTemplateClosure = $baseTemplateClosure;
0 ignored issues
show
Deprecated Code introduced by
The property TYPO3Fluid\Fluid\Core\Re...r::$baseTemplateClosure has been deprecated with message: Will be removed in Fluid 4.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
62
        return $this;
63
    }
64
65
    /**
66
     * @param callable|null $baseIdentifierClosure
67
     * @return FluidRenderer
68
     * @deprecated Will be removed in Fluid 4.0
69
     */
70
    public function setBaseIdentifierClosure(?callable $baseIdentifierClosure): self
71
    {
72
        $this->baseIdentifierClosure = $baseIdentifierClosure;
0 ignored issues
show
Deprecated Code introduced by
The property TYPO3Fluid\Fluid\Core\Re...:$baseIdentifierClosure has been deprecated with message: Will be removed in Fluid 4.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
73
        return $this;
74
    }
75
76
    public function getRenderingContext(): RenderingContextInterface
77
    {
78
        return $this->baseRenderingContext;
79
    }
80
81
    public function setRenderingContext(RenderingContextInterface $renderingContext): void
82
    {
83
        $this->baseRenderingContext = $renderingContext;
84
    }
85
86
    public function renderSource(string $source)
87
    {
88
        $renderingContext = $this->getCurrentRenderingContext();
89
        $renderingContext->getTemplatePaths()->setTemplateSource($source);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Re...ace::getTemplatePaths() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
90
        $templateParser = $renderingContext->getTemplateParser();
91
        $templatePaths = $renderingContext->getTemplatePaths();
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Re...ace::getTemplatePaths() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
92
        try {
93
            $parsedTemplate = $templateParser->getOrParseAndStoreTemplate(
94
                sha1($source),
95
                function() use ($source): string { return $source; }
96
            );
97
            $parsedTemplate->getArguments()->setRenderingContext($renderingContext);
98
        } catch (PassthroughSourceException $error) {
99
            return $error->getSource();
100
        }
101
102
        try {
103
            $layoutNameNode = $parsedTemplate->getNamedChild('layoutName');
104
            $layoutName = $layoutNameNode->getArguments()->setRenderingContext($renderingContext)['name'];
105
        } catch (ChildNotFoundException $exception) {
106
            $layoutName = null;
107
        }
108
109
        if ($layoutName) {
110
            try {
111
                $parsedLayout = $templateParser->getOrParseAndStoreTemplate(
112
                    $templatePaths->getLayoutIdentifier($layoutName),
113
                    function(RenderingContextInterface $renderingContext) use ($layoutName): string {
114
                        return $renderingContext->getTemplatePaths()->getLayoutSource($layoutName);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Re...ace::getTemplatePaths() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
115
                    }
116
                );
117
                $parsedLayout->getArguments()->setRenderingContext($renderingContext);
118
            } catch (PassthroughSourceException $error) {
119
                return $error->getSource();
120
            }
121
            $this->startRendering(self::RENDERING_LAYOUT, $parsedTemplate, $this->baseRenderingContext);
122
            $output = $parsedLayout->evaluate($this->baseRenderingContext);
123
            $this->stopRendering();
124
        } else {
125
            $this->startRendering(self::RENDERING_TEMPLATE, $parsedTemplate, $this->baseRenderingContext);
126
            $output = $parsedTemplate->evaluate($this->baseRenderingContext);
127
            $this->stopRendering();
128
        }
129
        return $output;
130
    }
131
132
    /**
133
     * Loads the template source and render the template.
134
     * If "layoutName" is set in a PostParseFacet callback, it will render the file with the given layout.
135
     *
136
     * @param string $filePathAndName
137
     * @return mixed Rendered Template
138
     */
139
    public function renderFile(string $filePathAndName)
140
    {
141
        $this->baseRenderingContext->getTemplatePaths()->setTemplatePathAndFilename($filePathAndName);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Re...ace::getTemplatePaths() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
142
        $output = $this->renderSource(file_get_contents($filePathAndName));
143
        $this->baseRenderingContext->getTemplatePaths()->setTemplatePathAndFilename(null);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Re...ace::getTemplatePaths() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
144
        return $output;
145
    }
146
147
    /**
148
     * Renders a given section.
149
     *
150
     * Deprecated in favor of the Atoms concept which can be accessed through the ViewHelperResolver.
151
     *
152
     * A section can be rendered by resolving the appropriate (template, layout or partial-like)
153
     * Atom and using either getTypedChildren() or getNamedChild() to extract the desired section
154
     * and render it via the Component interface the return value implements.
155
     *
156
     * @param string $sectionName Name of section to render
157
     * @param array $variables The variables to use
158
     * @param boolean $ignoreUnknown Ignore an unknown section and just return an empty string
159
     * @return mixed rendered template for the section
160
     * @throws ChildNotFoundException
161
     * @throws InvalidTemplateResourceException
162
     * @throws Exception
163
     * @deprecated Will be removed in Fluid 4.0
164
     */
165
    public function renderSection(string $sectionName, array $variables = [], bool $ignoreUnknown = false)
166
    {
167
        if ($this->getCurrentRenderingType() === self::RENDERING_LAYOUT) {
168
            // in case we render a layout right now, we will render a section inside a TEMPLATE.
169
            $renderingTypeOnNextLevel = self::RENDERING_TEMPLATE;
170
            $renderingContext = $this->getCurrentRenderingContext();
171
        } else {
172
            $renderingTypeOnNextLevel = $this->getCurrentRenderingType();
173
            $renderingContext = clone $this->getCurrentRenderingContext();
174
            $renderingContext->setVariableProvider($renderingContext->getVariableProvider()->getScopeCopy($variables));
175
        }
176
177
        try {
178
            $parsedTemplate = $this->getCurrentParsedTemplate();
179
            $section = $parsedTemplate->getNamedChild($sectionName);
180
        } catch (PassthroughSourceException $error) {
181
            return $error->getSource();
182
        } catch (InvalidTemplateResourceException $error) {
183
            if (!$ignoreUnknown) {
184
                return $renderingContext->getErrorHandler()->handleViewError($error);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Er...face::handleViewError() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
185
            }
186
            return '';
187
        } catch (ChildNotFoundException $error) {
188
            if (!$ignoreUnknown) {
189
                return $renderingContext->getErrorHandler()->handleViewError($error);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Er...face::handleViewError() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
190
            }
191
            return '';
192
        } catch (Exception $error) {
193
            return $renderingContext->getErrorHandler()->handleViewError($error);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Er...face::handleViewError() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
194
        }
195
196
        $this->startRendering($renderingTypeOnNextLevel, $parsedTemplate, $renderingContext);
197
        $output = $section->evaluate($renderingContext);
198
        $this->stopRendering();
199
200
        return $output;
201
    }
202
203
    /**
204
     * Renders a partial.
205
     *
206
     * Deprecated in favor of Atoms concept which can be accessed through the
207
     * ViewHelperResolver to fetch and render a (partial-like) Atom directly.
208
     *
209
     * @param string $partialName
210
     * @param string|null $sectionName
211
     * @param array $variables
212
     * @param boolean $ignoreUnknown Ignore an unknown section and just return an empty string
213
     * @return mixed
214
     * @throws ChildNotFoundException
215
     * @throws InvalidTemplateResourceException
216
     * @throws Exception
217
     * @deprecated Will be removed in Fluid 4.0
218
     */
219
    public function renderPartial(string $partialName, ?string $sectionName, array $variables, bool $ignoreUnknown = false)
220
    {
221
        $templatePaths = $this->baseRenderingContext->getTemplatePaths();
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Re...ace::getTemplatePaths() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
222
        $renderingContext = clone $this->getCurrentRenderingContext();
223
        try {
224
            $parsedPartial = $renderingContext->getTemplateParser()->getOrParseAndStoreTemplate(
225
                $templatePaths->getPartialIdentifier($partialName),
226
                function (RenderingContextInterface $renderingContext) use ($partialName): string {
227
                    return $renderingContext->getTemplatePaths()->getPartialSource($partialName);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Re...ace::getTemplatePaths() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
228
                }
229
            );
230
            $parsedPartial->getArguments()->setRenderingContext($renderingContext);
231
        } catch (PassthroughSourceException $error) {
232
            return $error->getSource();
233
        } catch (InvalidTemplateResourceException $error) {
234
            if (!$ignoreUnknown) {
235
                return $renderingContext->getErrorHandler()->handleViewError($error);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Er...face::handleViewError() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
236
            }
237
            return '';
238
        } catch (ChildNotFoundException $error) {
239
            if (!$ignoreUnknown) {
240
                return $renderingContext->getErrorHandler()->handleViewError($error);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Er...face::handleViewError() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
241
            }
242
            return '';
243
        } catch (Exception $error) {
244
            return $renderingContext->getErrorHandler()->handleViewError($error);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Er...face::handleViewError() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
245
        }
246
        $this->startRendering(self::RENDERING_PARTIAL, $parsedPartial, $renderingContext);
247
        if ($sectionName !== null) {
248
            $output = $this->renderSection($sectionName, $variables, $ignoreUnknown);
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Re...nderer::renderSection() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
249
        } else {
250
            $renderingContext->setVariableProvider($renderingContext->getVariableProvider()->getScopeCopy($variables));
251
            $output = $parsedPartial->evaluate($renderingContext);
252
        }
253
        $this->stopRendering();
254
        return $output;
255
    }
256
257
    /**
258
     * Start a new nested rendering. Pushes the given information onto the $renderingStack.
259
     *
260
     * @param integer $type one of the RENDERING_* constants
261
     * @param ComponentInterface $template
262
     * @param RenderingContextInterface $context
263
     * @return void
264
     */
265
    protected function startRendering(int $type, ComponentInterface $template, RenderingContextInterface $context): void
266
    {
267
        array_push($this->renderingStack, ['type' => $type, 'parsedTemplate' => $template, 'renderingContext' => $context]);
268
    }
269
270
    /**
271
     * Stops the current rendering. Removes one element from the $renderingStack. Make sure to always call this
272
     * method pair-wise with startRendering().
273
     *
274
     * @return void
275
     */
276
    protected function stopRendering(): void
277
    {
278
        array_pop($this->renderingStack);
279
    }
280
281
    protected function getCurrentRenderingType(): int
282
    {
283
        $currentRendering = end($this->renderingStack);
284
        return $currentRendering['type'] ? $currentRendering['type'] : self::RENDERING_TEMPLATE;
285
    }
286
287
    protected function getCurrentParsedTemplate(): ComponentInterface
288
    {
289
        $currentRendering = end($this->renderingStack);
290
        $renderingContext = $this->getCurrentRenderingContext();
291
        $parsedTemplate = $currentRendering['parsedTemplate'] ?? null;
292
        if ($parsedTemplate) {
293
            return $parsedTemplate;
294
        }
295
        $templatePaths = $renderingContext->getTemplatePaths();
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Re...ace::getTemplatePaths() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
296
        $templateParser = $renderingContext->getTemplateParser();
297
298
        // Retrieve the current parsed template, which happens if the renderSection() method was called as first entry
299
        // method (as opposed to rendering through renderFile / renderSource which causes stack entries which in turn
300
        // causes this method to return early).
301
        // Support for the closures will be removed in Fluid 4.0 since they are a temporary measure.
302
        $parsedTemplate = $templateParser->getOrParseAndStoreTemplate(
303
            $this->baseIdentifierClosure ? call_user_func($this->baseIdentifierClosure) : $templatePaths->getTemplateIdentifier('Default', 'Default'),
0 ignored issues
show
Deprecated Code introduced by
The property TYPO3Fluid\Fluid\Core\Re...:$baseIdentifierClosure has been deprecated with message: Will be removed in Fluid 4.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
304
            $this->baseTemplateClosure ?? function(RenderingContextInterface $renderingContext): string {
0 ignored issues
show
Documentation introduced by
$this->baseTemplateClosu...Default', 'Default'); } is of type callable, but the function expects a object<Closure>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Deprecated Code introduced by
The property TYPO3Fluid\Fluid\Core\Re...r::$baseTemplateClosure has been deprecated with message: Will be removed in Fluid 4.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
305
                return $renderingContext->getTemplatePaths()->getTemplateSource('Default', 'Default');
0 ignored issues
show
Deprecated Code introduced by
The method TYPO3Fluid\Fluid\Core\Re...ace::getTemplatePaths() has been deprecated with message: Will be removed in Fluid 4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
306
            }
307
        );
308
        return $parsedTemplate;
309
    }
310
311
    protected function getCurrentRenderingContext(): RenderingContextInterface
312
    {
313
        $currentRendering = end($this->renderingStack);
314
        return $currentRendering['renderingContext'] ? $currentRendering['renderingContext'] : $this->baseRenderingContext;
315
    }
316
}