Commit Graph

130 Commits

Author SHA1 Message Date
Fabien Potencier 89f886e324 Skip the string cast in PrintNode when the expression is already a string and add tests 2026-06-03 19:56:27 +02:00
Fabien Potencier 8a4b77920a Add PHPUnit attributes alongside annotations to silence doc-comment metadata deprecations on PHPUnit 11 2026-06-03 18:26:39 +02:00
Fabien Potencier ea3f7a2844 Validate macro name in MacroReferenceExpression constructor
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.
2026-05-19 23:42:31 +02:00
Alexandre Daubois e9ff55f691 Fix sandbox bypass: PHP code injection via {% use %} template name 2026-05-19 22:50:45 +02:00
Fabien Potencier 5462817da0 Add a needs_is_sandboxed option for filters, functions, and tests 2026-05-16 18:52:17 +01:00
Fabien Potencier 751a187f07 feature #4748 Support short-circuiting in null-safe operator chains (HypeMC)
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
2026-02-08 19:02:03 +01:00
Fabien Potencier 861215c507 Fix CS 2026-02-07 09:07:38 +01:00
HypeMC d56e8e2dba Support short-circuiting in null-safe operator chains 2026-02-06 22:36:57 +01:00
Felds Liscia 263c04fd1f Add null-safe operator 2026-01-17 14:57:47 +01:00
Fabien Potencier 8391928896 Fix deprecations 2026-01-12 09:52:14 +01:00
Simon André e4d7915702 Compile 'index' with repr (not string) in EmbedNode
Before this fix, the generated Template code had quotes around the
index (integer) parameter value.
2025-09-25 08:28:51 +02:00
Nicolas Grekas 85a4817128 CS fixes 2025-07-29 10:07:07 +02:00
Fabien Potencier 56204e951a Move some tests 2025-02-26 22:10:10 +01:00
Fabien Potencier d7702840da Remove $templateName from Template::loadTemplate() 2025-02-21 23:48:41 +01:00
Fabien Potencier 2d84abfd08 Make the defined test implementation more generic 2025-02-21 08:36:21 +01:00
Fabien Potencier 445f74cfe0 [SECURITY] Fix a security issue where escaping was missing when using ?? 2025-01-29 07:52:07 +01:00
Fabien Potencier 609767522a Add ForElseNode 2025-01-24 15:25:30 +01:00
Fabien Potencier 76062c8d51 Fix CS 2025-01-19 16:54:05 +01:00
Fabien Potencier efd12ef0cc Optimize NameExpression compilation 2025-01-02 12:32:36 +01:00
Fabien Potencier 14fc89ebea Fix CS 2024-11-30 09:42:13 +01:00
Fabien Potencier 4f8ba93600 Enforce AbstractBinary for all binary operators 2024-11-29 16:51:09 +01:00
Fabien Potencier bdb0f3c042 Fix having macro variables starting with an underscore 2024-11-26 16:59:45 +01:00
Fabien Potencier 494f010d29 Revert "minor #4411 Add return type to getDebugInfo (ruudk)"
This reverts commit 868b429853, reversing
changes made to b0017ad8c3.
2024-10-25 16:01:04 +02:00
Fabien Potencier 05550cafb1 minor #4410 Add return type to compiled macro (ruudk)
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
2024-10-25 08:22:06 +02:00
Fabien Potencier 89fdc7d31d Refactor code 2024-10-25 07:47:55 +02:00
Ruud Kamphuis 1e7c719e24 Add return type to compiled macro
This makes it easier for TwigStan to analyze the return type.
2024-10-24 13:51:09 +02:00
Ruud Kamphuis 0bb49dc40f Add return type to getDebugInfo 2024-10-24 13:37:17 +02:00
Fabien Potencier 612c7a14be Improve ImportNode impl 2024-10-24 07:23:37 +02:00
Fabien Potencier 2735f81a22 feature #4398 Rename Node classes related to variables (fabpot)
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
2024-10-23 22:29:28 +02:00
Fabien Potencier f43eba5b10 minor #4403 Documentation for types tag uses Twig types in examples instead of PHP (drjayvee)
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
2024-10-23 16:54:05 +02:00
Jeroen Versteeg f3e0a00cf0 Documentation for types tag uses Twig types in examples instead of PHP 2024-10-23 16:54:04 +02:00
Fabien Potencier fc15e7ccbc Rename Node classes related to variables 2024-10-23 12:36:57 +02:00
Fabien Potencier 72693884c4 Simplify code 2024-10-21 07:20:17 +02:00
Fabien Potencier 1105964873 Rework macros handling 2024-10-20 21:53:20 +02:00
Fabien Potencier 66658a3c1d Check reserved names in TempNameExpression 2024-10-14 19:42:47 +02:00
Fabien Potencier 223d36bbae Add support for named arguments on macro calls and dot operator arguments 2024-10-11 11:31:40 +02:00
Fabien Potencier 8b278986b8 Deprecate using Node directly, introduce EmptyNode and Nodes 2024-09-27 07:42:41 +02:00
Ruud Kamphuis 5c73c6695c Remove useless assign in compiled code 2024-09-26 14:36:01 +02:00
Fabien Potencier 09790a7542 Fix 'ignore missing' when used on an 'embed' tag 2024-09-26 07:55:44 +02:00
Fabien Potencier 9855e35d44 Optimize compiled code for "set" tag 2024-09-24 16:47:02 +02:00
Fabien Potencier b86575cfd6 Deprecate Environment::mergeGlobals() 2024-09-07 14:05:02 +02:00
Fabien Potencier a7e497af48 minor #4280 Fix isset in ForLoopNode (ruudk)
This PR was merged into the 3.x branch.

