Skip to content

Commit 7b2974c

Browse files
authored
[TASK] Establish proper API to set template layout (#1090)
The `ParsingState` now provides a proper method to set the layout of a template during parsing/compilation. Previously, the magic variable name `layoutName` has been used for that purpose, but with the new `nodeInitializedEvent` we can now access the `ParsingState` object directly. The usage of the old variable name has been deprecated and will no longer be evaluated with Fluid v5. Its main usage besides the `<f:layout>` ViewHelper (see above) has been the cache warmup command, which will receive a more straightforward implementation with Fluid v5.
1 parent 6d0d85f commit 7b2974c

File tree

11 files changed

+147
-25
lines changed

11 files changed

+147
-25
lines changed

Documentation/Changelog/4.x.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
Changelog 4.x
77
=============
88

9+
4.4
10+
---
11+
12+
* Deprecation: Setting a template's layout with the variable `layoutName` is deprecated and will no longer work in Fluid v5.
13+
Use :php:`TYPO3Fluid\Fluid\Core\Parser\ParsingState->setLayoutName()` instead.
14+
* Deprecation: Constant :php:`TYPO3Fluid\Fluid\Core\Compiler\TemplateCompiler::LAYOUT_VARIABLE`
15+
has been marked as deprecated and will be removed in Fluid v5.
16+
917
4.3
1018
---
1119

Documentation/Integrating/Index.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ composition: which implementations of `TemplatePaths` the View requires, if it
3535
needs a :ref:`custom ViewHelperResolver <viewhelperresolver>`,
3636
if it must have some default variables, if it should have a default cache, etc.
3737

38-
.. note::
39-
40-
The special variable `layoutName` is reserved and can be assigned to a
41-
template to set its Layout instead of using `<f:layout name="LayoutName" />`.
38+
.. deprecated:: 4.4
39+
Prevously, it was possible to set the layout of a template with the special
40+
variable `layoutName`. This will no longer work with Fluid 5. Please use the
41+
`<f:layout> ViewHelper <https://docs.typo3.org/permalink/fluid:typo3fluid-fluid-layout>`_
42+
instead.
4243

4344
.. _templatepaths:
4445

Documentation/ViewHelpers/Fluid.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

Documentation/ViewHelpers/Fluid/Flatten.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@
1212
Flatten ViewHelper `<f:flatten>`
1313
================================
1414

15+
.. .. note::
16+
.. This reference is part of the documentation of Fluid Standalone.
17+
.. If you are working with Fluid in TYPO3 CMS, please refer to
18+
.. :doc:`TYPO3's ViewHelper reference <t3viewhelper:Global/Flatten>` instead.
19+
1520
.. typo3:viewhelper:: flatten
1621
:source: ../Fluid.json

Documentation/ViewHelpers/Fluid/Shuffle.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@
1212
Shuffle ViewHelper `<f:shuffle>`
1313
================================
1414

15+
.. .. note::
16+
.. This reference is part of the documentation of Fluid Standalone.
17+
.. If you are working with Fluid in TYPO3 CMS, please refer to
18+
.. :doc:`TYPO3's ViewHelper reference <t3viewhelper:Global/Shuffle>` instead.
19+
1520
.. typo3:viewhelper:: shuffle
1621
:source: ../Fluid.json

src/Core/Compiler/TemplateCompiler.php

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
use TYPO3Fluid\Fluid\Core\Parser\ParsedTemplateInterface;
1313
use TYPO3Fluid\Fluid\Core\Parser\ParsingState;
1414
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\NodeInterface;
15-
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\RootNode;
1615
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
1716
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
1817
use TYPO3Fluid\Fluid\Core\ViewHelper\ArgumentDefinition;
@@ -37,8 +36,7 @@ class TemplateCompiler
3736
* Variable name to be used to transfer information about a template's layout
3837
* from the ViewHelper context to the TemplateView and the TemplateCompiler
3938
*
40-
* @todo This data-shuffling between parser, compiler and renderer should be
41-
* avoided in the future.
39+
* @deprecated variable will no longer be necessary in Fluid v5
4240
*/
4341
public const LAYOUT_VARIABLE = 'layoutName';
4442

@@ -174,7 +172,7 @@ public function store(string $identifier, ParsingState $parsingState): ?string
174172
'Main Render function',
175173
);
176174

