Avoid storing expression parser instances in Node attributes

This commit is contained in:
Fabien Potencier
2025-02-14 11:17:21 +01:00
parent 613d6b9498
commit d6f539bb01
+7 -3
View File
@@ -43,6 +43,7 @@ use Twig\Util\ReflectionCallable;
class Parser class Parser
{ {
private $stack = []; private $stack = [];
private ?\WeakMap $expressionRefs = null;
private $stream; private $stream;
private $parent; private $parent;
private $visitors; private $visitors;
@@ -94,6 +95,7 @@ class Parser
$this->blockStack = []; $this->blockStack = [];
$this->importedSymbols = [[]]; $this->importedSymbols = [[]];
$this->embeddedTemplates = []; $this->embeddedTemplates = [];
$this->expressionRefs = new \WeakMap();
try { try {
$body = $this->subparse($test, $dropNeedle); $body = $this->subparse($test, $dropNeedle);
@@ -111,6 +113,8 @@ class Parser
} }
throw $e; throw $e;
} finally {
$this->expressionRefs = null;
} }
$node = new ModuleNode(new BodyNode([$body]), $this->parent, new Nodes($this->blocks), new Nodes($this->macros), new Nodes($this->traits), $this->embeddedTemplates, $stream->getSourceContext()); $node = new ModuleNode(new BodyNode([$body]), $this->parent, new Nodes($this->blocks), new Nodes($this->macros), new Nodes($this->traits), $this->embeddedTemplates, $stream->getSourceContext());
@@ -557,7 +561,7 @@ class Parser
private function checkPrecedenceDeprecations(ExpressionParserInterface $expressionParser, AbstractExpression $expr) private function checkPrecedenceDeprecations(ExpressionParserInterface $expressionParser, AbstractExpression $expr)
{ {
$expr->setAttribute('expression_parser', $expressionParser); $this->expressionRefs[$expr] = $expressionParser;
$precedenceChanges = $this->parsers->getPrecedenceChanges(); $precedenceChanges = $this->parsers->getPrecedenceChanges();
// Check that the all nodes that are between the 2 precedences have explicit parentheses // Check that the all nodes that are between the 2 precedences have explicit parentheses
@@ -576,7 +580,7 @@ class Parser
if (!\in_array($expressionParser, $changes, true)) { if (!\in_array($expressionParser, $changes, true)) {
continue; continue;
} }
if ($node->hasAttribute('expression_parser') && $ep === $node->getAttribute('expression_parser')) { if (isset($this->expressionRefs[$node]) && $ep === $this->expressionRefs[$node]) {
$change = $expressionParser->getPrecedenceChange(); $change = $expressionParser->getPrecedenceChange();
trigger_deprecation($change->getPackage(), $change->getVersion(), \sprintf('As the "%s" %s operator will change its precedence in the next major version, add explicit parentheses to avoid behavior change in "%s" at line %d.', $expressionParser->getName(), ExpressionParserType::getType($expressionParser)->value, $this->getStream()->getSourceContext()->getName(), $node->getTemplateLine())); trigger_deprecation($change->getPackage(), $change->getVersion(), \sprintf('As the "%s" %s operator will change its precedence in the next major version, add explicit parentheses to avoid behavior change in "%s" at line %d.', $expressionParser->getName(), ExpressionParserType::getType($expressionParser)->value, $this->getStream()->getSourceContext()->getName(), $node->getTemplateLine()));
} }
@@ -586,7 +590,7 @@ class Parser
foreach ($precedenceChanges[$expressionParser] as $ep) { foreach ($precedenceChanges[$expressionParser] as $ep) {
foreach ($expr as $node) { foreach ($expr as $node) {
/** @var AbstractExpression $node */ /** @var AbstractExpression $node */
if ($node->hasAttribute('expression_parser') && $ep === $node->getAttribute('expression_parser') && !$node->hasExplicitParentheses()) { if (isset($this->expressionRefs[$node]) && $ep === $this->expressionRefs[$node] && !$node->hasExplicitParentheses()) {
$change = $ep->getPrecedenceChange(); $change = $ep->getPrecedenceChange();
trigger_deprecation($change->getPackage(), $change->getVersion(), \sprintf('As the "%s" %s operator will change its precedence in the next major version, add explicit parentheses to avoid behavior change in "%s" at line %d.', $ep->getName(), ExpressionParserType::getType($ep)->value, $this->getStream()->getSourceContext()->getName(), $node->getTemplateLine())); trigger_deprecation($change->getPackage(), $change->getVersion(), \sprintf('As the "%s" %s operator will change its precedence in the next major version, add explicit parentheses to avoid behavior change in "%s" at line %d.', $ep->getName(), ExpressionParserType::getType($ep)->value, $this->getStream()->getSourceContext()->getName(), $node->getTemplateLine()));
} }