Discussion
----------

Fix isset in ForLoopNode

Even though the code works perfectly fine, PHPStan doesn't understand it.

> Offset 'revindex0' does not exist on array{parent: array{index0: int<1, max>, index: int<2, max>, first: false, revindex0?: int, revindex?: int, length: int<0, max>, last?: bool}.
> Offset 'revindex' does not exist on array{parent: array{index0: int<1, max>, index: int<2, max>, first: false, revindex0: int, revindex?: int, length: int<0, max>, last?: bool}.

Since we want to work with `revindex` and `revindex0`, we can better check for their existence, instead of `length`.

/cc `@stof` `@fabpot`

Commits
-------

2b887e64 Fix isset in ForLoopNode
2024-09-06 12:33:51 +02:00
Fabien Potencier 2ae0c0d38c Fix CS 2024-09-06 12:33:38 +02:00
Ruud Kamphuis 2b887e64d5 Fix isset in ForLoopNode
Even though the code works perfectly fine, PHPStan doesn't understand it.

Since we want to work with `revindex` and `revindex0`, we can better check for their
existence, instead of `length`.
2024-09-06 09:42:11 +02:00
Ruud Kamphuis 4cd8955f74 Fix iterable return type
Currently, the PHPDoc states that a string should be returned, but that's not correct.

It can be anything that is echo-able.

That means any scalar, Stringable and even null.
2024-09-05 09:24:38 +02:00
Alexandre Daubois 797e490356 Improve exception expectations reliability 2024-09-04 15:22:48 +02:00
Fabien Potencier dd9af8ec25 feature #4265 Migrate NodeTestCase to static data providers (derrabus)
This PR was merged into the 3.x branch.

Discussion
----------

Migrate NodeTestCase to static data providers

PHPUnit 11 requires data providers to be static. This PR prepares our abstract `NodeTestCase` by deprecating the non-static `getTests()` and adding a static `provideTests()` as a replacement. I've also added PHPUnit attributes which newer PHPUnit releases prefer over PHPDoc annotations.

Commits
-------

f555a33c Migrate NodeTestCase to static data providers
2024-09-03 14:03:04 +02:00
Alexander M. Turek f555a33caf Migrate NodeTestCase to static data providers 2024-09-03 13:51:42 +02:00
Ruud Kamphuis 09a43a9f6f Replace return; yield with yield from []
This has the same effect, but looks less hacky and makes PHPStan happy.
https://phpstan.org/r/df7fcc88-1df8-428e-b675-5dc6965c34d6

Previously this was needed to work properly with output capturing.
2024-09-03 09:55:58 +02:00
Alexander M. Turek 1e5dea44f9 Fix MacroTest 2024-09-02 18:57:57 +02:00