Skip to content

Commit

Permalink
feature #4577 Make the raw filter more "sticky" (fabpot)
Browse files Browse the repository at this point in the history
This PR was merged into the 4.x branch.

Discussion
----------

Make the raw filter more "sticky"

When using `{{ foo|raw }}`, the expression won't be escaped as expected.

But if you store the result and reuse it afterwards, the value is not considered "safe" anymore:

`{% set bar = foo|raw %}{{ bar }}`

Here `bar` will be escaped.

For Twig 4.x, I propose to make the `raw` filter "sticky" by wrapping the value with `Markup`. That way, the second example won't be escaped as expected.

Commits
-------

54dea53 Make the raw filter "sticky"
  • Loading branch information
fabpot committed Feb 14, 2025
2 parents 3e97fc6 + 54dea53 commit 82c88d3
Show file tree
Hide file tree
Showing 5 changed files with 18 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# 4.0.0 (2024-XX-XX)

* Make the `raw` filter sticky
* Add back the `if` condition on `for` loops
* Add return types to all ExtensionInterface methods (`getFunctions()`, `getFilters()`, etc.)
* Add support for recursive loops (via the `loop()` function)
Expand Down
6 changes: 5 additions & 1 deletion src/Node/Expression/Filter/RawFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public function __construct(AbstractExpression $node, ?TwigFilter $filter = null

public function compile(Compiler $compiler): void
{
$compiler->subcompile($this->getNode('node'));
$compiler
->raw('(is_scalar($tmp = ')
->subcompile($this->getNode('node'))
->raw(') ? new Markup($tmp, $this->env->getCharset()) : $tmp)')
;
}
}
10 changes: 10 additions & 0 deletions tests/Fixtures/filters/raw.test
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@
"raw" filter excludes a variable from being escaped
--TEMPLATE--
{{ br|raw }}
{% set br1 = '<br>'|raw %}
{{ br1 }}
{{ include('included', {br: '<br>'|raw, br1: br1}) }}
--TEMPLATE(included)--
{{ br }}
{{ br1 }}
--DATA--
return ['br' => '<br>']
--EXPECT--
<br>
<br>

<br>
<br>
2 changes: 1 addition & 1 deletion tests/Node/Expression/Filter/RawTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public static function provideTests(): iterable
$node = new RawFilter(new ConstantExpression('foo', 12));

return [
[$node, '"foo"'],
[$node, '(is_scalar($tmp = "foo") ? new Markup($tmp, $this->env->getCharset()) : $tmp)'],
];
}
}
2 changes: 1 addition & 1 deletion tests/Node/ForTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public static function provideTests(): iterable
\$parent = \$context;
\$context['loop'] = new \Twig\Runtime\LoopContext(\$iterator, \$parent, \$blocks, \$recurseFunc, \$depth);
foreach (\$iterator as \$context["_key"] => \$context["item"]) {
yield from CoreExtension::getAttribute(\$this->env, \$this->source, \$context["loop"], "__invoke", arguments: [CoreExtension::getAttribute(\$this->env, \$this->source, \$context["item"], "children", arguments: [], lineno: 1)], type: "method", lineno: 1);
yield from (is_scalar(\$tmp = CoreExtension::getAttribute(\$this->env, \$this->source, \$context["loop"], "__invoke", arguments: [CoreExtension::getAttribute(\$this->env, \$this->source, \$context["item"], "children", arguments: [], lineno: 1)], type: "method", lineno: 1)) ? new Markup(\$tmp, \$this->env->getCharset()) : \$tmp);
}
unset(\$context['_key'], \$context['item'], \$context['loop']);
\$context = array_intersect_key(\$context, \$parent) + \$parent;
Expand Down

0 comments on commit 82c88d3

Please sign in to comment.