diff --git a/src/Template.php b/src/Template.php index 530532a5d..aeca14677 100644 --- a/src/Template.php +++ b/src/Template.php @@ -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 */ 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; + } } diff --git a/tests/ErrorTest.php b/tests/ErrorTest.php index 5ec565a7f..4f409b18f 100644 --- a/tests/ErrorTest.php +++ b/tests/ErrorTest.php @@ -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([