1<?php 2/** 3 * Zend Framework (http://framework.zend.com/) 4 * 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 8 */ 9 10namespace Zend\Mvc; 11 12use Zend\EventManager\EventManagerAwareInterface; 13use Zend\EventManager\EventManagerInterface; 14use Zend\ServiceManager\ServiceManager; 15use Zend\Stdlib\ResponseInterface; 16 17/** 18 * Main application class for invoking applications 19 * 20 * Expects the user will provide a configured ServiceManager, configured with 21 * the following services: 22 * 23 * - EventManager 24 * - ModuleManager 25 * - Request 26 * - Response 27 * - RouteListener 28 * - Router 29 * - DispatchListener 30 * - ViewManager 31 * 32 * The most common workflow is: 33 * <code> 34 * $services = new Zend\ServiceManager\ServiceManager($servicesConfig); 35 * $app = new Application($appConfig, $services); 36 * $app->bootstrap(); 37 * $response = $app->run(); 38 * $response->send(); 39 * </code> 40 * 41 * bootstrap() opts in to the default route, dispatch, and view listeners, 42 * sets up the MvcEvent, and triggers the bootstrap event. This can be omitted 43 * if you wish to setup your own listeners and/or workflow; alternately, you 44 * can simply extend the class to override such behavior. 45 */ 46class Application implements 47 ApplicationInterface, 48 EventManagerAwareInterface 49{ 50 const ERROR_CONTROLLER_CANNOT_DISPATCH = 'error-controller-cannot-dispatch'; 51 const ERROR_CONTROLLER_NOT_FOUND = 'error-controller-not-found'; 52 const ERROR_CONTROLLER_INVALID = 'error-controller-invalid'; 53 const ERROR_EXCEPTION = 'error-exception'; 54 const ERROR_ROUTER_NO_MATCH = 'error-router-no-match'; 55 56 /** 57 * @var array 58 */ 59 protected $configuration = null; 60 61 /** 62 * Default application event listeners 63 * 64 * @var array 65 */ 66 protected $defaultListeners = array( 67 'RouteListener', 68 'DispatchListener', 69 'HttpMethodListener', 70 'ViewManager', 71 'SendResponseListener', 72 ); 73 74 /** 75 * MVC event token 76 * @var MvcEvent 77 */ 78 protected $event; 79 80 /** 81 * @var EventManagerInterface 82 */ 83 protected $events; 84 85 /** 86 * @var \Zend\Stdlib\RequestInterface 87 */ 88 protected $request; 89 90 /** 91 * @var ResponseInterface 92 */ 93 protected $response; 94 95 /** 96 * @var ServiceManager 97 */ 98 protected $serviceManager = null; 99 100 /** 101 * Constructor 102 * 103 * @param mixed $configuration 104 * @param ServiceManager $serviceManager 105 */ 106 public function __construct($configuration, ServiceManager $serviceManager) 107 { 108 $this->configuration = $configuration; 109 $this->serviceManager = $serviceManager; 110 111 $this->setEventManager($serviceManager->get('EventManager')); 112 113 $this->request = $serviceManager->get('Request'); 114 $this->response = $serviceManager->get('Response'); 115 } 116 117 /** 118 * Retrieve the application configuration 119 * 120 * @return array|object 121 */ 122 public function getConfig() 123 { 124 return $this->serviceManager->get('Config'); 125 } 126 127 /** 128 * Bootstrap the application 129 * 130 * Defines and binds the MvcEvent, and passes it the request, response, and 131 * router. Attaches the ViewManager as a listener. Triggers the bootstrap 132 * event. 133 * 134 * @param array $listeners List of listeners to attach. 135 * @return Application 136 */ 137 public function bootstrap(array $listeners = array()) 138 { 139 $serviceManager = $this->serviceManager; 140 $events = $this->events; 141 142 $listeners = array_unique(array_merge($this->defaultListeners, $listeners)); 143 144 foreach ($listeners as $listener) { 145 $events->attach($serviceManager->get($listener)); 146 } 147 148 // Setup MVC Event 149 $this->event = $event = new MvcEvent(); 150 $event->setTarget($this); 151 $event->setApplication($this) 152 ->setRequest($this->request) 153 ->setResponse($this->response) 154 ->setRouter($serviceManager->get('Router')); 155 156 // Trigger bootstrap events 157 $events->trigger(MvcEvent::EVENT_BOOTSTRAP, $event); 158 return $this; 159 } 160 161 /** 162 * Retrieve the service manager 163 * 164 * @return ServiceManager 165 */ 166 public function getServiceManager() 167 { 168 return $this->serviceManager; 169 } 170 171 /** 172 * Get the request object 173 * 174 * @return \Zend\Stdlib\RequestInterface 175 */ 176 public function getRequest() 177 { 178 return $this->request; 179 } 180 181 /** 182 * Get the response object 183 * 184 * @return ResponseInterface 185 */ 186 public function getResponse() 187 { 188 return $this->response; 189 } 190 191 /** 192 * Get the MVC event instance 193 * 194 * @return MvcEvent 195 */ 196 public function getMvcEvent() 197 { 198 return $this->event; 199 } 200 201 /** 202 * Set the event manager instance 203 * 204 * @param EventManagerInterface $eventManager 205 * @return Application 206 */ 207 public function setEventManager(EventManagerInterface $eventManager) 208 { 209 $eventManager->setIdentifiers(array( 210 __CLASS__, 211 get_class($this), 212 )); 213 $this->events = $eventManager; 214 return $this; 215 } 216 217 /** 218 * Retrieve the event manager 219 * 220 * Lazy-loads an EventManager instance if none registered. 221 * 222 * @return EventManagerInterface 223 */ 224 public function getEventManager() 225 { 226 return $this->events; 227 } 228 229 /** 230 * Static method for quick and easy initialization of the Application. 231 * 232 * If you use this init() method, you cannot specify a service with the 233 * name of 'ApplicationConfig' in your service manager config. This name is 234 * reserved to hold the array from application.config.php. 235 * 236 * The following services can only be overridden from application.config.php: 237 * 238 * - ModuleManager 239 * - SharedEventManager 240 * - EventManager & Zend\EventManager\EventManagerInterface 241 * 242 * All other services are configured after module loading, thus can be 243 * overridden by modules. 244 * 245 * @param array $configuration 246 * @return Application 247 */ 248 public static function init($configuration = array()) 249 { 250 $smConfig = isset($configuration['service_manager']) ? $configuration['service_manager'] : array(); 251 $serviceManager = new ServiceManager(new Service\ServiceManagerConfig($smConfig)); 252 $serviceManager->setService('ApplicationConfig', $configuration); 253 $serviceManager->get('ModuleManager')->loadModules(); 254 255 $listenersFromAppConfig = isset($configuration['listeners']) ? $configuration['listeners'] : array(); 256 $config = $serviceManager->get('Config'); 257 $listenersFromConfigService = isset($config['listeners']) ? $config['listeners'] : array(); 258 259 $listeners = array_unique(array_merge($listenersFromConfigService, $listenersFromAppConfig)); 260 261 return $serviceManager->get('Application')->bootstrap($listeners); 262 } 263 264 /** 265 * Run the application 266 * 267 * @triggers route(MvcEvent) 268 * Routes the request, and sets the RouteMatch object in the event. 269 * @triggers dispatch(MvcEvent) 270 * Dispatches a request, using the discovered RouteMatch and 271 * provided request. 272 * @triggers dispatch.error(MvcEvent) 273 * On errors (controller not found, action not supported, etc.), 274 * populates the event with information about the error type, 275 * discovered controller, and controller class (if known). 276 * Typically, a handler should return a populated Response object 277 * that can be returned immediately. 278 * @return self 279 */ 280 public function run() 281 { 282 $events = $this->events; 283 $event = $this->event; 284 285 // Define callback used to determine whether or not to short-circuit 286 $shortCircuit = function ($r) use ($event) { 287 if ($r instanceof ResponseInterface) { 288 return true; 289 } 290 if ($event->getError()) { 291 return true; 292 } 293 return false; 294 }; 295 296 // Trigger route event 297 $result = $events->trigger(MvcEvent::EVENT_ROUTE, $event, $shortCircuit); 298 if ($result->stopped()) { 299 $response = $result->last(); 300 if ($response instanceof ResponseInterface) { 301 $event->setTarget($this); 302 $event->setResponse($response); 303 $events->trigger(MvcEvent::EVENT_FINISH, $event); 304 $this->response = $response; 305 return $this; 306 } 307 } 308 309 if ($event->getError()) { 310 return $this->completeRequest($event); 311 } 312 313 // Trigger dispatch event 314 $result = $events->trigger(MvcEvent::EVENT_DISPATCH, $event, $shortCircuit); 315 316 // Complete response 317 $response = $result->last(); 318 if ($response instanceof ResponseInterface) { 319 $event->setTarget($this); 320 $event->setResponse($response); 321 $events->trigger(MvcEvent::EVENT_FINISH, $event); 322 $this->response = $response; 323 return $this; 324 } 325 326 $response = $this->response; 327 $event->setResponse($response); 328 $this->completeRequest($event); 329 330 return $this; 331 } 332 333 /** 334 * @deprecated 335 */ 336 public function send() 337 { 338 } 339 340 /** 341 * Complete the request 342 * 343 * Triggers "render" and "finish" events, and returns response from 344 * event object. 345 * 346 * @param MvcEvent $event 347 * @return Application 348 */ 349 protected function completeRequest(MvcEvent $event) 350 { 351 $events = $this->events; 352 $event->setTarget($this); 353 $events->trigger(MvcEvent::EVENT_RENDER, $event); 354 $events->trigger(MvcEvent::EVENT_FINISH, $event); 355 return $this; 356 } 357} 358