This PR was merged into the 3.x branch.
Discussion
----------
Check that the use tag is allowed before resolving trait templates
Commits
-------
18863f0371 Check that the use tag is allowed before resolving trait templates
This PR was squashed before being merged into the 3.x branch.
Discussion
----------
Fix `TemplateWrapper::hasBlock()` and `TemplateWrapper::getBlockNames()` omitting environment globals
Commits
-------
0e1852632f Fix `TemplateWrapper::hasBlock()` and `TemplateWrapper::getBlockNames()` omitting environment globals
This PR was merged into the 3.x branch.
Discussion
----------
Fix html_attr dropping style declarations whose value is zero
`InlineStyle::getValue()` skips a declaration when `empty($value)` is true. That also matches `0`, `0.0` and `'0'`, which are ordinary CSS values.
```twig
{{ html_attr({style: {opacity: 0}}) }} {# "" #}
{{ html_attr({style: {'flex-grow': 0, color: 'red'}}) }} {# style="color: red;" #}
{{ html_attr({style: ['opacity: 0']}) }} {# style="opacity: 0;" #}
{{ html_attr({class: [0, 'a']}) }} {# class="0 a" #}
```
Same declaration, opposite result. The numeric-key branch never consults `empty()`, and the sibling `SeparatedTokenList::getValue()` already uses an explicit `null !== $v && false !== $v` test, so token lists keep a `0`. When every declaration is dropped the attribute is omitted entirely, so `{style: {opacity: 0}}` renders nothing at all, and `HtmlExtension::htmlAttrValue('style', ['opacity' => 0])` returns `null`.
Silently affects `opacity`, `z-index`, `margin`, `padding`, `border`, `flex-grow` and custom properties.
The guard now lists the values that carry no declaration. `null`, `false`, `''` and `true` still skip, and `[]` is kept in that list so empty arrays behave exactly as before.
`html_attr.rst` documents the `null`/`false`/`true` omission rules and says nothing about zero, so no doc change is needed. `HtmlAttrTest.php` already has a case named "zero is not treated as falsy", but only for a plain attribute value.
3 tests added. Reverting the fix fails the first; a naive `null`/`false`-only guard fails the other two.
Commits
-------
c072ff85b3 Fix html_attr dropping style declarations whose value is zero
InlineStyle::getValue() skipped a declaration when empty($value) was true,
which also matches 0, 0.0 and '0'. Those are ordinary CSS values (opacity: 0,
z-index: 0, margin: 0, flex-grow: 0), so they were silently dropped, and a
style map containing only such declarations omitted the attribute entirely.
The sibling SeparatedTokenList::getValue() already uses an explicit
null/false test, so class token lists keep a 0 while style declarations did
not. The numeric-key branch of InlineStyle itself never consulted empty(),
so {style: ['opacity: 0']} printed while {style: {opacity: 0}} did not.
This PR was merged into the 3.x branch.
Discussion
----------
Fix the default filter fallback reusing a null-safe temporary variable
The `default` filter reuses the fallback argument node in both branches of the conditional it compiles to. Because `GetAttrExpression` records the temporary variable it allocated for a null-safe chain on the node itself, the second compilation skipped the assignment and emitted a bare `$_vN` reference, so `{{ item?.label|default(item?.name) }}` warned about an undefined variable whenever `item` was null.
The fallback node is now cloned, like the node used for the defined test already is, so each branch compiles its own temporary.
Fixes#4914
Commits
-------
2d0c30b075 Remove redundant default filter cases
0b4199c522 Strengthen the default filter regression test
86c830ef45 Fix the default filter fallback reusing a null-safe temporary variable
This PR was squashed before being merged into the 3.x branch.
Discussion
----------
Remove the documentation comments compilation overhead
Documentation comments are now attached to semantic nodes during parsing, removing the dedicated AST traversal. Token parsers returning placeholder nodes can select the semantic documentation target.
Commits
-------
60eed4ccd5 Remove the documentation comments compilation overhead
This PR was merged into the 3.x branch.
Discussion
----------
Release destructuring temporaries after assignment
The compiled destructuring code kept the right-hand side alive in a hidden local until the end of rendering, wasting memory on large arrays and iterators. The temporary is now cleared as part of the compiled expression.
The way it works:
For this Twig expression:
```twig
{% do {name, email: address} = user %}
```
The previous generated expression was conceptually:
```php
[$context["name"], $context["address"]] = [
getAttribute($_v0 = $context["user"], "name"),
getAttribute($_v0, "email"),
];
```
The branch now generates:
```php
[
[$context["name"], $context["address"]] = [
getAttribute($_v0 = $context["user"], "name"),
getAttribute($_v0, "email"),
],
$_v0 = null,
][0]
```
The important wrapper is:
```php
[$originalExpression, $_v0 = null][0]
```
PHP evaluates array elements from left to right:
1. The original destructuring assignment runs.
2. The temporary variable is set to `null`, releasing its reference.
3. `[0]` returns the result of the original expression.
This preserves Twig’s rule that a destructuring expression returns its right-hand value.
Commits
-------
c459ef0bdd Release destructuring temporaries after assignment
This PR was merged into the 3.x branch.
Discussion
----------
Clarify source function trust requirements
Commits
-------
fdfef2b14a Clarify source function trust requirements
This PR was merged into the 3.x branch.
Discussion
----------
Report regular expression errors from the matches operator
The `matches` operator now throws a `RuntimeError` when PCRE cannot evaluate a regular expression instead of silently treating the error as a non-match.
This makes failures such as exhausted backtrack limits visible to template authors.
Commits
-------
e5347301d7 Throw on PCRE errors in the matches operator
This PR was merged into the 3.x branch.
Discussion
----------
Deprecate prefixed macro definedness checks
Testing a macro through a legacy `macro_`-prefixed name would have silently returned `false` in Twig 4.0 without ever warning; `has()` now triggers the same deprecation as `call()`.
Commits
-------
977aef7172 Deprecate prefixed macro definedness checks
This PR was merged into the 3.x branch.
Discussion
----------
Fix duplicate macro deprecation wording
With three or more definitions of the same macro, the deprecation mislabeled the previous duplicate as the "first definition"; it now reports "previous definition" and "new definition" lines accurately.
Commits
-------
8a37332def Fix duplicate macro deprecation wording
This PR was merged into the 3.x branch.
Discussion
----------
Throw when list formatting fails
`format_list` silently returned an empty string when `IntlListFormatter` fails (for instance on malformed UTF-8); it now throws a `RuntimeError` with the formatter's error message.
Commits
-------
87b930ae67 Throw when list formatting fails
This PR was squashed before being merged into the 3.x branch.
Discussion
----------
Fix documentation inaccuracies found during the 3.29 review
Aligns the docs with actual behavior: `include_only` globals, `html_attr` `data-*` iterables, destructuring slot consumption and iterator reuse, a caution for the Tempest converter, `MacroNode::compile()` overrides, and a duplicated CHANGELOG entry.
Commits
-------
59f7d67848 Document that reusing a non-rewindable iterator after destructuring is unsupported
099fa3471a Document that sequence destructuring consumes one value per pattern slot
abbdf82823 Fix the html_attr documentation about iterables in data attributes
be220e6fd8 Warn about untrusted input with the default Tempest markdown converter
1205b8b6ca Document that overriding MacroNode::compile() is not supported anymore
7bd052dd91 Merge overlapping CHANGELOG entries for the destructuring fatal error fix
207f873739 Document that include_only keeps global variables available
This PR was squashed before being merged into the 3.x branch.
Discussion
----------
Remove lazy macro import resolution
Reverts the fix for #4879
This removes the unreleased lazy resolution of template-level macro imports, which introduced complex and surprising behavior around template state, blocks, interrupted renders and sandboxing.
Macros called from another template should import their dependencies inside their own body. The documentation now explains this pattern.
Commits
-------
cf971e1a59 Remove lazy macro import resolution
This PR was squashed before being merged into the 3.x branch.
Discussion
----------
Honor date formatter prototype calendars
This fixes calendar selection when an `IntlDateFormatter` prototype is configured.
An explicit calendar now takes precedence, followed by the prototype calendar, with Gregorian used as the final fallback. In particular, the `TRADITIONAL` calendar is no longer mistaken for an absent value because its constant value is zero.
Commits
-------
1de0bfceb4 Honor date formatter prototype calendars
This PR was squashed before being merged into the 3.x branch.
Discussion
----------
Fix Stringable keys for ArrayAccess implementations
This restores support for `Stringable` keys on string-keyed `ArrayAccess` implementations such as `ArrayObject` and `ArrayIterator`, while preserving object keys for `SplObjectStorage`.
The object key is attempted first and is converted to a string only when the implementation rejects it. The optimized and strict lookup paths now share the same behavior without duplicate existence checks.
Commits
-------
f3f1649955 Fix Stringable keys for ArrayAccess implementations
This PR was merged into the 3.x branch.
Discussion
----------
Evaluate object destructuring expressions once
This ensures the right-hand expression of an object or mapping destructuring assignment is evaluated exactly once.
All properties are now read from the same resolved value, avoiding repeated side effects and unnecessary work while preserving assignment order and return semantics.
Commits
-------
609376f491 Fix repeated object destructuring evaluation
This PR was squashed before being merged into the 3.x branch.
Discussion
----------
Restore void return type compatibility for extension points
This restores the PHPDoc-only `void` return types on non-final extension points so subclasses written for Twig 3.28 continue to load on Twig 3.29.
Native return types remain on final classes and test methods where they are backward compatible. PHP CS Fixer is configured to preserve the compatible signatures.
Commits
-------
6bbbb49c3e Restore void return type compatibility for extension points
This PR was squashed before being merged into the 3.x branch.
Discussion
----------
Reject destructuring patterns containing no variables
This rejects sequence destructuring patterns containing only empty slots, such as `[,]` and `[,,]`.
These patterns previously compiled to an empty PHP list assignment and caused an uncatchable fatal error. They now produce a Twig `SyntaxError` with the template source and line.
Commits
-------
a3a318face Reject destructuring patterns containing no variables
This PR was squashed before being merged into the 3.x branch.
Discussion
----------
Extract `htmlAttrValue()` from `html_attr` for standalone attribute rendering
The per-value resolution behind `html_attr` is extracted into a new public `HtmlExtension::htmlAttrValue()`, returning the unescaped value or `null` to omit the attribute; `html_attr()` now delegates to it, output unchanged, existing tests untouched. This lets third parties render a single attribute exactly like `html_attr` without a Twig `Environment`, since the resolution is escaper-free. symfony/ux#3820 and symfony/ux#3821 depend on this PR.
The `data-*` branch only tested `is_scalar()`, so a `\Stringable` was JSON-encoded instead of using its string representation; the same object already rendered its string form in `title` or `class`, and `AttributeValueInterface` was already excluded from that branch.
| Value in `data-value` | Before | After |
| --- | --- | --- |
| a `\Stringable` | `data-value="{}"` | `data-value="hello"` |
| a `\Stringable` that is also `JsonSerializable` | `data-value=""01JABC""` | `data-value="01JABC"` |
Commits
-------
9b18e3757d Extract `htmlAttrValue()` from `html_attr` for standalone attribute rendering
This PR was merged into the 3.x branch.
Discussion
----------
Fix an empty destructuring pattern triggering a PHP fatal error instead of a SyntaxError
Commits
-------
a2b023397e Fix an empty destructuring pattern triggering a PHP fatal error instead of a SyntaxError