3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
13 use Zend\EventManager\AbstractListenerAggregate
;
14 use Zend\EventManager\EventManagerInterface
;
15 use Zend\ServiceManager\Exception\InvalidServiceException
;
16 use Zend\Stdlib\ArrayUtils
;
19 * Default dispatch listener
21 * Pulls controllers from the service manager's "ControllerManager" service.
23 * If the controller cannot be found a "404" result is set up. Otherwise it
24 * will continue to try to load the controller.
26 * If the controller is not dispatchable it sets up a "404" result. In case
27 * of any other exceptions it trigger the "dispatch.error" event in an attempt
28 * to return a 500 status.
30 * If the controller subscribes to InjectApplicationEventInterface, it injects
31 * the current MvcEvent into the controller.
33 * It then calls the controller's "dispatch" method, passing it the request and
34 * response. If an exception occurs, it triggers the "dispatch.error" event,
35 * in an attempt to return a 500 status.
37 * The return value of dispatching the controller is placed into the result
38 * property of the MvcEvent, and returned.
40 class DispatchListener
extends AbstractListenerAggregate
43 * @var Controller\ControllerManager
45 private $controllerManager;
48 * @param Controller\ControllerManager $controllerManager
50 public function __construct(Controller\ControllerManager
$controllerManager)
52 $this->controllerManager
= $controllerManager;
56 * Attach listeners to an event manager
58 * @param EventManagerInterface $events
59 * @param int $priority
62 public function attach(EventManagerInterface
$events, $priority = 1)
64 $this->listeners
[] = $events->attach(MvcEvent
::EVENT_DISPATCH
, [$this, 'onDispatch']);
65 if (function_exists('zend_monitor_custom_event_ex')) {
66 $this->listeners
[] = $events->attach(MvcEvent
::EVENT_DISPATCH_ERROR
, [$this, 'reportMonitorEvent']);
71 * Listen to the "dispatch" event
76 public function onDispatch(MvcEvent
$e)
78 $routeMatch = $e->getRouteMatch();
79 $controllerName = $routeMatch instanceof Router\RouteMatch
80 ?
$routeMatch->getParam('controller', 'not-found')
82 $application = $e->getApplication();
83 $events = $application->getEventManager();
84 $controllerManager = $this->controllerManager
;
87 // Query abstract controllers, too!
88 if (! $controllerManager->has($controllerName)) {
89 $return = $this->marshalControllerNotFoundEvent($application::ERROR_CONTROLLER_NOT_FOUND
, $controllerName, $e, $application);
90 return $this->complete($return, $e);
94 $controller = $controllerManager->get($controllerName);
95 } catch (Exception\InvalidControllerException
$exception) {
96 $return = $this->marshalControllerNotFoundEvent($application::ERROR_CONTROLLER_INVALID
, $controllerName, $e, $application, $exception);
97 return $this->complete($return, $e);
98 } catch (InvalidServiceException
$exception) {
99 $return = $this->marshalControllerNotFoundEvent($application::ERROR_CONTROLLER_INVALID
, $controllerName, $e, $application, $exception);
100 return $this->complete($return, $e);
101 } catch (\Throwable
$exception) {
102 $return = $this->marshalBadControllerEvent($controllerName, $e, $application, $exception);
103 return $this->complete($return, $e);
104 } catch (\Exception
$exception) { // @TODO clean up once PHP 7 requirement is enforced
105 $return = $this->marshalBadControllerEvent($controllerName, $e, $application, $exception);
106 return $this->complete($return, $e);
109 if ($controller instanceof InjectApplicationEventInterface
) {
110 $controller->setEvent($e);
113 $request = $e->getRequest();
114 $response = $application->getResponse();
115 $caughtException = null;
118 $return = $controller->dispatch($request, $response);
119 } catch (\Throwable
$ex) {
120 $caughtException = $ex;
121 } catch (\Exception
$ex) { // @TODO clean up once PHP 7 requirement is enforced
122 $caughtException = $ex;
125 if ($caughtException !== null) {
126 $e->setName(MvcEvent
::EVENT_DISPATCH_ERROR
);
127 $e->setError($application::ERROR_EXCEPTION
);
128 $e->setController($controllerName);
129 $e->setControllerClass(get_class($controller));
130 $e->setParam('exception', $caughtException);
132 $return = $application->getEventManager()->triggerEvent($e)->last();
134 $return = $e->getResult();
138 return $this->complete($return, $e);
144 public function reportMonitorEvent(MvcEvent
$e)
146 $error = $e->getError();
147 $exception = $e->getParam('exception');
148 if ($exception instanceof \Exception ||
$exception instanceof \Throwable
) { // @TODO clean up once PHP 7 requirement is enforced
149 zend_monitor_custom_event_ex($error, $exception->getMessage(), 'Zend Framework Exception', ['code' => $exception->getCode(), 'trace' => $exception->getTraceAsString()]);
154 * Complete the dispatch
156 * @param mixed $return
157 * @param MvcEvent $event
160 protected function complete($return, MvcEvent
$event)
162 if (!is_object($return)) {
163 if (ArrayUtils
::hasStringKeys($return)) {
164 $return = new ArrayObject($return, ArrayObject
::ARRAY_AS_PROPS
);
167 $event->setResult($return);
172 * Marshal a controller not found exception event
174 * @param string $type
175 * @param string $controllerName
176 * @param MvcEvent $event
177 * @param Application $application
178 * @param \Exception $exception
181 protected function marshalControllerNotFoundEvent(
185 Application
$application,
186 \Exception
$exception = null
188 $event->setName(MvcEvent
::EVENT_DISPATCH_ERROR
);
189 $event->setError($type);
190 $event->setController($controllerName);
191 $event->setControllerClass('invalid controller class or alias: ' . $controllerName);
192 if ($exception !== null) {
193 $event->setParam('exception', $exception);
196 $events = $application->getEventManager();
197 $results = $events->triggerEvent($event);
198 $return = $results->last();
200 $return = $event->getResult();
206 * Marshal a controller not found exception event
208 * @deprecated Use marshalControllerNotFoundEvent() instead
209 * @param string $type
210 * @param string $controllerName
211 * @param MvcEvent $event
212 * @param Application $application
213 * @param \Exception $exception
216 protected function marshallControllerNotFoundEvent(
220 Application
$application,
221 \Exception
$exception = null
223 trigger_error(sprintf(
224 '%s is deprecated; please use %s::marshalControllerNotFoundEvent instead',
227 ), E_USER_DEPRECATED
);
229 return $this->marshalControllerNotFoundEvent($type, $controllerName, $event, $application, $exception);
233 * Marshal a bad controller exception event
235 * @todo Update $exception typehint to "Throwable" once PHP 7 requirement
237 * @param string $controllerName
238 * @param MvcEvent $event
239 * @param Application $application
240 * @param \Exception|\Throwable $exception
243 protected function marshalBadControllerEvent($controllerName, MvcEvent
$event, Application
$application, $exception)
245 $event->setName(MvcEvent
::EVENT_DISPATCH_ERROR
);
246 $event->setError($application::ERROR_EXCEPTION
);
247 $event->setController($controllerName);
248 $event->setParam('exception', $exception);
250 $events = $application->getEventManager();
251 $results = $events->triggerEvent($event);
252 $return = $results->last();
254 return $event->getResult();