This PR was merged into the 3.x branch.
Discussion
----------
Allow calling a macro with a dynamic name via the dot operator
Closes#4715
Commits
-------
87093aab9e Allow calling a macro with a dynamic name via the dot operator
This PR was merged into the 3.x branch.
Discussion
----------
Add an allow-list for tests to the sandbox security policy
Commits
-------
416d07da1d Add an allow-list for tests to the sandbox security policy
The name passed to MacroReferenceExpression is emitted as raw PHP in
compile() via "->{$name}(...)". Callers were expected to validate
the name, but a missing check led to CVE-2026-XXXXX (PHP code injection
via _self / import macro reference): defense-in-depth, validate the
name in the constructor so the class is safe by construction.
This PR was merged into the 3.x branch.
Discussion
----------
Support short-circuiting in null-safe operator chains
This PR adds short-circuiting for null-safe operator chains, using the same rules as PHP, `PropertyAccess`, and the `ExpressionLanguage`.
Previously, only the immediate null-safe access was guarded. With this change, as soon as a `null` is encountered at a null-safe access, the rest of the chain is skipped.
My approach was to move the null check outside of the `getAttribute()` calls so the expression can immediately return `null`, eg:
```twig
foo?.bar.baz
```
Before:
```php
yield $this->env
->getRuntime('Twig\Runtime\EscaperRuntime')
->escape(
CoreExtension::getAttribute(
$this->env,
$this->source,
(
null === (
$_v0 = (
isset($context['foo']) || array_key_exists('foo', $context)
? $context['foo']
: throw new RuntimeError('Variable "foo" does not exist.', 3, $this->source)
)
)
? null
: CoreExtension::getAttribute(
$this->env,
$this->source,
$_v0,
'bar',
[],
'any',
false,
false,
false,
3
)
),
'baz',
[],
'any',
false,
false,
false,
3
),
'html',
null,
true
);
```
Now:
```php
yield $this->env
->getRuntime('Twig\Runtime\EscaperRuntime')
->escape(
(
null === (
$_v0 = (
isset($context['foo']) || array_key_exists('foo', $context)
? $context['foo']
: throw new RuntimeError('Variable "foo" does not exist.', 3, $this->source)
)
)
? null
: CoreExtension::getAttribute(
$this->env,
$this->source,
CoreExtension::getAttribute(
$this->env,
$this->source,
$_v0,
'bar',
[],
'any',
false,
false,
false,
3
),
'baz',
[],
'any',
false,
false,
false,
3
)
),
'html',
null,
true
);
```
Commits
-------
d56e8e2dba Support short-circuiting in null-safe operator chains
This PR was merged into the 3.x branch.
Discussion
----------
Add return type to compiled macro
This makes it easier for TwigStan to analyze the return type.
Commits
-------
1e7c719e24 Add return type to compiled macro
This PR was merged into the 3.x branch.
Discussion
----------
Rename Node classes related to variables
Whenever I work on Twig internals, it's always complicated to reason about variable names, probably because the class names are confusing. This PR is an attempt to find "better" and more explicit names.
This PR does the following renaming:
* `NameExpression` to `Variable\ContextVariable`
Represents the value of a context variable like `$context[VAR] ?? null`
* `AssignNameExpression` to `Variable\AssignContextVariable`
Represents a context variable assignment like in `$context[VAR] = `
* `TempNameExpression` to `Variable\LocalVariable`
Represents a "private" local variable like `$_l111`
Commits
-------
fc15e7ccbc Rename Node classes related to variables
This PR was squashed before being merged into the 3.x branch.
Discussion
----------
Documentation for types tag uses Twig types in examples instead of PHP
Specifically, "bool" => "boolean" and "int" => "number".
This aligns the `types` documentation with templates.rst. See #4362
Thanks, `@alexander`-schranz!
Commits
-------
f3e0a00cf0 Documentation for types tag uses Twig types in examples instead of PHP