diff --git a/CHANGELOG b/CHANGELOG index bc1d00b8d9..21fb43f107 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,8 @@ # 3.18.0 (2024-XX-XX) + * Fix unary operator precedence change * Ignore `SyntaxError` exceptions from undefined handlers when using the `guard` tag - * Add a way to stream template rendering (`TemplateWrapper::stream()` and `TemplateWrapper::streamBlock()` ) + * Add a way to stream template rendering (`TemplateWrapper::stream()` and `TemplateWrapper::streamBlock()`) # 3.17.1 (2024-12-12) diff --git a/src/ExpressionParser.php b/src/ExpressionParser.php index eb60a43b3b..0f1b0ed366 100644 --- a/src/ExpressionParser.php +++ b/src/ExpressionParser.php @@ -149,7 +149,6 @@ private function triggerPrecedenceDeprecations(AbstractExpression $expr): void return; } $target = explode('_', $unaryOp)[1]; - $change = $this->unaryOperators[$target]['precedence_change']; /** @var AbstractExpression $node */ $node = $expr->getNode('node'); foreach ($this->precedenceChanges as $operatorName => $changes) { @@ -157,6 +156,7 @@ private function triggerPrecedenceDeprecations(AbstractExpression $expr): void continue; } if ($node->hasAttribute('operator') && $operatorName === $node->getAttribute('operator')) { + $change = $this->unaryOperators[$target]['precedence_change']; trigger_deprecation($change->getPackage(), $change->getVersion(), \sprintf('Add explicit parentheses around the "%s" unary operator to avoid behavior change in the next major version as its precedence will change in "%s" at line %d.', $target, $this->parser->getStream()->getSourceContext()->getName(), $node->getTemplateLine())); } } diff --git a/tests/ExpressionParserTest.php b/tests/ExpressionParserTest.php index 162f8836c1..9b9d9b5048 100644 --- a/tests/ExpressionParserTest.php +++ b/tests/ExpressionParserTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Twig\Attribute\FirstClassTwigCallableReady; +use Twig\Compiler; use Twig\Environment; use Twig\Error\SyntaxError; use Twig\Extension\AbstractExtension; @@ -24,6 +25,7 @@ use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\FunctionExpression; use Twig\Node\Expression\TestExpression; +use Twig\Node\Expression\Unary\AbstractUnary; use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; use Twig\Parser; @@ -564,6 +566,28 @@ public function testTwoWordTestPrecedence() $this->expectNotToPerformAssertions(); } + public function testUnaryPrecedenceChange() + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addExtension(new class () extends AbstractExtension { + public function getOperators() + { + $class = new class (new ConstantExpression('foo', 1), 1) extends AbstractUnary { + public function operator(Compiler $compiler): Compiler + { + return $compiler->raw('!'); + } + }; + + return [['!' => ['precedence' => 50, 'class' => $class::class]], []]; + } + }); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ !false ? "OK" : "KO" }}', 'index'))); + $this->expectNotToPerformAssertions(); + } + private static function createContextVariable(string $name, array $attributes): ContextVariable { $expression = new ContextVariable($name, 1);