177-
$storedLayoutName = $parsingState->getVariableContainer()->get(static::LAYOUT_VARIABLE);
175+
$storedLayoutName = $parsingState->getUnevaluatedLayoutName();
178176
$templateCode = sprintf(
179177
'<?php' . chr(10) .
180178
'%s {' . chr(10) .
@@ -203,12 +201,9 @@ public function store(string $identifier, ParsingState $parsingState): ?string
203201
return $templateCode;
204202
}
205203

206-
/**
207-
* @todo this type is crazy, this should really be something like NodeInterface|string
208-
*/
209-
protected function generateCodeForLayoutName(NodeInterface|string|int|float|null|bool $storedLayoutNameArgument): string
204+
protected function generateCodeForLayoutName(NodeInterface|string|null $storedLayoutNameArgument): string
210205
{
211-
if ($storedLayoutNameArgument instanceof RootNode) {
206+
if ($storedLayoutNameArgument instanceof NodeInterface) {
212207
$convertedCode = $storedLayoutNameArgument->convert($this);
213208
$initialization = $convertedCode['initialization'];
214209
$execution = $convertedCode['execution'];

src/Core/Parser/ParsedTemplateInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public function getVariableContainer(): VariableProviderInterface;
5252
* Returns the name of the layout that is defined within the current template via <f:layout name="..." />
5353
* If no layout is defined, this returns null.
5454
* This requires the current rendering context in order to be able to evaluate the layout name
55+
*
56+
* @todo remove NodeInterface from return types in Fluid v5
5557
*/
5658
public function getLayoutName(RenderingContextInterface $renderingContext): string|null|NodeInterface;
5759

src/Core/Parser/ParsingState.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
class ParsingState implements ParsedTemplateInterface
2828
{
2929
protected string $identifier;
30+
protected string|NodeInterface|null $layoutName = null;
3031

3132
/**
3233
* @var array<string, ArgumentDefinition>
@@ -205,7 +206,8 @@ public function getVariableContainer(): VariableProviderInterface
205206
*/
206207
public function hasLayout(): bool
207208
{
208-
return $this->variableContainer->exists(TemplateCompiler::LAYOUT_VARIABLE);
209+
// @todo remove fallback with Fluid v5
210+
return isset($this->layoutName) || $this->variableContainer->exists(TemplateCompiler::LAYOUT_VARIABLE);
209211
}
210212

211213
/**
@@ -215,10 +217,29 @@ public function hasLayout(): bool
215217
*
216218
* @throws View\Exception
217219
*/
218-
public function getLayoutName(RenderingContextInterface $renderingContext): string|null|NodeInterface
220+
public function getLayoutName(RenderingContextInterface $renderingContext): ?string
219221
{
220-
$layoutName = $this->variableContainer->get(TemplateCompiler::LAYOUT_VARIABLE);
221-
return $layoutName instanceof RootNode ? $layoutName->evaluate($renderingContext) : $layoutName;
222+
$layoutName = $this->getUnevaluatedLayoutName();
223+
return $layoutName instanceof NodeInterface ? $layoutName->evaluate($renderingContext) : $layoutName;
224+
}
225+
226+
/**
227+
* @internal only to be used by TemplateCompiler
228+
*/
229+
public function getUnevaluatedLayoutName(): NodeInterface|string|null
230+
{
231+
// @todo remove fallback with Fluid v5
232+
if (!isset($this->layoutName) && $this->variableContainer->exists(TemplateCompiler::LAYOUT_VARIABLE)) {
233+
trigger_error('Setting a template\'s layout with the variable "layoutName" is deprecated and will no longer work in Fluid v5. Use ParsingState->setLayoutName() instead.', E_USER_DEPRECATED);
234+
$layoutName = $this->variableContainer->get(TemplateCompiler::LAYOUT_VARIABLE);
235+
return !$layoutName instanceof NodeInterface && !is_null($layoutName) ? (string)$layoutName : $layoutName;
236+
}
237+
return $this->layoutName;
238+
}
239+
240+
public function setLayoutName(string|NodeInterface|null $layoutName): void
241+
{
242+
$this->layoutName = $layoutName;
222243
}
223244

224245
public function addCompiledNamespaces(RenderingContextInterface $renderingContext): void {}

src/ViewHelpers/LayoutViewHelper.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperNodeInitializedEventInterface;
1616

1717
/**
18-
* With this tag, you can select a layout to be used for the current template.
18+
* With this ViewHelper, you can select a layout to be used for the current template.
19+
*
20+
* .. deprecated:: 4.4
21+
* Prevously, it was possible to set the layout of a template with the special
22+
* variable `layoutName`. This will no longer work with Fluid 5.
1923
*
2024
* Examples
2125
* ========
@@ -62,8 +66,8 @@ final public function convert(TemplateCompiler $templateCompiler): array
6266
}
6367

6468
/**
65-
* On the node initialized event, add the "layoutName" variable to the variable container so it can
66-
* be used by the TemplateView.
69+
* Set the defined layout name (which can include variables) to the ParsingState,
70+
* to be used both for compilation and uncached rendering
6771
*
6872
* @param array<string, NodeInterface> $arguments Unevaluated ViewHelper arguments
6973
*/
@@ -74,6 +78,6 @@ public static function nodeInitializedEvent(ViewHelperNode $node, array $argumen
7478
} else {
7579
$layoutNameNode = 'Default';
7680
}
77-
$parsingState->getVariableContainer()->add(TemplateCompiler::LAYOUT_VARIABLE, $layoutNameNode);
81+
$parsingState->setLayoutName($layoutNameNode);
7882
}
7983
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file belongs to the package "TYPO3 Fluid".
7+
* See LICENSE.txt that was shipped with this package.
8+
*/
9+
10+
namespace TYPO3Fluid\Fluid\Tests\Functional\Core\Rendering;
11+
12+
use PHPUnit\Framework\Attributes\IgnoreDeprecations;
13+
use PHPUnit\Framework\Attributes\Test;
14+
use TYPO3Fluid\Fluid\Tests\Functional\AbstractFunctionalTestCase;
15+
use TYPO3Fluid\Fluid\View\TemplateView;
16+
17+
final class LayoutNameVariableTest extends AbstractFunctionalTestCase
18+
{
19+
#[Test]
20+
#[IgnoreDeprecations]
21+
public function layoutVariable(): void
22+
{
23+
$view = new TemplateView();
24+
$view->getRenderingContext()->setCache(self::$cache);
25+
$view->getRenderingContext()->getVariableProvider()->add('layoutName', 'Default');
26+
$view->getRenderingContext()->getTemplatePaths()->setLayoutRootPaths([__DIR__ . '/../../Fixtures/Layouts/']);
27+
$view->getRenderingContext()->getTemplatePaths()->setTemplateSource('');
28+
self::assertSame('DefaultLayout with name Default.html', trim($view->render()), 'uncached');
29+
30+
$view = new TemplateView();
31+
$view->getRenderingContext()->setCache(self::$cache);
32+
$view->getRenderingContext()->getVariableProvider()->add('layoutName', 'Default');
33+
$view->getRenderingContext()->getTemplatePaths()->setLayoutRootPaths([__DIR__ . '/../../Fixtures/Layouts/']);
34+
$view->getRenderingContext()->getTemplatePaths()->setTemplateSource('');
35+
self::assertSame('DefaultLayout with name Default.html', trim($view->render()), 'cached');
36+
}
37+
}

0 commit comments

Comments
 (0)