From 9e6c4a6ac1e7f84c25e898c40f4971945685d0e6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Aug 2024 09:01:52 +0200 Subject: [PATCH] Deprecate some internal methods from Parser --- CHANGELOG | 1 + doc/deprecated.rst | 4 ++++ src/Extension/CoreExtension.php | 10 ++++---- src/Parser.php | 23 ++++++++++++++++++- src/TokenParser/BlockTokenParser.php | 3 --- .../functions/parent_outside_of_a_block.test | 10 ++++++++ .../inheritance/parent_without_extends.test | 2 +- tests/ParserTest.php | 4 +++- 8 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 tests/Fixtures/functions/parent_outside_of_a_block.test diff --git a/CHANGELOG b/CHANGELOG index 052713737..a29d4019e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ # 3.12.0 (2024-XX-XX) + * Deprecate some internal methods from `Parser`: `getBlockStack()`, `hasBlock()`, `getBlock()`, `hasMacro()`, `hasTraits()`, `getParent()` * Deprecate passing `null` to `Twig\Parser::setParent()` * Update `Node::__toString()` to include the node tag if set * Add support for integers in methods of `Twig\Node\Node` that take a Node name diff --git a/doc/deprecated.rst b/doc/deprecated.rst index 8469250ba..83a33a537 100644 --- a/doc/deprecated.rst +++ b/doc/deprecated.rst @@ -160,6 +160,10 @@ Node Visitors Parser ------ +* The following methods from ``Twig\Parser`` are deprecated as of Twig 3.12: + ``getBlockStack()``, ``hasBlock()``, ``getBlock()``, ``hasMacro()``, + ``hasTraits()``, ``getParent()``. + * The ``Twig\ExpressionParser::parseHashExpression()`` method is deprecated, use ``Twig\ExpressionParser::parseMappingExpression()`` instead. diff --git a/src/Extension/CoreExtension.php b/src/Extension/CoreExtension.php index 3a6a2e655..cd2f5d7e6 100644 --- a/src/Extension/CoreExtension.php +++ b/src/Extension/CoreExtension.php @@ -1919,15 +1919,15 @@ final class CoreExtension extends AbstractExtension */ public static function parseParentFunction(Parser $parser, Node $fakeNode, $args, int $line): AbstractExpression { - if (!\count($parser->getBlockStack())) { - throw new SyntaxError('Calling "parent" outside a block is forbidden.', $line, $parser->getStream()->getSourceContext()); + if (!$blockName = $parser->peekBlockStack()) { + throw new SyntaxError('Calling the "parent" function outside of a block is forbidden.', $line, $parser->getStream()->getSourceContext()); } - if (!$parser->getParent() && !$parser->hasTraits()) { - throw new SyntaxError('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $parser->getStream()->getSourceContext()); + if (!$parser->hasInheritance()) { + throw new SyntaxError('Calling the "parent" function on a template that does not call "extends" or "use" is forbidden.', $line, $parser->getStream()->getSourceContext()); } - return new ParentExpression($parser->peekBlockStack(), $line); + return new ParentExpression($blockName, $line); } /** diff --git a/src/Parser.php b/src/Parser.php index f8a3dd366..e3273230c 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -92,7 +92,7 @@ class Parser } if (!$e->getTemplateLine()) { - $e->setTemplateLine($this->stream->getCurrent()->getLine()); + $e->setTemplateLine($this->getCurrentToken()->getLine()); } throw $e; @@ -194,6 +194,8 @@ class Parser public function getBlockStack(): array { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return $this->blockStack; } @@ -214,21 +216,31 @@ class Parser public function hasBlock(string $name): bool { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return isset($this->blocks[$name]); } public function getBlock(string $name): Node { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return $this->blocks[$name]; } public function setBlock(string $name, BlockNode $value): void { + if (isset($this->blocks[$name])) { + throw new SyntaxError(\sprintf("The block '%s' has already been defined line %d.", $name, $this->blocks[$name]->getTemplateLine()), $this->getCurrentToken()->getLine(), $this->blocks[$name]->getSourceContext()); + } + $this->blocks[$name] = new BodyNode([$value], [], $value->getTemplateLine()); } public function hasMacro(string $name): bool { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return isset($this->macros[$name]); } @@ -244,6 +256,8 @@ class Parser public function hasTraits(): bool { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return \count($this->traits) > 0; } @@ -287,9 +301,16 @@ class Parser public function getParent(): ?Node { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return $this->parent; } + public function hasInheritance() + { + return $this->parent || 0 < \count($this->traits); + } + public function setParent(?Node $parent): void { if (null === $parent) { diff --git a/src/TokenParser/BlockTokenParser.php b/src/TokenParser/BlockTokenParser.php index 0b7126274..b3768742d 100644 --- a/src/TokenParser/BlockTokenParser.php +++ b/src/TokenParser/BlockTokenParser.php @@ -36,9 +36,6 @@ final class BlockTokenParser extends AbstractTokenParser $lineno = $token->getLine(); $stream = $this->parser->getStream(); $name = $stream->expect(Token::NAME_TYPE)->getValue(); - if ($this->parser->hasBlock($name)) { - throw new SyntaxError(\sprintf("The block '%s' has already been defined line %d.", $name, $this->parser->getBlock($name)->getTemplateLine()), $stream->getCurrent()->getLine(), $stream->getSourceContext()); - } $this->parser->setBlock($name, $block = new BlockNode($name, new Node([]), $lineno)); $this->parser->pushLocalScope(); $this->parser->pushBlockStack($name); diff --git a/tests/Fixtures/functions/parent_outside_of_a_block.test b/tests/Fixtures/functions/parent_outside_of_a_block.test new file mode 100644 index 000000000..03d4f5d66 --- /dev/null +++ b/tests/Fixtures/functions/parent_outside_of_a_block.test @@ -0,0 +1,10 @@ +--TEST-- +"parent" cannot be called outside of a block +--TEMPLATE-- +{% extends "parent.twig" %} +{{ parent() }} +--TEMPLATE(parent.twig)-- +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Calling the "parent" function outside of a block is forbidden in "index.twig" at line 3. diff --git a/tests/Fixtures/tags/inheritance/parent_without_extends.test b/tests/Fixtures/tags/inheritance/parent_without_extends.test index 6d9889155..c2025f60c 100644 --- a/tests/Fixtures/tags/inheritance/parent_without_extends.test +++ b/tests/Fixtures/tags/inheritance/parent_without_extends.test @@ -5,4 +5,4 @@ {{ parent() }} {% endblock %} --EXCEPTION-- -Twig\Error\SyntaxError: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3. +Twig\Error\SyntaxError: Calling the "parent" function on a template that does not call "extends" or "use" is forbidden in "index.twig" at line 3. diff --git a/tests/ParserTest.php b/tests/ParserTest.php index 73de41e6f..621c9d83c 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -151,7 +151,9 @@ class ParserTest extends TestCase new Token(Token::EOF_TYPE, '', 1), ])); - $this->assertNull($parser->getParent()); + $p = new \ReflectionProperty($parser, 'parent'); + $p->setAccessible(true); + $this->assertNull($p->getValue($parser)); } public function testGetVarName()