From 68673a3f9878cc5e38c374e8c23b28f67906247d Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 18 Oct 2023 10:16:04 +0200 Subject: [PATCH] [HttpKernel] Introduce `ExceptionEvent::isKernelTerminating()` to skip error rendering when kernel is terminating --- UPGRADE-7.1.md | 7 +++++++ .../Component/HttpKernel/Event/ExceptionEvent.php | 9 ++++++++- .../HttpKernel/EventListener/ErrorListener.php | 4 ++++ src/Symfony/Component/HttpKernel/HttpKernel.php | 10 ++++++++-- .../Tests/EventListener/ErrorListenerTest.php | 13 +++++++++++++ 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 UPGRADE-7.1.md diff --git a/UPGRADE-7.1.md b/UPGRADE-7.1.md new file mode 100644 index 0000000000000..f5bdda37813e4 --- /dev/null +++ b/UPGRADE-7.1.md @@ -0,0 +1,7 @@ +UPGRADE FROM 7.0 to 7.1 +======================= + +HttpKernel +---------- + + * `ExceptionEvent` now takes an optional `$isKernelTerminating` parameter diff --git a/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php b/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php index 8bc25f9c37b81..84210b45c92c1 100644 --- a/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/ExceptionEvent.php @@ -31,12 +31,14 @@ final class ExceptionEvent extends RequestEvent { private \Throwable $throwable; private bool $allowCustomResponseCode = false; + private bool $isKernelTerminating = false; - public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, \Throwable $e) + public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, \Throwable $e, bool $isKernelTerminating = false) { parent::__construct($kernel, $request, $requestType); $this->setThrowable($e); + $this->isKernelTerminating = $isKernelTerminating; } public function getThrowable(): \Throwable @@ -69,4 +71,9 @@ public function isAllowingCustomResponseCode(): bool { return $this->allowCustomResponseCode; } + + public function isKernelTerminating(): bool + { + return $this->isKernelTerminating; + } } diff --git a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php index a2f6db57a6e7f..4c4aafb9e36eb 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php @@ -102,6 +102,10 @@ public function onKernelException(ExceptionEvent $event) return; } + if (!$this->debug && $event->isKernelTerminating()) { + return; + } + $throwable = $event->getThrowable(); if ($exceptionHandler = set_exception_handler(var_dump(...))) { diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index d2cf4eaee27ce..693d6933330f4 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -56,6 +56,7 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface protected $requestStack; private ArgumentResolverInterface $argumentResolver; private bool $handleAllThrowables; + private bool $terminating = false; public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null, ArgumentResolverInterface $argumentResolver = null, bool $handleAllThrowables = false) { @@ -112,7 +113,12 @@ public function handle(Request $request, int $type = HttpKernelInterface::MAIN_R */ public function terminate(Request $request, Response $response) { - $this->dispatcher->dispatch(new TerminateEvent($this, $request, $response), KernelEvents::TERMINATE); + try { + $this->terminating = true; + $this->dispatcher->dispatch(new TerminateEvent($this, $request, $response), KernelEvents::TERMINATE); + } finally { + $this->terminating = false; + } } /** @@ -235,7 +241,7 @@ private function finishRequest(Request $request, int $type): void */ private function handleThrowable(\Throwable $e, Request $request, int $type): Response { - $event = new ExceptionEvent($this, $request, $type, $e); + $event = new ExceptionEvent($this, $request, $type, $e, isKernelTerminating: $this->terminating); $this->dispatcher->dispatch($event, KernelEvents::EXCEPTION); // a listener might have replaced the exception diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php index be98f1ceacb93..214178984c2ff 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php @@ -244,6 +244,19 @@ public function testCSPHeaderIsRemoved() $this->assertFalse($response->headers->has('content-security-policy'), 'CSP header has been removed'); } + public function testTerminating() + { + $listener = new ErrorListener('foo', $this->createMock(LoggerInterface::class)); + + $kernel = $this->createMock(HttpKernelInterface::class); + $kernel->expects($this->never())->method('handle'); + + $request = Request::create('/'); + + $event = new ExceptionEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, new \Exception('foo'), true); + $listener->onKernelException($event); + } + /** * @dataProvider controllerProvider */