diff --git a/library/Zend/Mvc/View/RouteNotFoundStrategy.php b/library/Zend/Mvc/View/RouteNotFoundStrategy.php index d8c3c1e5d23..57f831e12a6 100644 --- a/library/Zend/Mvc/View/RouteNotFoundStrategy.php +++ b/library/Zend/Mvc/View/RouteNotFoundStrategy.php @@ -43,6 +43,13 @@ class RouteNotFoundStrategy implements ListenerAggregate */ protected $listeners = array(); + /** + * Whether or not to display exceptions related to the 404 condition + * + * @var bool + */ + protected $displayExceptions = false; + /** * Whether or not to display the reason for a 404 * @@ -77,6 +84,28 @@ public function attach(EventCollection $events) $this->listeners[] = $events->attach('dispatch.error', array($this, 'prepareNotFoundViewModel')); } + /** + * Set value indicating whether or not to display exceptions related to a not-found condition + * + * @param bool $displayExceptions + * @return RouteNotFoundStrategy + */ + public function setDisplayExceptions($displayExceptions) + { + $this->displayExceptions = (bool) $displayExceptions; + return $this; + } + + /** + * Should we display exceptions related to a not-found condition? + * + * @return bool + */ + public function displayExceptions() + { + return $this->displayExceptions; + } + /** * Detach aggregate listeners from the specified event manager * @@ -194,11 +223,28 @@ public function prepareNotFoundViewModel(MvcEvent $e) $model->setTemplate($this->getNotFoundTemplate()); // If displaying reasons, inject the reason - $this->injectNotFoundReason($model); + $this->injectNotFoundReason($model, $e); + + // If displaying exceptions, inject + $this->injectException($model, $e); + + // Inject controller if we're displaying either the reason or the exception + $this->injectController($model, $e); $e->setResult($model); } + /** + * Inject the not-found reason into the model + * + * If $displayNotFoundReason is enabled, checks to see if $reason is set, + * and, if so, injects it into the model. If not, it injects + * Application::ERROR_CONTROLLER_CANNOT_DISPATCH. + * + * @param ViewModel $model + * @param MvcEvent $e + * @return void + */ protected function injectNotFoundReason($model) { if (!$this->displayNotFoundReason()) { @@ -215,4 +261,65 @@ protected function injectNotFoundReason($model) // dispatch itself. $model->setVariable('reason', Application::ERROR_CONTROLLER_CANNOT_DISPATCH); } + + /** + * Inject the exception message into the model + * + * If $displayExceptions is enabled, and an exception is found in the + * event, inject it into the model. + * + * @param ViewModel $model + * @param MvcEvent $e + * @return void + */ + protected function injectException($model, $e) + { + if (!$this->displayExceptions()) { + return; + } + + $exception = $e->getParam('exception', false); + if (!$exception instanceof \Exception) { + return; + } + + $model->setVariable('exception', $exception); + } + + /** + * Inject the controller and controller class into the model + * + * If either $displayExceptions or $displayNotFoundReason are enabled, + * injects the controllerClass from the MvcEvent. It checks to see if a + * controller is present in the MvcEvent, and, if not, grabs it from + * the route match if present; if a controller is found, it injects it into + * the model. + * + * @param ViewModel $model + * @param MvcEvent $e + * @return void + */ + protected function injectController($model, $e) + { + if (!$this->displayExceptions() && !$this->displayNotFoundReason()) { + return; + } + + $controller = $e->getController(); + if (empty($controller)) { + $routeMatch = $e->getRouteMatch(); + if (empty($routeMatch)) { + return; + } + + $controller = $routeMatch->getParam('controller', false); + if (!$controller) { + return; + } + } + + $controllerClass = $e->getControllerClass(); + $model->setVariable('controller', $controller); + $model->setVariable('controller_class', $controllerClass); + } } diff --git a/tests/Zend/Mvc/View/RouteNotFoundStrategyTest.php b/tests/Zend/Mvc/View/RouteNotFoundStrategyTest.php index 18b984377a9..7e144b90828 100644 --- a/tests/Zend/Mvc/View/RouteNotFoundStrategyTest.php +++ b/tests/Zend/Mvc/View/RouteNotFoundStrategyTest.php @@ -178,6 +178,61 @@ public function test404ResponsePrepares404ViewModelWithReasonWhenAllowed() } } + public function test404ResponsePrepares404ViewModelWithExceptionWhenAllowed() + { + $response = new Response(); + $event = new MvcEvent(); + $exception = new \Exception(); + $event->setParam('exception', $exception); + + foreach (array(true, false) as $allow) { + $this->strategy->setDisplayExceptions($allow); + $response->setStatusCode(404); + $event->setResponse($response); + $this->strategy->prepareNotFoundViewModel($event); + $model = $event->getResult(); + $this->assertInstanceOf('Zend\View\Model', $model); + $variables = $model->getVariables(); + if ($allow) { + $this->assertTrue(isset($variables['exception'])); + $this->assertSame($exception, $variables['exception']); + } else { + $this->assertFalse(isset($variables['exception'])); + } + } + } + + public function test404ResponsePrepares404ViewModelWithControllerWhenAllowed() + { + $response = new Response(); + $event = new MvcEvent(); + $controller = 'some-or-other'; + $controllerClass = 'Some\Controller\OrOtherController'; + $event->setController($controller); + $event->setControllerClass($controllerClass); + + foreach (array('setDisplayNotFoundReason', 'setDisplayExceptions') as $method) { + foreach (array(true, false) as $allow) { + $this->strategy->$method($allow); + $response->setStatusCode(404); + $event->setResponse($response); + $this->strategy->prepareNotFoundViewModel($event); + $model = $event->getResult(); + $this->assertInstanceOf('Zend\View\Model', $model); + $variables = $model->getVariables(); + if ($allow) { + $this->assertTrue(isset($variables['controller'])); + $this->assertEquals($controller, $variables['controller']); + $this->assertTrue(isset($variables['controller_class'])); + $this->assertEquals($controllerClass, $variables['controller_class']); + } else { + $this->assertFalse(isset($variables['controller'])); + $this->assertFalse(isset($variables['controller_class'])); + } + } + } + } + public function testInjectsHttpResponseIntoEventIfNoneAlreadyPresent() { $event = new MvcEvent();