From 51e93aa130fb63fec0001c57c4ea1c5a21c9e0e8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 24 Aug 2024 10:49:53 +0200 Subject: [PATCH] Deprecate not passing a BodyNode instance as the body of a ModuleNode constructor --- CHANGELOG | 1 + doc/deprecated.rst | 3 +++ src/Node/MacroNode.php | 7 +++++++ src/Node/ModuleNode.php | 7 +++++++ tests/Node/MacroTest.php | 5 +++-- tests/Node/ModuleTest.php | 9 +++++---- tests/NodeVisitor/SandboxTest.php | 5 +++-- 7 files changed, 29 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 627423370..e72d8af36 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ # 3.12.0 (2024-XX-XX) * Add support for integers in methods of `Twig\Node\Node` that take a Node name + * Deprecate not passing a `BodyNode` instance as the body of a `ModuleNode` or `MacroNode` constructor * Deprecate `OptimizerNodeVisitor::OPTIMIZE_TEXT_NODES` * Fix performance regression when `use_yield` is `false` (which is the default) * Improve compatibility when `use_yield` is `false` (as extensions still using `echo` will work as is) diff --git a/doc/deprecated.rst b/doc/deprecated.rst index f5fa4a77e..6eceed15f 100644 --- a/doc/deprecated.rst +++ b/doc/deprecated.rst @@ -40,6 +40,9 @@ Nodes ``getNode()``, ``hasNode()``, ``setNode()``, ``removeNode()``, and ``deprecateNode()``. +* Not passing a ``BodyNode`` instance as the body of a ``ModuleNode`` or + ``MacroNode`` constructor is deprecated as of Twig 3.12. + * The second argument of the ``Twig\Node\Expression\CallExpression::compileArguments()`` method is deprecated. diff --git a/src/Node/MacroNode.php b/src/Node/MacroNode.php index a6048de9b..54a04ddc2 100644 --- a/src/Node/MacroNode.php +++ b/src/Node/MacroNode.php @@ -25,8 +25,15 @@ class MacroNode extends Node { public const VARARGS_NAME = 'varargs'; + /** + * @param BodyNode $body + */ public function __construct(string $name, Node $body, Node $arguments, int $lineno, ?string $tag = null) { + if (!$body instanceof BodyNode) { + trigger_deprecation('twig/twig', '3.12', sprintf('Not passing a "%s" instance as the "body" argument of the "%s" constructor is deprecated.', BodyNode::class, __CLASS__)); + } + foreach ($arguments as $argumentName => $argument) { if (self::VARARGS_NAME === $argumentName) { throw new SyntaxError(\sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getTemplateLine(), $argument->getSourceContext()); diff --git a/src/Node/ModuleNode.php b/src/Node/ModuleNode.php index 158f8f53e..96f0c9ccc 100644 --- a/src/Node/ModuleNode.php +++ b/src/Node/ModuleNode.php @@ -30,8 +30,15 @@ use Twig\Source; #[YieldReady] final class ModuleNode extends Node { + /** + * @param BodyNode $body + */ public function __construct(Node $body, ?AbstractExpression $parent, Node $blocks, Node $macros, Node $traits, $embeddedTemplates, Source $source) { + if (!$body instanceof BodyNode) { + trigger_deprecation('twig/twig', '3.12', sprintf('Not passing a "%s" instance as the "body" argument of the "%s" constructor is deprecated.', BodyNode::class, __CLASS__)); + } + $nodes = [ 'body' => $body, 'blocks' => $blocks, diff --git a/tests/Node/MacroTest.php b/tests/Node/MacroTest.php index ce51187be..ef36774f7 100644 --- a/tests/Node/MacroTest.php +++ b/tests/Node/MacroTest.php @@ -13,6 +13,7 @@ namespace Twig\Tests\Node; use Twig\Environment; use Twig\Loader\ArrayLoader; +use Twig\Node\BodyNode; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\NameExpression; use Twig\Node\MacroNode; @@ -24,7 +25,7 @@ class MacroTest extends NodeTestCase { public function testConstructor() { - $body = new TextNode('foo', 1); + $body = new BodyNode([new TextNode('foo', 1)]); $arguments = new Node([new NameExpression('foo', 1)], [], 1); $node = new MacroNode('foo', $body, $arguments, 1); @@ -42,7 +43,7 @@ class MacroTest extends NodeTestCase 'bar' => new ConstantExpression('Foo', 1), ], [], 1); - $body = new TextNode('foo', 1); + $body = new BodyNode([new TextNode('foo', 1)]); $node = new MacroNode('foo', $body, $arguments, 1); $text[] = [$node, <<setAttribute('is_generator', true); - $node = new ModuleNode(new PrintNode($expr, 1), null, new Node(), new Node(), new Node(), new Node([]), new Source('foo', 'foo')); + $node = new ModuleNode(new BodyNode([new PrintNode($expr, 1)]), null, new Node(), new Node(), new Node(), new Node([]), new Source('foo', 'foo')); $traverser = new NodeTraverser($env, [new SandboxNodeVisitor($env)]); $node = $traverser->traverse($node); - $this->assertNotInstanceOf(CheckToStringNode::class, $node->getNode('body')->getNode('expr')); + $this->assertNotInstanceOf(CheckToStringNode::class, $node->getNode('body')->getNode(0)->getNode('expr')); $this->assertSame("// line 1\nyield from (\$context[\"foo\"] ?? null);\n", $env->compile($node->getNode('body'))); } }