From b6d9dd62eeb58fc72407acdcddcfe8ebb39e9f4d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 23 Jan 2025 18:39:15 +0100 Subject: [PATCH] Fix inheritance logic to be able to use instanceof ContextVariable instead of NameExpression --- src/ExpressionParser.php | 8 ++++---- src/Node/Expression/ArrayExpression.php | 3 ++- src/Node/Expression/AssignNameExpression.php | 3 ++- src/Node/Expression/Binary/NullCoalesceBinary.php | 4 ++-- src/Node/Expression/Filter/DefaultFilter.php | 4 ++-- src/Node/Expression/GetAttrExpression.php | 3 ++- src/Node/Expression/MethodCallExpression.php | 3 ++- src/Node/Expression/NullCoalesceExpression.php | 3 ++- src/Node/Expression/Test/DefinedTest.php | 3 ++- src/Node/Expression/Variable/ContextVariable.php | 2 +- src/Node/ImportNode.php | 3 ++- src/NodeVisitor/OptimizerNodeVisitor.php | 7 ++++--- src/NodeVisitor/SafeAnalysisNodeVisitor.php | 3 ++- src/NodeVisitor/SandboxNodeVisitor.php | 3 ++- src/TokenParser/EmbedTokenParser.php | 3 ++- 15 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/ExpressionParser.php b/src/ExpressionParser.php index 0f1b0ed36..f03df9733 100644 --- a/src/ExpressionParser.php +++ b/src/ExpressionParser.php @@ -655,7 +655,7 @@ class ExpressionParser $name = null; if ($namedArguments && (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || (!$definition && $token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':')))) { - if (!$value instanceof NameExpression) { + if (!$value instanceof ContextVariable) { throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext()); } $name = $value->getAttribute('name'); @@ -743,7 +743,7 @@ class ExpressionParser $arguments = new Nodes([0 => $this->getPrimary()]); } - if ('defined' === $test->getName() && $node instanceof NameExpression && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) { + if ('defined' === $test->getName() && $node instanceof ContextVariable && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) { $node = new MacroReferenceExpression($alias['node']->getNode('var'), $alias['name'], new ArrayExpression([], $node->getTemplateLine()), $node->getTemplateLine()); } @@ -898,7 +898,7 @@ class ExpressionParser $name = null; if (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || ($token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':'))) { - if (!$value instanceof NameExpression) { + if (!$value instanceof ContextVariable) { throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext()); } $name = $value->getAttribute('name'); @@ -946,7 +946,7 @@ class ExpressionParser } if ( - $node instanceof NameExpression + $node instanceof ContextVariable && ( null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name')) || '_self' === $node->getAttribute('name') && $attribute instanceof ConstantExpression diff --git a/src/Node/Expression/ArrayExpression.php b/src/Node/Expression/ArrayExpression.php index 9769b719e..87d5cb8d7 100644 --- a/src/Node/Expression/ArrayExpression.php +++ b/src/Node/Expression/ArrayExpression.php @@ -13,6 +13,7 @@ namespace Twig\Node\Expression; use Twig\Compiler; use Twig\Node\Expression\Unary\StringCastUnary; +use Twig\Node\Expression\Variable\ContextVariable; class ArrayExpression extends AbstractExpression { @@ -99,7 +100,7 @@ class ArrayExpression extends AbstractExpression ++$nextIndex; } else { $key = null; - if ($pair['key'] instanceof NameExpression) { + if ($pair['key'] instanceof ContextVariable) { $pair['key'] = new StringCastUnary($pair['key'], $pair['key']->getTemplateLine()); } if ($pair['key'] instanceof TempNameExpression) { diff --git a/src/Node/Expression/AssignNameExpression.php b/src/Node/Expression/AssignNameExpression.php index c2cbb8e4a..c194660da 100644 --- a/src/Node/Expression/AssignNameExpression.php +++ b/src/Node/Expression/AssignNameExpression.php @@ -15,8 +15,9 @@ namespace Twig\Node\Expression; use Twig\Compiler; use Twig\Error\SyntaxError; use Twig\Node\Expression\Variable\AssignContextVariable; +use Twig\Node\Expression\Variable\ContextVariable; -class AssignNameExpression extends NameExpression +class AssignNameExpression extends ContextVariable { public function __construct(string $name, int $lineno) { diff --git a/src/Node/Expression/Binary/NullCoalesceBinary.php b/src/Node/Expression/Binary/NullCoalesceBinary.php index 15b6e8ee9..1472e8e2e 100644 --- a/src/Node/Expression/Binary/NullCoalesceBinary.php +++ b/src/Node/Expression/Binary/NullCoalesceBinary.php @@ -15,11 +15,11 @@ use Twig\Compiler; use Twig\Node\EmptyNode; use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\BlockReferenceExpression; -use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\OperatorEscapeInterface; use Twig\Node\Expression\Test\DefinedTest; use Twig\Node\Expression\Test\NullTest; use Twig\Node\Expression\Unary\NotUnary; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\TwigTest; final class NullCoalesceBinary extends AbstractBinary implements OperatorEscapeInterface @@ -28,7 +28,7 @@ final class NullCoalesceBinary extends AbstractBinary implements OperatorEscapeI { parent::__construct($left, $right, $lineno); - if (!$left instanceof NameExpression) { + if (!$left instanceof ContextVariable) { $test = new DefinedTest(clone $left, new TwigTest('defined'), new EmptyNode(), $left->getTemplateLine()); // for "block()", we don't need the null test as the return value is always a string if (!$left instanceof BlockReferenceExpression) { diff --git a/src/Node/Expression/Filter/DefaultFilter.php b/src/Node/Expression/Filter/DefaultFilter.php index cabd2aa1a..bccd7f0a4 100644 --- a/src/Node/Expression/Filter/DefaultFilter.php +++ b/src/Node/Expression/Filter/DefaultFilter.php @@ -19,9 +19,9 @@ use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\GetAttrExpression; -use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\Ternary\ConditionalTernary; use Twig\Node\Expression\Test\DefinedTest; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; use Twig\TwigFilter; use Twig\TwigTest; @@ -53,7 +53,7 @@ class DefaultFilter extends FilterExpression $default = new FilterExpression($node, new TwigFilter('default', [CoreExtension::class, 'default']), $arguments, $node->getTemplateLine()); } - if ('default' === $name && ($node instanceof NameExpression || $node instanceof GetAttrExpression)) { + if ('default' === $name && ($node instanceof ContextVariable || $node instanceof GetAttrExpression)) { $test = new DefinedTest(clone $node, new TwigTest('defined'), new EmptyNode(), $node->getTemplateLine()); $false = \count($arguments) ? $arguments->getNode('0') : new ConstantExpression('', $node->getTemplateLine()); diff --git a/src/Node/Expression/GetAttrExpression.php b/src/Node/Expression/GetAttrExpression.php index 0d4b96b14..e072f2a0b 100644 --- a/src/Node/Expression/GetAttrExpression.php +++ b/src/Node/Expression/GetAttrExpression.php @@ -14,6 +14,7 @@ namespace Twig\Node\Expression; use Twig\Compiler; use Twig\Extension\SandboxExtension; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Template; class GetAttrExpression extends AbstractExpression @@ -28,7 +29,7 @@ class GetAttrExpression extends AbstractExpression $nodes['arguments'] = $arguments; } - if ($arguments && !$arguments instanceof ArrayExpression && !$arguments instanceof NameExpression) { + if ($arguments && !$arguments instanceof ArrayExpression && !$arguments instanceof ContextVariable) { trigger_deprecation('twig/twig', '3.15', \sprintf('Not passing a "%s" instance as the "arguments" argument of the "%s" constructor is deprecated ("%s" given).', ArrayExpression::class, static::class, $arguments::class)); } diff --git a/src/Node/Expression/MethodCallExpression.php b/src/Node/Expression/MethodCallExpression.php index 9aede826c..922b98b10 100644 --- a/src/Node/Expression/MethodCallExpression.php +++ b/src/Node/Expression/MethodCallExpression.php @@ -12,6 +12,7 @@ namespace Twig\Node\Expression; use Twig\Compiler; +use Twig\Node\Expression\Variable\ContextVariable; class MethodCallExpression extends AbstractExpression { @@ -21,7 +22,7 @@ class MethodCallExpression extends AbstractExpression parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false, 'is_defined_test' => false], $lineno); - if ($node instanceof NameExpression) { + if ($node instanceof ContextVariable) { $node->setAttribute('always_defined', true); } } diff --git a/src/Node/Expression/NullCoalesceExpression.php b/src/Node/Expression/NullCoalesceExpression.php index c07bb3696..74ddaf791 100644 --- a/src/Node/Expression/NullCoalesceExpression.php +++ b/src/Node/Expression/NullCoalesceExpression.php @@ -18,6 +18,7 @@ use Twig\Node\Expression\Binary\NullCoalesceBinary; use Twig\Node\Expression\Test\DefinedTest; use Twig\Node\Expression\Test\NullTest; use Twig\Node\Expression\Unary\NotUnary; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; use Twig\TwigTest; @@ -60,7 +61,7 @@ class NullCoalesceExpression extends ConditionalExpression * cases might be implemented as an optimizer node visitor, but has not been done * as benefits are probably not worth the added complexity. */ - if ($this->getNode('expr2') instanceof NameExpression) { + if ($this->getNode('expr2') instanceof ContextVariable) { $this->getNode('expr2')->setAttribute('always_defined', true); $compiler ->raw('((') diff --git a/src/Node/Expression/Test/DefinedTest.php b/src/Node/Expression/Test/DefinedTest.php index 62aec9217..626cd4f3e 100644 --- a/src/Node/Expression/Test/DefinedTest.php +++ b/src/Node/Expression/Test/DefinedTest.php @@ -24,6 +24,7 @@ use Twig\Node\Expression\MacroReferenceExpression; use Twig\Node\Expression\MethodCallExpression; use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\TestExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; use Twig\TwigTest; @@ -49,7 +50,7 @@ class DefinedTest extends TestExpression trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "node" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, \get_class($node)); } - if ($node instanceof NameExpression) { + if ($node instanceof ContextVariable) { $node->setAttribute('is_defined_test', true); } elseif ($node instanceof GetAttrExpression) { $node->setAttribute('is_defined_test', true); diff --git a/src/Node/Expression/Variable/ContextVariable.php b/src/Node/Expression/Variable/ContextVariable.php index cabc16a43..01bbcb711 100644 --- a/src/Node/Expression/Variable/ContextVariable.php +++ b/src/Node/Expression/Variable/ContextVariable.php @@ -13,6 +13,6 @@ namespace Twig\Node\Expression\Variable; use Twig\Node\Expression\NameExpression; -final class ContextVariable extends NameExpression +class ContextVariable extends NameExpression { } diff --git a/src/Node/ImportNode.php b/src/Node/ImportNode.php index 124c41ba9..49e2fd19d 100644 --- a/src/Node/ImportNode.php +++ b/src/Node/ImportNode.php @@ -16,6 +16,7 @@ use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\Variable\AssignTemplateVariable; +use Twig\Node\Expression\Variable\ContextVariable; /** * Represents an import node. @@ -44,7 +45,7 @@ class ImportNode extends Node { $compiler->subcompile($this->getNode('var')); - if ($this->getNode('expr') instanceof NameExpression && '_self' === $this->getNode('expr')->getAttribute('name')) { + if ($this->getNode('expr') instanceof ContextVariable && '_self' === $this->getNode('expr')->getAttribute('name')) { $compiler->raw('$this'); } else { $compiler diff --git a/src/NodeVisitor/OptimizerNodeVisitor.php b/src/NodeVisitor/OptimizerNodeVisitor.php index a943f45c3..b82060f6b 100644 --- a/src/NodeVisitor/OptimizerNodeVisitor.php +++ b/src/NodeVisitor/OptimizerNodeVisitor.php @@ -19,6 +19,7 @@ use Twig\Node\Expression\FunctionExpression; use Twig\Node\Expression\GetAttrExpression; use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\ParentExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\ForNode; use Twig\Node\IncludeNode; use Twig\Node\Node; @@ -137,13 +138,13 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface // when do we need to add the loop variable back? // the loop variable is referenced for the current loop - elseif ($node instanceof NameExpression && 'loop' === $node->getAttribute('name')) { + elseif ($node instanceof ContextVariable && 'loop' === $node->getAttribute('name')) { $node->setAttribute('always_defined', true); $this->addLoopToCurrent(); } // optimize access to loop targets - elseif ($node instanceof NameExpression && \in_array($node->getAttribute('name'), $this->loopsTargets)) { + elseif ($node instanceof ContextVariable && \in_array($node->getAttribute('name'), $this->loopsTargets)) { $node->setAttribute('always_defined', true); } @@ -173,7 +174,7 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface || 'parent' === $node->getNode('attribute')->getAttribute('value') ) && (true === $this->loops[0]->getAttribute('with_loop') - || ($node->getNode('node') instanceof NameExpression + || ($node->getNode('node') instanceof ContextVariable && 'loop' === $node->getNode('node')->getAttribute('name') ) ) diff --git a/src/NodeVisitor/SafeAnalysisNodeVisitor.php b/src/NodeVisitor/SafeAnalysisNodeVisitor.php index 681751e98..3030ba80b 100644 --- a/src/NodeVisitor/SafeAnalysisNodeVisitor.php +++ b/src/NodeVisitor/SafeAnalysisNodeVisitor.php @@ -22,6 +22,7 @@ use Twig\Node\Expression\MethodCallExpression; use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\OperatorEscapeInterface; use Twig\Node\Expression\ParentExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; /** @@ -146,7 +147,7 @@ final class SafeAnalysisNodeVisitor implements NodeVisitorInterface } elseif ($node instanceof MethodCallExpression || $node instanceof MacroReferenceExpression) { // all macro calls are safe $this->setSafe($node, ['all']); - } elseif ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression) { + } elseif ($node instanceof GetAttrExpression && $node->getNode('node') instanceof ContextVariable) { $name = $node->getNode('node')->getAttribute('name'); if (\in_array($name, $this->safeVars)) { $this->setSafe($node, ['all']); diff --git a/src/NodeVisitor/SandboxNodeVisitor.php b/src/NodeVisitor/SandboxNodeVisitor.php index 74b686f6e..0cdff62e9 100644 --- a/src/NodeVisitor/SandboxNodeVisitor.php +++ b/src/NodeVisitor/SandboxNodeVisitor.php @@ -23,6 +23,7 @@ use Twig\Node\Expression\FunctionExpression; use Twig\Node\Expression\GetAttrExpression; use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\Unary\SpreadUnary; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\ModuleNode; use Twig\Node\Node; use Twig\Node\Nodes; @@ -122,7 +123,7 @@ final class SandboxNodeVisitor implements NodeVisitorInterface private function wrapNode(Node $node, string $name): void { $expr = $node->getNode($name); - if (($expr instanceof NameExpression || $expr instanceof GetAttrExpression) && !$expr->isGenerator()) { + if (($expr instanceof ContextVariable || $expr instanceof GetAttrExpression) && !$expr->isGenerator()) { // Simplify in 4.0 as the spread attribute has been removed there $new = new CheckToStringNode($expr); if ($expr->hasAttribute('spread')) { diff --git a/src/TokenParser/EmbedTokenParser.php b/src/TokenParser/EmbedTokenParser.php index 7bf3233e2..a19a7b31b 100644 --- a/src/TokenParser/EmbedTokenParser.php +++ b/src/TokenParser/EmbedTokenParser.php @@ -14,6 +14,7 @@ namespace Twig\TokenParser; use Twig\Node\EmbedNode; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; use Twig\Token; @@ -35,7 +36,7 @@ final class EmbedTokenParser extends IncludeTokenParser $parentToken = $fakeParentToken = new Token(Token::STRING_TYPE, '__parent__', $token->getLine()); if ($parent instanceof ConstantExpression) { $parentToken = new Token(Token::STRING_TYPE, $parent->getAttribute('value'), $token->getLine()); - } elseif ($parent instanceof NameExpression) { + } elseif ($parent instanceof ContextVariable) { $parentToken = new Token(Token::NAME_TYPE, $parent->getAttribute('name'), $token->getLine()); }