Discard what the non-selected branch of a guard tag registers on the parser

This commit is contained in:
Fabien Potencier
2026-09-26 09:07:40 +02:00
parent 14a0e4adb3
commit d5011f4654
5 changed files with 35 additions and 1 deletions
+1
View File
@@ -2,6 +2,7 @@
* 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
* Fix the non-selected branch of a `guard` tag registering its parent, blocks, and macros
* Speed up reading object attributes backed by getters or class constants
* Fix the sandbox not reporting the line of a rejected `guard` tag
+5
View File
@@ -159,14 +159,19 @@ class Parser
return $this->ignoreUnknownTwigCallables;
}
/**
* Parses a discarded part of the template: what it registers (parent, blocks, macros, ...) is discarded as well.
*/
public function subparseIgnoreUnknownTwigCallables($test, bool $dropNeedle = false): void
{
$previous = $this->ignoreUnknownTwigCallables;
$state = [$this->parent, $this->blocks, $this->documentationTargets, $this->macros, $this->importedSymbols, $this->traits, $this->embeddedTemplates];
$this->ignoreUnknownTwigCallables = true;
try {
$this->subparse($test, $dropNeedle);
} finally {
$this->ignoreUnknownTwigCallables = $previous;
[$this->parent, $this->blocks, $this->documentationTargets, $this->macros, $this->importedSymbols, $this->traits, $this->embeddedTemplates] = $state;
}
}
+5 -1
View File
@@ -55,7 +55,11 @@ final class GuardTokenParser extends AbstractTokenParser
$else = new EmptyNode();
if ('else' === $stream->next()->getValue()) {
$stream->expect(Token::BLOCK_END_TYPE);
$else = $this->parser->subparse([$this, 'decideGuardEnd'], true);
if ($exists) {
$this->parser->subparseIgnoreUnknownTwigCallables([$this, 'decideGuardEnd'], true);
} else {
$else = $this->parser->subparse([$this, 'decideGuardEnd'], true);
}
}
$stream->expect(Token::BLOCK_END_TYPE);
@@ -0,0 +1,9 @@
--TEST--
"guard" ignores the blocks and macros defined in the discarded branch
--TEMPLATE--
{% guard function constant %}{% else %}{% block discarded %}{% endblock %}{% macro discarded() %}{% endmacro %}{% endguard %}
{{ block('discarded') is defined ? 'NEVER' : 'no block' }} {{ _self.discarded is defined ? 'NEVER' : 'no macro' }}
--DATA--
return []
--EXPECT--
no block no macro
@@ -0,0 +1,15 @@
--TEST--
"guard" ignores the parent defined in the discarded branch
--DEPRECATION--
Since twig/twig 3.28: Using the "extends" tag outside the root of a template is deprecated in index.twig at line 2.
--TEMPLATE--
{% guard function foobar %}{% extends "never.twig" %}{% else %}{% extends "layout.twig" %}{% endguard %}
{% block content %}content{% endblock %}
--TEMPLATE(layout.twig)--
layout {{ block('content') }}
--TEMPLATE(never.twig)--
NEVER
--DATA--
return []
--EXPECT--
layout content