diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml index 0622c4196c..b97125375a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml @@ -67,6 +67,16 @@ + + + + null + null + %kernel.debug% + %kernel.charset% + %debug.file_link_format% + + diff --git a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php index 0895366aa8..4fc7e32fed 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ExceptionListener.php @@ -13,8 +13,10 @@ namespace Symfony\Component\HttpKernel\EventListener; use Psr\Log\LoggerInterface; use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\Debug\ExceptionHandler; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; @@ -33,12 +35,17 @@ class ExceptionListener implements EventSubscriberInterface protected $controller; protected $logger; protected $debug; + private $charset; + private $fileLinkFormat; + private $isTerminating = false; - public function __construct($controller, LoggerInterface $logger = null, $debug = false) + public function __construct($controller, LoggerInterface $logger = null, $debug = false, string $charset = null, $fileLinkFormat = null) { $this->controller = $controller; $this->logger = $logger; $this->debug = $debug; + $this->charset = $charset; + $this->fileLinkFormat = $fileLinkFormat; } public function logKernelException(GetResponseForExceptionEvent $event) @@ -50,6 +57,17 @@ class ExceptionListener implements EventSubscriberInterface public function onKernelException(GetResponseForExceptionEvent $event) { + if (null === $this->controller) { + if (!$event->isMasterRequest()) { + return; + } + if (!$this->isTerminating) { + $this->isTerminating = true; + + return; + } + $this->isTerminating = false; + } $exception = $event->getException(); $request = $this->duplicateRequest($exception, $event->getRequest()); $eventDispatcher = func_num_args() > 2 ? func_get_arg(2) : null; @@ -85,6 +103,11 @@ class ExceptionListener implements EventSubscriberInterface } } + public function reset() + { + $this->isTerminating = false; + } + public static function getSubscribedEvents() { return array( @@ -123,8 +146,12 @@ class ExceptionListener implements EventSubscriberInterface protected function duplicateRequest(\Exception $exception, Request $request) { $attributes = array( - '_controller' => $this->controller, - 'exception' => FlattenException::create($exception), + 'exception' => $exception = FlattenException::create($exception), + '_controller' => $this->controller ?: function () use ($exception) { + $handler = new ExceptionHandler($this->debug, $this->charset, $this->fileLinkFormat); + + return new Response($handler->getHtml($exception), $exception->getStatusCode(), $exception->getHeaders()); + }, 'logger' => $this->logger instanceof DebugLoggerInterface ? $this->logger : null, ); $request = $request->duplicate(null, null, $attributes); diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php index 330dbe9769..32146f00c1 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/ExceptionListenerTest.php @@ -155,6 +155,25 @@ class ExceptionListenerTest extends TestCase $this->assertFalse($response->headers->has('content-security-policy'), 'CSP header has been removed'); $this->assertFalse($dispatcher->hasListeners(KernelEvents::RESPONSE), 'CSP removal listener has been removed'); } + + public function testNullController() + { + $listener = new ExceptionListener(null); + $kernel = $this->getMockBuilder(HttpKernelInterface::class)->getMock(); + $kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { + $controller = $request->attributes->get('_controller'); + + return $controller(); + })); + $request = Request::create('/'); + $event = new GetResponseForExceptionEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, new \Exception('foo')); + + $listener->onKernelException($event); + $this->assertNull($event->getResponse()); + + $listener->onKernelException($event); + $this->assertContains('Whoops, looks like something went wrong.', $event->getResponse()->getContent()); + } } class TestLogger extends Logger implements DebugLoggerInterface