mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-15 03:46:39 +00:00
bug #4924 Resolve constant parent templates once instead of on every lookup (fabpot)
This PR was merged into the 3.x branch.
Discussion
----------
Resolve constant parent templates once instead of on every lookup
Currently, `Template::$parent` is only memoized by the compiled `doDisplay()`. Anything that reaches a template through `getParent()` without rendering it (`TemplateWrapper::hasBlock()`, `getBlockNames()`, `renderBlock()`, `yieldParentBlock()`, `MacroNamespace::getParent()`) re-evaluates `doGetParent()` and re-runs the sandbox check on every call, even when the parent is a string literal.
`doGetParent()` now does `$this->parent ??= $this->load("name", $line)` for a constant parent, which is what `doDisplay()` already does one line later. Dynamic parents are untouched and still resolve per context.
This also fixes a bug: `getParent()` loaded a constant parent with line `-1`, so a missing parent reported through `hasBlock()`/`getBlockNames()` lost its line number, while `render()` reported the `{% extends %}` line. All three now agree.
Commits
-------
c53b6468c9 Resolve constant parent templates once instead of on every lookup
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
* Fix `TemplateWrapper::streamBlock()`, `TemplateWrapper::hasBlock()`, and `TemplateWrapper::getBlockNames()` omitting environment globals
|
||||
* Fix exceptions from dynamic parent expressions escaping without template context
|
||||
* Add the `BlockChain` class to compose blocks from multiple templates without using template internals
|
||||
* Fix `TemplateWrapper::hasBlock()` and `TemplateWrapper::getBlockNames()` losing the `extends` line when the parent template does not exist
|
||||
* Fix an output buffer leak when a parent block rendered in an expression throws in non-yield mode
|
||||
* Add the `HtmlExtension::htmlAttrValue()` method to resolve a single HTML attribute value the way the `html_attr` function renders it
|
||||
* Fix `html_attr` JSON encoding a `Stringable` value in a `data-*` attribute instead of using its string representation
|
||||
|
||||
+10
-9
@@ -143,17 +143,18 @@ final class ModuleNode extends Node implements CoercesChildrenToStringInterface
|
||||
;
|
||||
|
||||
if ($parent instanceof ConstantExpression) {
|
||||
$compiler->subcompile($parent);
|
||||
} else {
|
||||
$compiler
|
||||
->raw('$this->load(')
|
||||
->subcompile($parent)
|
||||
->raw(', ')
|
||||
->repr($parent->getTemplateLine())
|
||||
->raw(')')
|
||||
;
|
||||
// a constant parent never depends on the context, so resolve it once
|
||||
$compiler->raw('$this->parent ??= ');
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('$this->load(')
|
||||
->subcompile($parent)
|
||||
->raw(', ')
|
||||
->repr($parent->getTemplateLine())
|
||||
->raw(')')
|
||||
;
|
||||
|
||||
$compiler
|
||||
->raw(";\n")
|
||||
->outdent()
|
||||
|
||||
@@ -232,7 +232,7 @@ class __TwigTemplate_%x extends Template
|
||||
protected function doGetParent(array \$context): bool|string|Template|TemplateWrapper
|
||||
{
|
||||
// line 1
|
||||
return "layout.twig";
|
||||
return \$this->parent ??= \$this->load("layout.twig", 1);
|
||||
}
|
||||
|
||||
protected function doDisplay(array \$context, array \$blocks = []): iterable
|
||||
|
||||
@@ -139,6 +139,19 @@ class TemplateWrapperTest extends TestCase
|
||||
yield 'exception thrown by parent expression' => ['{% extends boom() %}', new TwigFunction('boom', static function (): never { throw new \DomainException('kaboom'); }), \DomainException::class];
|
||||
}
|
||||
|
||||
public function testBlockIntrospectionReportsTheExtendsLineForAMissingParent(): void
|
||||
{
|
||||
$twig = new Environment(new ArrayLoader(['index' => "\n\n{% extends 'missing' %}"]));
|
||||
|
||||
try {
|
||||
$twig->load('index')->hasBlock('foo');
|
||||
$this->fail('Introspecting a template with a missing parent must fail.');
|
||||
} catch (LoaderError $e) {
|
||||
$this->assertSame('Template "missing" is not defined.', $e->getRawMessage());
|
||||
$this->assertSame(3, $e->getTemplateLine());
|
||||
}
|
||||
}
|
||||
|
||||
public function testRenderBlock(): void
|
||||
{
|
||||
$twig = new Environment(new ArrayLoader([
|
||||
|
||||
Reference in New Issue
Block a user