Deduplicate template error handling

This commit is contained in:
Fabien Potencier
2026-08-03 10:55:43 +02:00
parent 7793b6153f
commit e62abbf528
2 changed files with 40 additions and 32 deletions
+21 -32
View File
@@ -410,23 +410,8 @@ abstract class Template
try {
$this->ensureSecurityChecked();
yield from $this->doDisplay($context, $blocks);
} catch (Error $e) {
if (!$e->getSourceContext()) {
$e->setSourceContext($this->getSourceContext());
}
// this is mostly useful for \Twig\Error\LoaderError exceptions
// see \Twig\Error\LoaderError
if (-1 === $e->getTemplateLine()) {
$e->guess();
}
throw $e;
} catch (\Throwable $e) {
$e = new RuntimeError(\sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getSourceContext(), $e);
$e->guess();
throw $e;
$this->handleException($e);
}
}
@@ -457,23 +442,8 @@ abstract class Template
try {
$template->ensureSecurityChecked();
yield from $template->$block($context, $blocks);
} catch (Error $e) {
if (!$e->getSourceContext()) {
$e->setSourceContext($template->getSourceContext());
}
// this is mostly useful for \Twig\Error\LoaderError exceptions
// see \Twig\Error\LoaderError
if (-1 === $e->getTemplateLine()) {
$e->guess();
}
throw $e;
} catch (\Throwable $e) {
$e = new RuntimeError(\sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getSourceContext(), $e);
$e->guess();
throw $e;
$template->handleException($e);
}
} elseif ($parent = $this->getParent($context)) {
yield from $parent->unwrap()->yieldBlock($name, $context, array_merge($this->blocks, $blocks), false, $templateContext ?? $this);
@@ -541,4 +511,23 @@ abstract class Template
* @return iterable<scalar|\Stringable|null>
*/
abstract protected function doDisplay(array $context, array $blocks = []): iterable;
private function handleException(\Throwable $error): never
{
if ($error instanceof Error) {
if (!$error->getSourceContext()) {
$error->setSourceContext($this->getSourceContext());
}
if (-1 === $error->getTemplateLine()) {
$error->guess();
}
throw $error;
}
$error = new RuntimeError(\sprintf('An exception has been thrown during the rendering of a template ("%s").', $error->getMessage()), -1, $this->getSourceContext(), $error);
$error->guess();
throw $error;
}
}
+19
View File
@@ -36,6 +36,7 @@ use Twig\Node\Node;
use Twig\Source;
use Twig\Token;
use Twig\TokenParser\AbstractTokenParser;
use Twig\TwigFunction;
class ErrorTest extends TestCase
{
@@ -47,6 +48,24 @@ class ErrorTest extends TestCase
$this->assertStringContainsString('tests'.\DIRECTORY_SEPARATOR.'ErrorTest.php', $error->getMessage());
}
public function testTwigErrorIsEnrichedWithoutBeingWrapped(): void
{
$twig = new Environment(new ArrayLoader(['index' => "foo\n{{ fail() }}"]), ['debug' => true, 'cache' => false]);
$error = null;
$twig->addFunction(new TwigFunction('fail', static function () use (&$error): never {
throw $error = new RuntimeError('Runtime error.');
}));
try {
$twig->render('index');
$this->fail();
} catch (RuntimeError $e) {
$this->assertSame($error, $e);
$this->assertSame(2, $e->getTemplateLine());
$this->assertSame('index', $e->getSourceContext()->getName());
}
}
public function testTwigExceptionGuessWithMissingVarAndArrayLoader(): void
{
$loader = new ArrayLoader([