diff --git a/src/DispatchListener.php b/src/DispatchListener.php index ca97412c3..d091351f3 100644 --- a/src/DispatchListener.php +++ b/src/DispatchListener.php @@ -98,7 +98,10 @@ public function onDispatch(MvcEvent $e) } catch (InvalidServiceException $exception) { $return = $this->marshalControllerNotFoundEvent($application::ERROR_CONTROLLER_INVALID, $controllerName, $e, $application, $exception); return $this->complete($return, $e); - } catch (\Exception $exception) { + } catch (\Throwable $exception) { + $return = $this->marshalBadControllerEvent($controllerName, $e, $application, $exception); + return $this->complete($return, $e); + } catch (\Exception $exception) { // @TODO clean up once PHP 7 requirement is enforced $return = $this->marshalBadControllerEvent($controllerName, $e, $application, $exception); return $this->complete($return, $e); } @@ -109,15 +112,22 @@ public function onDispatch(MvcEvent $e) $request = $e->getRequest(); $response = $application->getResponse(); + $caughtException = null; try { $return = $controller->dispatch($request, $response); - } catch (\Exception $ex) { + } catch (\Throwable $ex) { + $caughtException = $ex; + } catch (\Exception $ex) { // @TODO clean up once PHP 7 requirement is enforced + $caughtException = $ex; + } + + if ($caughtException !== null) { $e->setName(MvcEvent::EVENT_DISPATCH_ERROR); $e->setError($application::ERROR_EXCEPTION); $e->setController($controllerName); $e->setControllerClass(get_class($controller)); - $e->setParam('exception', $ex); + $e->setParam('exception', $caughtException); $return = $application->getEventManager()->triggerEvent($e)->last(); if (! $return) { @@ -135,7 +145,7 @@ public function reportMonitorEvent(MvcEvent $e) { $error = $e->getError(); $exception = $e->getParam('exception'); - if ($exception instanceof \Exception) { + if ($exception instanceof \Exception || $exception instanceof \Throwable) { // @TODO clean up once PHP 7 requirement is enforced zend_monitor_custom_event_ex($error, $exception->getMessage(), 'Zend Framework Exception', ['code' => $exception->getCode(), 'trace' => $exception->getTraceAsString()]); } } diff --git a/src/MiddlewareListener.php b/src/MiddlewareListener.php index 19004b380..4c19672ea 100644 --- a/src/MiddlewareListener.php +++ b/src/MiddlewareListener.php @@ -56,9 +56,17 @@ public function onDispatch(MvcEvent $event) $event->setResult($return); return $return; } + + $caughtException = null; try { $return = $middleware(Psr7Request::fromZend($request), Psr7Response::fromZend($response)); - } catch (\Exception $exception) { + } catch (\Throwable $exception) { + $caughtException = $exception; + } catch (\Exception $exception) { // @TODO clean up once PHP 7 requirement is enforced + $caughtException = $exception; + } + + if ($caughtException !== null) { $event->setName(MvcEvent::EVENT_DISPATCH_ERROR); $event->setError($application::ERROR_EXCEPTION); $event->setController($middlewareName); diff --git a/src/View/Console/ExceptionStrategy.php b/src/View/Console/ExceptionStrategy.php index 11fb483d5..7aa7a512b 100644 --- a/src/View/Console/ExceptionStrategy.php +++ b/src/View/Console/ExceptionStrategy.php @@ -184,7 +184,7 @@ public function prepareExceptionViewModel(MvcEvent $e) if (is_callable($this->message)) { $callback = $this->message; $message = (string) $callback($exception, $this->displayExceptions); - } elseif ($this->displayExceptions && $exception instanceof \Exception) { + } elseif ($this->displayExceptions && ($exception instanceof \Exception || $exception instanceof \Throwable)) { // @TODO clean up once PHP 7 requirement is enforced $previous = ''; $previousException = $exception->getPrevious(); while ($previousException) { diff --git a/src/View/Console/RouteNotFoundStrategy.php b/src/View/Console/RouteNotFoundStrategy.php index c3d74eb71..a61833586 100644 --- a/src/View/Console/RouteNotFoundStrategy.php +++ b/src/View/Console/RouteNotFoundStrategy.php @@ -460,7 +460,7 @@ protected function reportNotFoundReason($e) ]; $report = sprintf("\nReason for failure: %s\n", $reasons[$reason]); - while ($exception instanceof \Exception) { + while ($exception instanceof \Exception || $exception instanceof \Throwable) { // @TODO clean up once PHP 7 requirement is enforced $report .= sprintf("Exception: %s\nTrace:\n%s\n", $exception->getMessage(), $exception->getTraceAsString()); $exception = $exception->getPrevious(); } diff --git a/src/View/Http/DefaultRenderingStrategy.php b/src/View/Http/DefaultRenderingStrategy.php index a5d02bb67..71bb74a6a 100644 --- a/src/View/Http/DefaultRenderingStrategy.php +++ b/src/View/Http/DefaultRenderingStrategy.php @@ -99,9 +99,17 @@ public function render(MvcEvent $e) $view->setRequest($request); $view->setResponse($response); + $caughtException = null; + try { $view->render($viewModel); - } catch (\Exception $ex) { + } catch (\Throwable $ex) { + $caughtException = $ex; + } catch (\Exception $ex) { // @TODO clean up once PHP 7 requirement is enforced + $caughtException = $ex; + } + + if ($caughtException !== null) { if ($e->getName() === MvcEvent::EVENT_RENDER_ERROR) { throw $ex; } diff --git a/src/View/Http/RouteNotFoundStrategy.php b/src/View/Http/RouteNotFoundStrategy.php index b43fe0072..3a932185b 100644 --- a/src/View/Http/RouteNotFoundStrategy.php +++ b/src/View/Http/RouteNotFoundStrategy.php @@ -250,7 +250,7 @@ protected function injectException($model, $e) $model->setVariable('display_exceptions', true); $exception = $e->getParam('exception', false); - if (!$exception instanceof \Exception) { + if (!$exception instanceof \Exception && !$exception instanceof \Throwable) { // @TODO clean up once PHP 7 requirement is enforced return; } diff --git a/test/ApplicationTest.php b/test/ApplicationTest.php index 49f70a258..00acff441 100644 --- a/test/ApplicationTest.php +++ b/test/ApplicationTest.php @@ -291,7 +291,7 @@ public function setupActionController() return $this->application; } - public function setupBadController($addService = true) + public function setupBadController($addService = true, $action = 'test') { $request = $this->serviceManager->get('Request'); $request->setUri('http://example.local/bad'); @@ -301,7 +301,7 @@ public function setupBadController($addService = true) 'route' => '/bad', 'defaults' => [ 'controller' => 'bad', - 'action' => 'test', + 'action' => $action, ], ]); $router->addRoute('bad', $route); @@ -374,6 +374,25 @@ public function testLocatorExceptionShouldTriggerDispatchError() $this->assertSame($response, $result->getResponse(), get_class($result)); } + /** + * @requires PHP 7.0 + * @group error-handling + */ + public function testPhp7ErrorRaisedInDispatchableShouldRaiseDispatchErrorEvent() + { + $this->setupBadController(true, 'test-php7-error'); + $response = $this->application->getResponse(); + $events = $this->application->getEventManager(); + $events->attach(MvcEvent::EVENT_DISPATCH_ERROR, function ($e) use ($response) { + $exception = $e->getParam('exception'); + $response->setContent($exception->getMessage()); + return $response; + }); + + $this->application->run(); + $this->assertContains('Raised an error', $response->getContent()); + } + /** * @group error-handling */ diff --git a/test/Controller/TestAsset/BadController.php b/test/Controller/TestAsset/BadController.php index 7369c0f90..35034dc42 100644 --- a/test/Controller/TestAsset/BadController.php +++ b/test/Controller/TestAsset/BadController.php @@ -17,4 +17,9 @@ public function testAction() { throw new \Exception('Raised an exception'); } + + public function testPhp7ErrorAction() + { + throw new \Error('Raised an error'); + } }