From c0504b90c57a30bf01dc09d1751bfcae28982f6e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 8 Jun 2026 13:13:35 +0200 Subject: [PATCH] Handle single-node child template bodies in cleanup --- src/Parser.php | 10 ++++++++-- tests/ParserTest.php | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 9b30f1ba6..5ef1aea8d 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -121,7 +121,7 @@ class Parser } if ($this->parent) { - $this->cleanupBodyForChildTemplates($body); + $body = $this->cleanupBodyForChildTemplates($body); } $node = new ModuleNode( @@ -552,8 +552,12 @@ class Parser return $test; } - private function cleanupBodyForChildTemplates(Node $body): void + private function cleanupBodyForChildTemplates(Node $body): Node { + if ($body instanceof BlockReferenceNode || ($body instanceof TextNode && $body->isBlank())) { + return new EmptyNode(); + } + foreach ($body as $k => $node) { if ($node instanceof BlockReferenceNode) { // as it has a parent, the block reference won't be used @@ -563,6 +567,8 @@ class Parser $body->removeNode($k); } } + + return $body; } private function checkPrecedenceDeprecations(ExpressionParserInterface $expressionParser, AbstractExpression $expr) diff --git a/tests/ParserTest.php b/tests/ParserTest.php index 14bc5bf4d..703f82401 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -171,6 +171,17 @@ EOF, 'index'))); $this->assertSame('set', $body->getNode('4')->getNodeTag()); } + public function testCleanupBodyForChildTemplatesWithASingleNodeBody() + { + $twig = new Environment(new ArrayLoader()); + $twig->addTokenParser(new ParentSettingTokenParser()); + + $node = $twig->parse($twig->tokenize(new Source('{% set_parent %}', 'index'))); + + $body = $node->getNode('body')->getNode('0'); + $this->assertInstanceOf(EmptyNode::class, $body); + } + public function testBodyForParentTemplates() { $twig = new Environment(new ArrayLoader()); @@ -231,3 +242,20 @@ class TestTokenParser extends AbstractTokenParser return 'test'; } } + +class ParentSettingTokenParser extends AbstractTokenParser +{ + public function parse(Token $token): Node + { + $this->parser->setParent(new ConstantExpression('base', $token->getLine()), false); + $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); + + // returns a blank text node so the whole child body is a single removable node + return new TextNode(' ', $token->getLine()); + } + + public function getTag(): string + { + return 'set_parent'; + } +}