Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
14 contributors

Users who have contributed to this file

@samsonasik @prolic @stephanoff @Maks3w @ezimuel @ralphschindler @stefanotorresi @x3ak @Ocramius @BinaryKitten @micheh @fabiocarneiro @devosc @Xerkus
374 lines (333 sloc) 11 KB
<?php
/**
* @link http://github.com/zendframework/zend-mvc for the canonical source repository
* @copyright Copyright (c) 2005-2018 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-mvc/blob/master/LICENSE.md New BSD License
*/
namespace Zend\Mvc;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\Stdlib\RequestInterface;
use Zend\Stdlib\ResponseInterface;
/**
* Main application class for invoking applications
*
* Expects the user will provide a configured ServiceManager, configured with
* the following services:
*
* - EventManager
* - ModuleManager
* - Request
* - Response
* - RouteListener
* - Router
* - DispatchListener
* - MiddlewareListener
* - ViewManager
*
* The most common workflow is:
* <code>
* $services = new Zend\ServiceManager\ServiceManager($servicesConfig);
* $app = new Application($appConfig, $services);
* $app->bootstrap();
* $response = $app->run();
* $response->send();
* </code>
*
* bootstrap() opts in to the default route, dispatch, and view listeners,
* sets up the MvcEvent, and triggers the bootstrap event. This can be omitted
* if you wish to setup your own listeners and/or workflow; alternately, you
* can simply extend the class to override such behavior.
*/
class Application implements
ApplicationInterface,
EventManagerAwareInterface
{
const ERROR_CONTROLLER_CANNOT_DISPATCH = 'error-controller-cannot-dispatch';
const ERROR_CONTROLLER_NOT_FOUND = 'error-controller-not-found';
const ERROR_CONTROLLER_INVALID = 'error-controller-invalid';
const ERROR_EXCEPTION = 'error-exception';
const ERROR_ROUTER_NO_MATCH = 'error-router-no-match';
const ERROR_MIDDLEWARE_CANNOT_DISPATCH = 'error-middleware-cannot-dispatch';
/**
* Default application event listeners
*
* @var array
*/
protected $defaultListeners = [
'RouteListener',
'MiddlewareListener',
'DispatchListener',
'HttpMethodListener',
'ViewManager',
'SendResponseListener',
];
/**
* MVC event token
* @var MvcEvent
*/
protected $event;
/**
* @var EventManagerInterface
*/
protected $events;
/**
* @var \Zend\Stdlib\RequestInterface
*/
protected $request;
/**
* @var ResponseInterface
*/
protected $response;
/**
* @var ServiceManager
*/
protected $serviceManager;
/**
* Constructor
*
* @param ServiceManager $serviceManager
* @param null|EventManagerInterface $events
* @param null|RequestInterface $request
* @param null|ResponseInterface $response
*/
public function __construct(
ServiceManager $serviceManager,
EventManagerInterface $events = null,
RequestInterface $request = null,
ResponseInterface $response = null
) {
$this->serviceManager = $serviceManager;
$this->setEventManager($events ?: $serviceManager->get('EventManager'));
$this->request = $request ?: $serviceManager->get('Request');
$this->response = $response ?: $serviceManager->get('Response');
}
/**
* Retrieve the application configuration
*
* @return array|object
*/
public function getConfig()
{
return $this->serviceManager->get('config');
}
/**
* Bootstrap the application
*
* Defines and binds the MvcEvent, and passes it the request, response, and
* router. Attaches the ViewManager as a listener. Triggers the bootstrap
* event.
*
* @param array $listeners List of listeners to attach.
* @return Application
*/
public function bootstrap(array $listeners = [])
{
$serviceManager = $this->serviceManager;
$events = $this->events;
// Setup default listeners
$listeners = array_unique(array_merge($this->defaultListeners, $listeners));
foreach ($listeners as $listener) {
$serviceManager->get($listener)->attach($events);
}
// Setup MVC Event
$this->event = $event = new MvcEvent();
$event->setName(MvcEvent::EVENT_BOOTSTRAP);
$event->setTarget($this);
$event->setApplication($this);
$event->setRequest($this->request);
$event->setResponse($this->response);
$event->setRouter($serviceManager->get('Router'));
// Trigger bootstrap events
$events->triggerEvent($event);
return $this;
}
/**
* Retrieve the service manager
*
* @return ServiceManager
*/
public function getServiceManager()
{
return $this->serviceManager;
}
/**
* Get the request object
*
* @return \Zend\Stdlib\RequestInterface
*/
public function getRequest()
{
return $this->request;
}
/**
* Get the response object
*
* @return ResponseInterface
*/
public function getResponse()
{
return $this->response;
}
/**
* Get the MVC event instance
*
* @return MvcEvent
*/
public function getMvcEvent()
{
return $this->event;
}
/**
* Set the event manager instance
*
* @param EventManagerInterface $eventManager
* @return Application
*/
public function setEventManager(EventManagerInterface $eventManager)
{
$eventManager->setIdentifiers([
__CLASS__,
get_class($this),
]);
$this->events = $eventManager;
return $this;
}
/**
* Retrieve the event manager
*
* Lazy-loads an EventManager instance if none registered.
*
* @return EventManagerInterface
*/
public function getEventManager()
{
return $this->events;
}
/**
* Static method for quick and easy initialization of the Application.
*
* If you use this init() method, you cannot specify a service with the
* name of 'ApplicationConfig' in your service manager config. This name is
* reserved to hold the array from application.config.php.
*
* The following services can only be overridden from application.config.php:
*
* - ModuleManager
* - SharedEventManager
* - EventManager & Zend\EventManager\EventManagerInterface
*
* All other services are configured after module loading, thus can be
* overridden by modules.
*
* @param array $configuration
* @return Application
*/
public static function init($configuration = [])
{
// Prepare the service manager
$smConfig = isset($configuration['service_manager']) ? $configuration['service_manager'] : [];
$smConfig = new Service\ServiceManagerConfig($smConfig);
$serviceManager = new ServiceManager();
$smConfig->configureServiceManager($serviceManager);
$serviceManager->setService('ApplicationConfig', $configuration);
// Load modules
$serviceManager->get('ModuleManager')->loadModules();
// Prepare list of listeners to bootstrap
$listenersFromAppConfig = isset($configuration['listeners']) ? $configuration['listeners'] : [];
$config = $serviceManager->get('config');
$listenersFromConfigService = isset($config['listeners']) ? $config['listeners'] : [];
$listeners = array_unique(array_merge($listenersFromConfigService, $listenersFromAppConfig));
return $serviceManager->get('Application')->bootstrap($listeners);
}
/**
* Run the application
*
* @triggers route(MvcEvent)
* Routes the request, and sets the RouteMatch object in the event.
* @triggers dispatch(MvcEvent)
* Dispatches a request, using the discovered RouteMatch and
* provided request.
* @triggers dispatch.error(MvcEvent)
* On errors (controller not found, action not supported, etc.),
* populates the event with information about the error type,
* discovered controller, and controller class (if known).
* Typically, a handler should return a populated Response object
* that can be returned immediately.
* @return self
*/
public function run()
{
$events = $this->events;
$event = $this->event;
// Define callback used to determine whether or not to short-circuit
$shortCircuit = function ($r) use ($event) {
if ($r instanceof ResponseInterface) {
return true;
}
if ($event->getError()) {
return true;
}
return false;
};
// Trigger route event
$event->setName(MvcEvent::EVENT_ROUTE);
$event->stopPropagation(false); // Clear before triggering
$result = $events->triggerEventUntil($shortCircuit, $event);
if ($result->stopped()) {
$response = $result->last();
if ($response instanceof ResponseInterface) {
$event->setName(MvcEvent::EVENT_FINISH);
$event->setTarget($this);
$event->setResponse($response);
$event->stopPropagation(false); // Clear before triggering
$events->triggerEvent($event);
$this->response = $response;
return $this;
}
}
if ($event->getError()) {
return $this->completeRequest($event);
}
// Trigger dispatch event
$event->setName(MvcEvent::EVENT_DISPATCH);
$event->stopPropagation(false); // Clear before triggering
$result = $events->triggerEventUntil($shortCircuit, $event);
// Complete response
$response = $result->last();
if ($response instanceof ResponseInterface) {
$event->setName(MvcEvent::EVENT_FINISH);
$event->setTarget($this);
$event->setResponse($response);
$event->stopPropagation(false); // Clear before triggering
$events->triggerEvent($event);
$this->response = $response;
return $this;
}
$response = $this->response;
$event->setResponse($response);
return $this->completeRequest($event);
}
/**
* Complete the request
*
* Triggers "render" and "finish" events, and returns response from
* event object.
*
* @param MvcEvent $event
* @return Application
*/
protected function completeRequest(MvcEvent $event)
{
$events = $this->events;
$event->setTarget($this);
$event->setName(MvcEvent::EVENT_RENDER);
$event->stopPropagation(false); // Clear before triggering
$events->triggerEvent($event);
$event->setName(MvcEvent::EVENT_FINISH);
$event->stopPropagation(false); // Clear before triggering
$events->triggerEvent($event);
return $this;
}
}
You can’t perform that action at this time.