diff --git a/CHANGELOG b/CHANGELOG index 991e99dfc..47e3a7240 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ # 3.29.0 (2026-XX-XX) + * Fix the `default` filter fallback emitting an undefined variable warning when it uses the null-safe operator * Fix the `matches` operator silently treating PCRE execution errors as non-matches * Add the `HtmlExtension::htmlAttrValue()` method to resolve a single HTML attribute value the way the `html_attr` function renders it * Fix `html_attr` JSON encoding a `Stringable` value in a `data-*` attribute instead of using its string representation diff --git a/src/Node/Expression/Filter/DefaultFilter.php b/src/Node/Expression/Filter/DefaultFilter.php index fc64b88a9..cef374829 100644 --- a/src/Node/Expression/Filter/DefaultFilter.php +++ b/src/Node/Expression/Filter/DefaultFilter.php @@ -55,7 +55,7 @@ class DefaultFilter extends FilterExpression if ('default' === $name && ($node instanceof ContextVariable || $node instanceof GetAttrExpression)) { $test = new DefinedTest(clone $node, new TwigTest('defined', null, ['always_allowed_in_sandbox' => true]), new EmptyNode(), $node->getTemplateLine()); - $false = \count($arguments) ? $arguments->getNode('0') : new ConstantExpression('', $node->getTemplateLine()); + $false = \count($arguments) ? clone $arguments->getNode('0') : new ConstantExpression('', $node->getTemplateLine()); $node = new ConditionalTernary($test, $default, $false, $node->getTemplateLine()); } else { diff --git a/tests/ExpressionParserTest.php b/tests/ExpressionParserTest.php index 5754bf330..649e2b84c 100644 --- a/tests/ExpressionParserTest.php +++ b/tests/ExpressionParserTest.php @@ -445,6 +445,27 @@ class ExpressionParserTest extends TestCase ['foo' => (object) ['bar' => (object) ['baz' => null]]], '', ], + // default filter fallback + [ + '{{ foo?.bar|default(foo?.baz) }}', + ['foo' => null], + '', + ], + [ + '{{ foo?.bar|default(foo?.baz.qux) }}', + ['foo' => null], + '', + ], + [ + '{{ foo?.bar|default(foo?.baz) }}', + ['foo' => (object) ['bar' => null, 'baz' => 'qux']], + 'qux', + ], + [ + '{{ foo?.bar|default(foo?.baz) }}', + ['foo' => (object) ['bar' => 'corge', 'baz' => 'qux']], + 'corge', + ], ]; }