Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1f7bc72

Browse files
committed
[HttpKernel] Add kernel.error event to handle uncaught \Error
1 parent 879c634 commit 1f7bc72

File tree

7 files changed

+134
-18
lines changed

7 files changed

+134
-18
lines changed

src/Symfony/Component/HttpKernel/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ CHANGELOG
2222
* Deprecated the `ExceptionListener::logException()` method, use `logThrowable()` instead.
2323
* The `DataCollectorInterface::collect()` method third parameter signature will
2424
be `\Throwable $exception = null` instead of `\Exception $exception = null` in Symfony 5.0.
25+
* Added the `kernel.error` event to handle uncaught `\Error`.
2526

2627
4.3.0
2728
-----
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpKernel\Event;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpKernel\HttpKernelInterface;
16+
17+
final class ErrorEvent extends RequestEvent
18+
{
19+
private $error;
20+
private $allowCustomResponseCode = false;
21+
22+
public function __construct(HttpKernelInterface $kernel, Request $request, int $requestType, \Error $error)
23+
{
24+
parent::__construct($kernel, $request, $requestType);
25+
26+
$this->error = $error;
27+
}
28+
29+
public function getError(): \Error
30+
{
31+
return $this->error;
32+
}
33+
34+
public function allowCustomResponseCode(): void
35+
{
36+
$this->allowCustomResponseCode = true;
37+
}
38+
39+
public function isAllowingCustomResponseCode(): bool
40+
{
41+
return $this->allowCustomResponseCode;
42+
}
43+
}

src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,6 @@ public function configure(Event $event = null)
112112
throw $e;
113113
}
114114

115-
if (!$e instanceof \Exception) {
116-
$e = new ErrorException($e);
117-
}
118-
119115
$hasRun = true;
120116
$kernel->terminateWithException($e, $request);
121117
};

src/Symfony/Component/HttpKernel/HttpKernel.php

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\HttpKernel;
1313

14+
use Symfony\Component\ErrorHandler\Exception\ErrorException;
1415
use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
1516
use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
1617
use Symfony\Component\HttpFoundation\Request;
@@ -21,6 +22,7 @@
2122
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
2223
use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
2324
use Symfony\Component\HttpKernel\Event\ControllerEvent;
25+
use Symfony\Component\HttpKernel\Event\ErrorEvent;
2426
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
2527
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
2628
use Symfony\Component\HttpKernel\Event\RequestEvent;
@@ -66,7 +68,7 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ
6668

