Reset the escaping state when compiling a new template after a compilation error

This commit is contained in:
Fabien Potencier
2026-09-25 23:43:04 +02:00
parent 6ec06d1883
commit 39642e4cdf
3 changed files with 31 additions and 0 deletions
+1
View File
@@ -1,6 +1,7 @@
# 3.30.1 (2026-XX-XX)
* Reject a deprecated `Template` instance created by another environment in `Environment::resolveTemplate()`
* Fix a compilation error inside an `autoescape` tag leaking its escaping strategy into the next compiled template
* Speed up reading object attributes backed by getters or class constants
* Fix the sandbox not reporting the line of a rejected `guard` tag
+2
View File
@@ -51,12 +51,14 @@ final class EscaperNodeVisitor implements NodeVisitorInterface
public function enterNode(Node $node, Environment $env): Node
{
if ($node instanceof ModuleNode) {
$this->defaultStrategy = false;
if ($env->hasExtension(EscaperExtension::class) && $defaultStrategy = $env->getExtension(EscaperExtension::class)->getDefaultStrategy($node->getTemplateName())) {
$this->defaultStrategy = $defaultStrategy;
}
$node->setAttribute('strategy', \is_string($this->defaultStrategy) ? $this->defaultStrategy : false);
$this->safeVars = [];
$this->blocks = [];
$this->statusStack = [];
$this->usesEscaper = false;
} elseif ($node instanceof AutoEscapeNode) {
$this->statusStack[] = $node->getAttribute('value');
+28
View File
@@ -14,6 +14,7 @@ namespace Twig\Tests\NodeVisitor;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Twig\Environment;
use Twig\Error\Error;
use Twig\Extension\AbstractExtension;
use Twig\Loader\ArrayLoader;
use Twig\Node\Expression\ConstantExpression;
@@ -21,6 +22,7 @@ use Twig\Node\Node;
use Twig\Node\Nodes;
use Twig\Node\PrintNode;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TwigFilter;
class EscaperTest extends TestCase
{
@@ -46,6 +48,32 @@ class EscaperTest extends TestCase
yield 'escaping in an embedded template only' => [false, '{% embed "embedded" %}{% block content %}{{ foo|e }}{% endblock %}{% endembed %}', 1, '<br>'];
}
public function testCompilationErrorInsideAnAutoescapeTagDoesNotAffectTheNextTemplate(): void
{
$env = new Environment(new ArrayLoader([
'broken.html' => '{% autoescape false %}{{ foo|failing|nl2br }}{% endautoescape %}',
'index.html' => '{{ foo }}',
'index.txt' => '{{ foo }}',
]), ['autoescape' => 'name']);
$env->addExtension(new class extends AbstractExtension {
public function getFilters(): array
{
return [new TwigFilter('failing', static fn ($value) => $value, ['is_safe_callback' => static function (): array {
throw new \LogicException('Unable to compute the safety of the filter.');
}])];
}
});
try {
$env->load('broken.html');
$this->fail('Compiling the template should fail.');
} catch (Error) {
}
$this->assertSame('<br>', $env->render('index.txt', ['foo' => '<br>']));
$this->assertSame('&lt;br&gt;', $env->render('index.html', ['foo' => '<br>']));
}
public function testEscapeFilterAddedByALaterVisitorStillEscapes(): void
{
$env = new Environment(new ArrayLoader(['index' => '{{ "<br>" }}']), ['autoescape' => false]);