6769
try {
6870
return $this->handleRaw($request, $type);
69-
} catch (\Exception $e) {
71+
} catch (\Throwable $e) {
7072
if ($e instanceof RequestExceptionInterface) {
7173
$e = new BadRequestHttpException($e->getMessage(), $e);
7274
}
@@ -198,24 +200,35 @@ private function finishRequest(Request $request, int $type)
198200

199201
/**
200202
* Handles a throwable by trying to convert it to a Response.
201-
*
202-
* @throws \Exception
203203
*/
204204
private function handleThrowable(\Throwable $e, Request $request, int $type): Response
205205
{
206-
$event = new ExceptionEvent($this, $request, $type, $e);
207-
$this->dispatcher->dispatch($event, KernelEvents::EXCEPTION);
206+
if ($e instanceof \Error) {
207+
$event = new ErrorEvent($this, $request, $type, $e);
208+
$this->dispatcher->dispatch($event, KernelEvents::ERROR);
208209

209-
// a listener might have replaced the exception
210-
$e = $event->getException();
210+
if (!$event->hasResponse()) {
211+
$e = new ErrorException($e);
212+
} else {
213+
$response = $event->getResponse();
214+
}
215+
}
211216

212-
if (!$event->hasResponse()) {
213-
$this->finishRequest($request, $type);
217+
if (!isset($response)) {
218+
$event = new ExceptionEvent($this, $request, $type, $e);
219+
$this->dispatcher->dispatch($event, KernelEvents::EXCEPTION);
214220

215-
throw $e;
216-
}
221+
// a listener might have replaced the exception
222+
$e = $event->getException();
217223

218-
$response = $event->getResponse();
224+
if (!$event->hasResponse()) {
225+
$this->finishRequest($request, $type);
226+
227+
throw $e;
228+
}
229+
230+
$response = $event->getResponse();
231+
}
219232

220233
// the developer asked for a specific status code
221234
if (!$event->isAllowingCustomResponseCode() && !$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) {

src/Symfony/Component/HttpKernel/HttpKernelInterface.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ interface HttpKernelInterface
3535
* @param bool $catch Whether to catch exceptions or not
3636
*
3737
* @return Response A Response instance
38-
*
39-
* @throws \Exception When an Exception occurs during processing
4038
*/
4139
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);
4240
}

src/Symfony/Component/HttpKernel/KernelEvents.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ final class KernelEvents
2929
*/
3030
const REQUEST = 'kernel.request';
3131

32+
/**
33+
* The ERROR event occurs when an uncaught error appears.
34+
*
35+
* This event allows you to create a response for a thrown error.
36+
*
37+
* If no response is created, the error is converted into an uncaught
38+
* exception (@see KernelEvents::EXCEPTION).
39+
*
40+
* @Event("Symfony\Component\HttpKernel\Event\ErrorEvent")
41+
*/
42+
const ERROR = 'kernel.error';
43+
3244
/**
3345
* The EXCEPTION event occurs when an uncaught exception appears.
3446
*

src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpKernel\Tests;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\ErrorHandler\Exception\ErrorException;
1516
use Symfony\Component\EventDispatcher\EventDispatcher;
1617
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1718
use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -20,6 +21,8 @@
2021
use Symfony\Component\HttpFoundation\Response;
2122
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
2223
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
24+
use Symfony\Component\HttpKernel\Event\ErrorEvent;
25+
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
2326
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
2427
use Symfony\Component\HttpKernel\Exception\ControllerDoesNotReturnResponseException;
2528
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
@@ -333,6 +336,56 @@ public function testInconsistentClientIpsOnMasterRequests()
333336
Request::setTrustedProxies([], -1);
334337
}
335338

339+
public function testHandleErrorDirectRethrow()
340+
{
341+
$this->expectException(\DivisionByZeroError::class);
342+
$kernel = $this->getHttpKernel(new EventDispatcher(), static function (): void { throw new \DivisionByZeroError(); });
343+
344+
$kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, false);
345+
}
346+
347+
public function testHandleErrorWithNoListeners()
348+
{
349+
$this->expectException(ErrorException::class);
350+
$kernel = $this->getHttpKernel(new EventDispatcher(), static function (): void { throw new \DivisionByZeroError(); });
351+
352+
$kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true);
353+
}
354+
355+
public function testHandleErrorWithAnErrorListenerThatSetsAResponse()
356+
{
357+
$dispatcher = new EventDispatcher();
358+
$dispatcher->addListener(KernelEvents::ERROR, static function (ErrorEvent $event): void {
359+
$event->setResponse(new Response('from error'));
360+
});
361+
$dispatcher->addListener(KernelEvents::EXCEPTION, function (ExceptionEvent $event): void {
362+
$this->fail('This listener should not be called');
363+
});
364+
365+
$kernel = $this->getHttpKernel($dispatcher, static function (): void { throw new \DivisionByZeroError(); });
366+
$response = $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true);
367+
368+
$this->assertSame('from error', $response->getContent());
369+
}
370+
371+
public function testHandleErrorWithAnErrorListenerThatDontSetAResponse()
372+
{
373+
$dispatcher = new EventDispatcher();
374+
$errorListenerWasCalled = false;
375+
$dispatcher->addListener(KernelEvents::ERROR, static function () use (&$errorListenerWasCalled): void {
376+
$errorListenerWasCalled = true;
377+
});
378+
$dispatcher->addListener(KernelEvents::EXCEPTION, static function (ExceptionEvent $event): void {
379+
$event->setResponse(new Response('from exception'));
380+
});
381+
382+
$kernel = $this->getHttpKernel($dispatcher, static function (): void { throw new \DivisionByZeroError(); });
383+
$response = $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true);
384+
385+
$this->assertTrue($errorListenerWasCalled);
386+
$this->assertSame('from exception', $response->getContent());
387+
}
388+
336389
private function getHttpKernel(EventDispatcherInterface $eventDispatcher, $controller = null, RequestStack $requestStack = null, array $arguments = [])
337390
{
338391
if (null === $controller) {

0 commit comments

Comments
 (0)