diff --git a/.gitignore b/.gitignore index 78ee4855c..f146c8613 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ nbproject doc/html/ tmp/ +zf-mkdoc-theme/ clover.xml composer.lock diff --git a/README.md b/README.md index 43fb1f07b..5c241247c 100644 --- a/README.md +++ b/README.md @@ -21,4 +21,4 @@ The MVC layer is built on top of the following components: - File issues at https://github.com/zendframework/zend-mvc/issues -- Documentation is at http://framework.zend.com/manual/current/en/index.html#zend-mvc +- Documentation is at https://zendframework.github.io/zend-mvc/ diff --git a/doc/book/controllers.md b/doc/book/controllers.md new file mode 100644 index 000000000..7bd14befc --- /dev/null +++ b/doc/book/controllers.md @@ -0,0 +1,314 @@ +# Available Controllers + +Controllers in zend-mvc are objects implementing `Zend\Stdlib\DispatchableInterface`. +That interface describes a single method: + +```php +use Zend\Stdlib\DispatchableInterface; +use Zend\Stdlib\RequestInterface as Request; +use Zend\Stdlib\ResponseInterface as Response; + +class Foo implements DispatchableInterface +{ + public function dispatch(Request $request, Response $response = null) + { + // ... do something, and preferably return a Response ... + } +} +``` + +While the pattern is straight-forward, chances are you don't want to implement +custom dispatch logic for every controller, particularly as it's not unusual or +uncommon for a single controller to handle several related types of requests. + +To provide convenience, zend-mvc also defines several interfaces that, when +implemented, can provide controllers with additional capabilities. + +## Common Interfaces Used With Controllers + +### InjectApplicationEvent + +The `Zend\Mvc\InjectApplicationEventInterface` hints to the `Application` +instance that it should inject its `MvcEvent` into the controller itself. Why +would this be useful? + +Recall that the `MvcEvent` composes a number of objects: the `Request` and +`Response`, naturally, but also the router, the route matches (a `RouteMatch` +instance), and potentially the "result" of dispatching. + +A controller that has the `MvcEvent` injected, then, can retrieve or inject +these. As an example: + +```php +$matches = $this->getEvent()->getRouteMatch(); +$id = $matches->getParam('id', false); +if (! $id) { + $response = $this->getResponse(); + $response->setStatusCode(500); + $this->getEvent()->setResult('Invalid identifier; cannot complete request'); + return; +} +``` + +The `InjectApplicationEventInterface` defines two methods: + +```php +public function setEvent(Zend\EventManager\EventInterface $event); +public function getEvent(); +``` + +### ServiceLocatorAware + +In most cases, you should define your controllers such that dependencies are +injected by the application's `ServiceManager`, via either constructor arguments +or setter methods. + +However, occasionally you may have objects you wish to use in your controller +that are only valid for certain code paths. Examples include forms, paginators, +navigation, etc. In these cases, you may decide that it doesn't make sense to +inject those objects every time the controller is used. + +The `ServiceLocatorAwareInterface` interface hints to the `ServiceManager` that it should inject +itself into the controller. It defines two simple methods: + +```php +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\ServiceManager\ServiceLocatorAwareInterface; + +public function setServiceLocator(ServiceLocatorInterface $serviceLocator); +public function getServiceLocator(); +``` + +> #### ServiceLocatorInterface is deprecated +> +> `ServiceLocatorAwareInterface` [was removed from zend-servicemanager v3.0](http://zendframework.github.io/zend-servicemanager/migration/#miscellaneous-interfaces-traits-and-classes), +> and, as such, starting in zend-mvc 2.7.0, the `AbstractController` +> implementation no longer implements the interface, though it implements the +> methods the interface defines; this allows forwards compatibility with +> zend-servicemanager v3. +> +> However, also starting with the zend-mvc 2.7.0 release, `ServiceLocatorAwareInterface` +> usage is deprecated. We recommend injecting dependencies explicitly instead of +> pulling them from a composed `ServiceManager` instance. +> +> In cases where an object will not be used in all code paths, we recommend +> splitting into discrete controllers, or using [lazy services](http://zendframework.github.io/zend-servicemanager/lazy-services/). + +### EventManagerAware + +Typically, it's nice to be able to tie into a controller's workflow without +needing to extend it or hardcode behavior into it. The solution for this is to +use the `EventManager`. + +You can hint to the `ServiceManager` that you want an `EventManager` injected by +implementing the interface `EventManagerAwareInterface`, which tells the +`ServiceManager` to inject an `EventManager`. + +To do this, you define two methods. The first, a setter, should also set any +`SharedEventManager` identifiers you want to listen on, and the second, a getter, +should return the composed `EventManager` instance. + +```php +use Zend\EventManager\EventManagerAwareInterface; +use Zend\EventManager\EventManagerInterface; + +public function setEventManager(EventManagerInterface $events); +public function getEventManager(); +``` + +### Controller Plugins + +Code re-use is a common goal for developers. Another common goal is convenience. +However, this is often difficult to achieve cleanly in abstract, general +systems. + +Within your controllers, you'll often find yourself repeating tasks from one +controller to another. Some common examples: + +- Generating URLs. +- Redirecting. +- Setting and retrieving flash messages (self-expiring session messages). +- Invoking and dispatching additional controllers. + +To facilitate these actions while also making them available to alternate +controller implementations, we've created a `PluginManager` implementation for +the controller layer, `Zend\Mvc\Controller\PluginManager`, building on the +`Zend\ServiceManager\AbstractPluginManager` functionality. To utilize it, +implement the `setPluginManager(PluginManager $plugins)` method, and set up your +code to use the controller-specific implementation by default: + +```php +use Zend\Mvc\Controller\PluginManager; + +public function setPluginManager(PluginManager $plugins) +{ + $this->plugins = $plugins; + $this->plugins->setController($this); + + return $this; +} + +public function getPluginManager() +{ + if (!$this->plugins) { + $this->setPluginManager(new PluginManager()); + } + + return $this->plugins; +} + +public function plugin($name, array $options = null) +{ + return $this->getPluginManager()->get($name, $options); +} +``` + +## AbstractActionController + +Implementing each of the above interfaces is a lesson in redundancy; you won't +often want to do it. As such, we've developed abstract, base controllers you +can extend to get started. + +The first is `Zend\Mvc\Controller\AbstractActionController`. This controller +implements each of the above interfaces, and uses the following assumptions: + +- An "action" parameter is expected in the `RouteMatch` object composed in the + attached `MvcEvent`. If none is found, a `notFoundAction()` is invoked. +- The "action" parameter is converted to a camelCased format and appended with + the word "Action" to create a method name. As examples: "foo" maps to + `fooAction`, "foo-bar" or "foo.bar" or "foo\_bar" to `fooBarAction`. The + controller then checks to see if that method exists. If not, the + `notFoundAction()` method is invoked; otherwise, the discovered method is + called. +- The results of executing the given action method are injected into the + `MvcEvent`'s "result" property (via `setResult()`, and accessible via + `getResult()`). + +Essentially, a route mapping to an `AbstractActionController` needs to return +both the "controller" and "action" keys in its matches. + +Creation of action controllers looks like the following example: + +```php +namespace Foo\Controller; + +use Zend\Mvc\Controller\AbstractActionController; + +class BarController extends AbstractActionController +{ + public function bazAction() + { + return ['title' => __METHOD__]; + } + + public function batAction() + { + return ['title' => __METHOD__]; + } +} +``` + +### Interfaces and Collaborators + +`AbstractActionController` implements each of the following interfaces: + +- `Zend\Stdlib\DispatchableInterface` +- `Zend\Mvc\InjectApplicationEventInterface` +- `Zend\ServiceManager\ServiceLocatorAwareInterface` (starting with zend-mvc + 2.7.0, only the methods defined by the interface, not the interface itself) +- `Zend\EventManager\EventManagerAwareInterface` + +The composed `EventManager` will be configured to listen on the following contexts: + +- `Zend\Stdlib\DispatchableInterface` +- `Zend\Mvc\Controller\AbstractActionController` +- `Zend\Mvc\Controller\AbstractController` + +Additionally, if you extend the class, it will listen on the name of the +extending class. + +## AbstractRestfulController + +`Zend\Mvc\Controller\AbstractRestfulController` provides a native RESTful +implementation that maps HTTP request methods to controller methods, using the +following matrix: + +- **GET** maps to either `get()` or `getList()`, depending on whether or not an + "id" parameter is found in the route matches. If one is, it is passed as an + argument to `get()`; if not, `getList()` is invoked. In the former case, you + should provide a representation of the given entity with that identification; + in the latter, you should provide a list of entities. +- **POST** maps to `create()`. That method expects a `$data` argument, usually + the `$_POST` superglobal array. The data should be used to create a new + entity, and the response should typically be an HTTP 201 response with the + Location header indicating the URI of the newly created entity and the + response body providing the representation. +- **PUT** maps to `update()`, and requires that an "id" parameter exists in the + route matches; that value is passed as an argument to the method. It should + attempt to update the given entity, and, if successful, return either a 200 or + 202 response status, as well as the representation of the entity. +- **DELETE** maps to `delete()`, and requires that an "id" parameter exists in + the route matches; that value is passed as an argument to the method. It + should attempt to delete the given entity, and, if successful, return either a + 200 or 204 response status. + +Additionally, you can map "action" methods to the `AbstractRestfulController`, +just as you would in the `AbstractActionController`; these methods will be +suffixed with "Action", differentiating them from the RESTful methods listed +above. This allows you to perform such actions as providing forms used to submit +to the various RESTful methods, or to add RPC methods to your RESTful API. + +### Interfaces and Collaborators + +`AbstractRestfulController` implements each of the following interfaces: + +- `Zend\Stdlib\DispatchableInterface` +- `Zend\Mvc\InjectApplicationEventInterface` +- `Zend\ServiceManager\ServiceLocatorAwareInterface` (starting with zend-mvc + 2.7.0, only the methods defined by the interface, not the interface itself) +- `Zend\EventManager\EventManagerAwareInterface` + +The composed `EventManager` will be configured to listen on the following contexts: + +- `Zend\Stdlib\DispatchableInterface` +- `Zend\Mvc\Controller\AbstractRestfulController` +- `Zend\Mvc\Controller\AbstractController` + +Additionally, if you extend the class, it will listen on the name of the +extending class. + +## AbstractConsoleController + +`Zend\Mvc\Controller\AbstractConsoleController` extends from [AbstractActionController](#abstractactioncontroller) +and provides the following functionality: + +- The method `setConsole(Zend\Console\Adapter\AdapterInterface $console)` allows + injecting a console adapter representing the current console environment. By + default, the `ControllerManager` will inject this for you as part of + controller instantiation. +- The method `getConsole()` allows you to retrieve the current console adapter + instance, allowing you to retrieve console capabilities and generate console + output. +- The `dispatch()` method will throw an exception if invoked in a non-console + environment, ensuring that you do not need to do any checks within your action + methods for the environment. + +### Interfaces and Collaborators + +`AbstractRestfulController` implements each of the following interfaces: + +- `Zend\Stdlib\DispatchableInterface` +- `Zend\Mvc\InjectApplicationEventInterface` +- `Zend\ServiceManager\ServiceLocatorAwareInterface` (starting with zend-mvc + 2.7.0, only the methods defined by the interface, not the interface itself) +- `Zend\EventManager\EventManagerAwareInterface` + +The composed `EventManager` will be configured to listen on the following contexts: + +- `Zend\Stdlib\DispatchableInterface` +- `Zend\Mvc\Controller\AbstractConsoleController` +- `Zend\Mvc\Controller\AbstractActionController` +- `Zend\Mvc\Controller\AbstractController` + +Additionally, if you extend the class, it will listen on the name of the +extending class. diff --git a/doc/book/examples.md b/doc/book/examples.md new file mode 100644 index 000000000..48aa6b5af --- /dev/null +++ b/doc/book/examples.md @@ -0,0 +1,117 @@ +# Examples + +## Controllers + +### Accessing the Request and Response + +When using one of the abstract controller implementations, the request and +response object are composed directly into the controller as soon as +`dispatch()` is called. You may access them as follows: + +```php +// Using explicit accessor methods +$request = $this->getRequest(); +$response = $this->getResponse(); + +// Using direct property access +$request = $this->request; +$response = $this->response; +``` + +Additionally, if your controller implements `InjectApplicationEventInterface` +(as all shipped abstract controllers do), you can access these objects from the +attached `MvcEvent`: + +```php +$event = $this->getEvent(); +$request = $event->getRequest(); +$response = $event->getResponse(); +``` + +The above can be useful when composing event listeners into your controller. + +### Accessing routing parameters + +The parameters returned when routing completes are wrapped in a +`Zend\Mvc\Router\RouteMatch` object. This object is detailed in the section on +[Routing](routing.md). + +Within your controller, if you implement `InjectApplicationEventInterface` (as +all shipped abstract controllers do), you can access this object from the +attached `MvcEvent`: + +```php +$event = $this->getEvent(); +$matches = $event->getRouteMatch(); +``` + +Once you have the `RouteMatch` object, you can pull parameters from it. + +The same can be done using the [Params plugin](plugins.md#params-plugin). + +### Returning early + +You can short-circuit execution of the application at any point by returning a +`Response` from your controller or any event. When such a value is discovered, +it halts further execution of the event manager, bubbling up to the +`Application` instance, where it is immediately returned. + +As an example, the `Redirect` plugin returns a `Response`, which can be returned +immediately so as to complete the request as quickly as possible. Other use +cases might be for returning JSON or XML results from web service endpoints, +returning "401 Unauthorized" results, etc. + +## Bootstrapping + +### Registering module-specific listeners + +You may want module-specific listeners; these allow you to introduce +authorization, logging, caching, or other concerns into your application. + +Each `Module` class can have an optional `onBootstrap()` method. This method is +used for module-specific configuration, and is the ideal location to setup event +listeners for you module. The `onBootstrap()` method is called for **every** +module on **every** page request and should **only** be used for performing +**lightweight** tasks such as registering event listeners. + +The base `Application` class shipped with the framework has an `EventManager` +associated with it, and once the modules are initialized, it triggers the +[bootstrap](mvc-event.md#mvceventevent_bootstrap-bootstrap) event, which +provides a `getApplication()` method on the event. + +One way to accomplish module-specific listeners is to listen to that event, and +register listeners at that time. As an example: + +```php +namespace SomeCustomModule; + +class Module +{ + /** + * @param \Zend\Mvc\MvcEvent $e The MvcEvent instance + * @return void + */ + public function onBootstrap($e) + { + $application = $e->getApplication(); + $config = $application->getConfig(); + $view = $application->getServiceManager()->get('ViewHelperManager'); + // You must have these keys in you application config + $view->headTitle($config['view']['base_title']); + + // This is your custom listener + $listener = new Listeners\ViewListener(); + $listener->setView($view); + $listener->attach($application->getEventManager()); + } +} +``` + +The above demonstrates several things. First, it demonstrates a listener on the +application's [bootstrap](mvc-event.md#mvceventevent_bootstrap-bootstrap) event +(the `onBootstrap()` method). Second, it demonstrates that listener, and how it +can be used to register listeners with the application. It grabs the +`Application` instance; from the `Application`, it is able to grab the attached +service manager and configuration. These are then used to retrieve the view, +configure some helpers, and then register a listener aggregate with the +application event manager. diff --git a/doc/book/index.html b/doc/book/index.html new file mode 100644 index 000000000..1bd278447 --- /dev/null +++ b/doc/book/index.html @@ -0,0 +1,10 @@ +
+
+

zend-mvc

+ +

Zend Framework's event-driven MVC layer, including MVC Applications, Controllers, and Plugins.

+ +
$ composer require zendframework/zend-mvc
+
+
+ diff --git a/doc/book/index.md b/doc/book/index.md new file mode 120000 index 000000000..fe8400541 --- /dev/null +++ b/doc/book/index.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/doc/book/intro.md b/doc/book/intro.md new file mode 100644 index 000000000..d6c8c95a4 --- /dev/null +++ b/doc/book/intro.md @@ -0,0 +1,415 @@ +# Introduction + +zend-mvc is the MVC layer shipped with Zend Framework 2 and above, and focuses +on performance and flexibility. + +The MVC layer is built on top of the following components: + +- zend-servicemanager - zend-mvc provides a set of default service definitions, + used to create and configure the application instance and workflow. +- zend-eventmanager - The MVC is event driven, and uses events for the initial + bootstrapping of the application, for returning response and request calls, + for matching routes, and even for rendering views. +- zend-http - specifically the request and response objects from the + `Zend\Http\PhpEnvironment` namespace, which provide objects that ensure the + request is injected with the current environment (including query parameters, + POST parameters, HTTP headers, etc.), and that the response will interact + correctly with the SAPI and output buffering. +- zend-stdlib - specifically `Zend\Stdlib\DispatchableInterface`. All + "controllers" are simply dispatchable objects. + +Within the MVC layer, several sub-components are exposed: + +- `Zend\Mvc\Router` contains classes pertaining to routing a request. In other + words, it matches the request to its respective controller (or dispatchable). +- `Zend\Mvc\Controller`, a set of abstract "controller" classes with basic + responsibilities such as event wiring, action dispatching, etc., as well as + controller plugins. +- `Zend\Mvc\Service` provides a set of zend-servicemanager factories and + definitions for the default application workflow. +- `Zend\Mvc\View` provides default wiring for renderer selection, view script + resolution, helper registration, and more; additionally, it provides a + number of listeners that tie into the MVC workflow, providing features such + as automated template name resolution, automated view model creation and + injection, etc. + +The gateway to the MVC is the [Zend\\Mvc\\Application](https://github.com/zendframework/zf2/blob/master/library/Zend/Mvc/Application.php) +object (referred to as `Application` henceforth). Its primary responsibilities +are to **bootstrap** resources, to **route** the request, and to retrieve and +**dispatch** the controller matched during routing. Once accomplished, it will +**render** the view, and **finish** the request, returning and sending the +response. + +## Basic Application Structure + +The basic application structure follows: + +``` +application_root/ + config/ + application.config.php + autoload/ + global.php + local.php + // etc. + data/ + module/ + vendor/ + public/ + .htaccess + index.php + init_autoloader.php +``` + +The `public/index.php` script marshals all user requests to your website, +retrieving an array of configuration from `config/application.config.php`. On +return, it `run()`s the `Application`, processing the request and returning a +response to the user. + +The `config` directory as described above contains configuration used by +zend-modulemanager to load modules and merge configuration (e.g., database +configuration and credentials); we will detail this more later. + +The `vendor` sub-directory should contain any third-party modules or libraries +on which your application depends. This might include Zend Framework, custom +libraries from your organization, or other third-party libraries from other +projects. Libraries and modules placed in the `vendor` sub-directory should not +be modified from their original, distributed state. Typically, this directory +will be managed by [Composer](https://getcomposer.org). + +Finally, the `module` directory will contain one or more modules delivering your +application's functionality. + +Let's now turn to modules, as they are the basic units of a web application. + +## Basic Module Structure + +A module may contain anything: PHP code, including MVC functionality; library +code; view scripts; and/or or public assets such as images, CSS, and JavaScript. +The only requirement — and even this is optional — is that a module +acts as a PHP namespace and that it contains a `Module` class under that +namespace. This class is eventually consumed by zend-modulemanager to perform a +number of tasks. + +The recommended module structure follows: + +``` +module_root/ + Module.php + autoload_classmap.php + autoload_function.php + autoload_register.php + config/ + module.config.php + public/ + images/ + css/ + js/ + src/ + / + + test/ + phpunit.xml + bootstrap.php + / + + view/ + / + / + <.phtml files> +``` + +Since a module acts as a namespace, the module root directory should be that +namespace. This namespace could also include a vendor prefix of sorts. As an +example a module centered around "User" functionality delivered by Zend might be +named "ZendUser", and this is also what the module root directory will be named. + +> ### Source and test code organization +> +> The above details a [PSR-0](http://www.php-fig.org/psr/psr-0/) structure for +> the source and test code directories. You can also use +> [PSR-4](http://www.php-fig.org/psr/psr-4/) so long as you have setup +> autoloading correctly to do so. + +The `Module.php` file directly under the module root directory will be in the +module namespace shown below. + +```php +namespace ZendUser; + +class Module +{ +} +``` + +> ### Module class location +> +> If you have an autoloader defined, such as detailed later around the various +> `autoload_*.php` files or using Composer's [autoloading features](https://getcomposer.org/doc/01-basic-usage.md#autoloading), +> then your `Module.php` file can be co-located with your source code. +> +> Our current recommendation is to define autoloading for your +> application-specific modules via Composer. + +When an `init()` method is defined, this method will be triggered by a +zend-modulemanager listener when it loads the module class, and passed a +`ModuleManager` instance by default. This allows you to perform tasks such as +setting up module-specific event listeners. But be cautious, the `init()` method +is called for **every** module on **every** page request and should **only** be +used for performing **lightweight** tasks such as registering event listeners. +Similarly, an `onBootstrap()` method (which accepts an `MvcEvent` instance) may +be defined; it is also triggered for every page request, and should be used for +lightweight tasks as well. + +The three `autoload_*.php` files are not required, but recommended if you are +not using Composer to provide autoloading for your module. They provide the +following: + + File | Description +-------------------------|------------ + `autoload_classmap.php` | Should return an array classmap of class name/filename pairs (with the filenames resolved via the `__DIR__` magic constant). + `autoload_function.php` | Should return a PHP callback that can be passed to `spl_autoload_register()`. Typically, this callback should utilize the map returned by `autoload_classmap.php.` + `autoload_register.php` | should register a PHP callback (is typically returned by `autoload_function.php` with `spl_autoload_register()`. + +The point of these three files is to provide reasonable default mechanisms for autoloading the +classes contained in the module, thus providing a trivial way to consume the module without +requiring zend-modulemanager (e.g., for use outside a ZF2 application). + +The `config` directory should contain any module-specific configuration. These +files may be in any format zend-config supports. We recommend naming the main +configuration `module.config.` (e.g., for PHP-based configuration, +`module.config.php`). Typically, you will create configuration for the router as +well as for the service manager. + +The `src` directory should be a [PSR-0](http://www.php-fig.org/psr/psr-0/) or +[PSR-4](http://www.php-fig.org/psr/psr-4/) compliant directory structure with +your module's source code. + +The `test` directory should contain your unit tests. Typically, these are written using +[PHPUnit](http://phpunit.de). + +The `public` directory can be used for assets that you may want to expose in +your application's document root. These might include images, CSS files, +JavaScript files, etc. How these are exposed is left to the developer. + +The `view` directory contains view scripts related to your controllers. + +## Bootstrapping an Application + +The `Application` has seven basic dependencies. + +- **configuration**, usually an array or object implementing `Traversable`. +- **ServiceManager** instance. +- **EventManager** instance, which, by default, is pulled from the + `ServiceManager`, by the service name "EventManager". +- **SharedEventManager** instance, which, by default, is pulled from the + `ServiceManager`, by the service name "SharedEventManager"; this is injected + into the `EventManager` instance, and then pushed into every new + `EventManager` instance created. +- **ModuleManager** instance, which, by default, is pulled from the + `ServiceManager`, by the service name "ModuleManager". +- **Request** instance, which, by default, is pulled from the `ServiceManager`, + by the service name "Request". +- **Response** instance, which, by default, is pulled from the `ServiceManager`, + by the service name "Response". + +These may be satisfied at instantiation: + +```php +use Zend\EventManager\EventManager; +use Zend\EventManager\SharedEventManager; +use Zend\Http\PhpEnvironment; +use Zend\ModuleManager\ModuleManager; +use Zend\Mvc\Application; +use Zend\ServiceManager\ServiceManager; + +$config = include 'config/application.config.php'; + +$serviceManager = new ServiceManager(); +$serviceManager->setService('SharedEventManager', new SharedEventManager()); +$serviceManager->setService('ModuleManager', new ModuleManager($config)); +$serviceManager->setService('Request', new PhpEnvironment\Request()); +$serviceManager->setService('Response', new PhpEnvironment\Response()); +$serviceManager->setFactory('EventManager', function ($serviceManager) { + $eventManager = new EventManager(); + $eventManager->setSharedManager($serviceManager->get('SharedEventManager'); + return $eventManager; +}); +$serviceManager->setShared('EventManager', false); + +$application = new Application($config, $serviceManager); +``` + +Once you've done this, there are two additional actions you can take. The first is to "bootstrap" +the application. In the default implementation, this does the following: + +- Attaches the default route listener (`Zend\Mvc\RouteListener`). +- Attaches the default dispatch listener (`Zend\Mvc\DispatchListener`). +- Attaches the `ViewManager` listener (`Zend\Mvc\View\ViewManager`). +- Creates the `MvcEvent`, and injects it with the application, request, and + response; it also retrieves the router (`Zend\Mvc\Router\Http\TreeRouteStack`) + at this time and attaches it to the event. +- Triggers the "bootstrap" event. + +If you do not want these actions, or want to provide alternatives, you can do so by extending the +`Application` class and/or manually coding what actions you want to occur. + +The second action you can take with the configured `Application` is to `run()` +it. Calling this method performs the following: + +- it triggers the "route" event, +- followed by the "dispatch" event, and, depending on execution, +- the "render" event + +When done, it triggers the "finish" event, and then returns the response +instance. If an error occurs during either the "route" or "dispatch" event, a +"dispatch.error" event is triggered as well. + +This is a lot to remember in order to bootstrap the application; in fact, we haven't covered all the +services available by default yet. You can greatly simplify things by using the default +`ServiceManager` configuration shipped with the MVC. + +```php +use Zend\Loader\AutoloaderFactory; +use Zend\Mvc\Service\ServiceManagerConfig; +use Zend\ServiceManager\ServiceManager; + +// setup autoloader +AutoloaderFactory::factory(); + +// get application stack configuration +$configuration = include 'config/application.config.php'; + +// setup service manager +$serviceManager = new ServiceManager(new ServiceManagerConfig()); +$serviceManager->setService('ApplicationConfig', $configuration); + +// load modules -- which will provide services, configuration, and more +$serviceManager->get('ModuleManager')->loadModules(); + +// bootstrap and run application +$application = $serviceManager->get('Application'); +$application->bootstrap(); +$application->run(); +``` + +You can make this even simpler by using the `init()` method of the +`Application`. This is a static method for quick and easy initialization of the +`Application` instance. + +```php +use Zend\Loader\AutoloaderFactory; +use Zend\Mvc\Application; +use Zend\Mvc\Service\ServiceManagerConfig; +use Zend\ServiceManager\ServiceManager; + +// setup autoloader +AutoloaderFactory::factory(); + +// get application stack configuration +$configuration = include 'config/application.config.php'; + +// The init() method does something very similar with the previous example. +Application::init($configuration)->run(); +``` + +The `init()` does the following: + +- Grabs the application configuration and pulls from the `service_manager` key, + creating a `ServiceManager` instance with it and with the default services + shipped with zend-mvc; +- Creates a service named `ApplicationConfig` with the application configuration array; +- Grabs the `ModuleManager` service and loads the modules; +- `bootstrap()`s the `Application` and returns its instance. + +> ### ApplicationConfig service +> +> If you use the `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` and `Zend\EventManager\EventManagerInterface` +> +> All other services are configured after module loading, thus can be overridden +> by modules. + +You'll note that you have a great amount of control over the workflow. Using the +`ServiceManager`, you have fine-grained control over what services are +available, how they are instantiated, and what dependencies are injected into +them. Using the `EventManager`'s priority system, you can intercept any of the +application events ("bootstrap", "route", "dispatch", "dispatch.error", +"render", and "finish") anywhere during execution, allowing you to craft your +own application workflows as needed. + +## Bootstrapping a Modular Application + +While the previous approach largely works, where does the configuration come +from? When we create a modular application, the assumption will be that it's +from the modules themselves. How do we get that information and aggregate it, +then? + +The answer is via zend-modulemanager. This component allows you to specify what +modules the application will use; it then locates each module and initializes +it. Module classes can tie into various listeners in order to provide +configuration, services, listeners, and more to the application. Sounds +complicated? It's not. + +### Configuring the Module Manager + +The first step is configuring the module manager. Inform the module manager +which modules to load, and potentially provide configuration for the module +listeners. + +Remember the `application.config.php` from earlier? We're going to provide some +configuration. + +```php + array( + /* ... */ + ), + 'module_listener_options' => array( + 'module_paths' => array( + './module', + './vendor', + ), + ), +); +``` + +As we add modules to the system, we'll add items to the `modules` array. + +Each `Module` class that has configuration it wants the `Application` to know +about should define a `getConfig()` method. That method should return an array +or `Traversable` object such as a `Zend\Config\Config` instance. As an example: + +```php +namespace ZendUser; + +class Module +{ + public function getConfig() + { + return include __DIR__ . '/config/module.config.php' + } +} +``` + +There are a number of other methods you can define for tasks ranging from +providing autoloader configuration, to providing services to the +`ServiceManager`, to listening to the bootstrap event. The +[ModuleManager documentation](http:/framework.zend.com/manual/current/en/modules/zend.module-manager.intro.html) +goes into more detail on these. + +## Conclusion + +zend-mvc is incredibly flexible, offering an opt-in, easy to create modular +infrastructure, as well as the ability to craft your own application workflows +via the `ServiceManager` and `EventManager`. The `ModuleManager` is a +lightweight and simple approach to enforcing a modular architecture that +encourages clean separation of concerns and code re-use. diff --git a/doc/book/migration.md b/doc/book/migration.md index bcf6b4cae..41868188e 100644 --- a/doc/book/migration.md +++ b/doc/book/migration.md @@ -1,8 +1,8 @@ # Migration Guide -This is a guide for migration from version 2 to version 3 of zend-mvc. +## Upgrading to 2.7 -## Application +### Application The constructor signature of `Zend\Mvc\Application` has changed. Previously, it was: @@ -14,31 +14,36 @@ __construct($configuration, ServiceManager $serviceManager) and internally, it pulled the services `EventManager`, `Request`, and `Response` from the provided `$serviceManager` during initialization. -The new constructor signature is: +The new constructor signature provides optional arguments for injecting the +event manager, request, and response: ```php __construct( $configuration, ServiceManager $serviceManager, - EventManager $events, - RequestInterface $request, - ResponseInterface $response + EventManager $events = null, + RequestInterface $request = null, + ResponseInterface $response = null ) ``` -making all dependencies explicit. The factory -`Zend\Mvc\Service\ApplicationFactory` was updated to follow the new signature. +This change makes all dependencies explicit. Starting in v3.0, the new arguments +will be *required*. + +The factory `Zend\Mvc\Service\ApplicationFactory` was updated to follow the new +signature. This change should only affect users who are manually instantiating the `Application` instance. -## EventManager initializer and ControllerManager event manager injection +### EventManagerAware initializers zend-mvc provides two mechanisms for injecting event managers into `EventManagerAware` objects. One is the "EventManagerAwareInitializer" -registered in `Zend\Mvc\Service\ServiceManagerConfig`, and the other is internal -logic in `Zend\Mvc\Controller\ControllerManager`. In both cases, the logic was -updated due to changes in the v3 version of zend-eventmanager. +registered in `Zend\Mvc\Service\ServiceManagerConfig`, and the other is the +`Zend\Mvc\Controller\ControllerManager::injectEventManager()` initializer. In +both cases, the logic was updated to be forwards compatible with +zend-eventmanager v3. Previously each would check if the instance's `getEventManager()` method returned an event manager instance, and, if so, inject the shared event manager: @@ -67,3 +72,27 @@ pulled does not have a shared manager composed. This likely will not cause regressions in existing code, but may be something to be aware of if you were previously depending on lazy-loaded event manager state. + +### ServiceLocatorAware initializers + +zend-servicemanager v3.0 removes `Zend\ServiceManager\ServiceLocatorAwareInterface`. +Since zend-mvc provides initializers around that interface, they needed updates +to allow both forwards compatibility with zend-servicemanager v3 as well as +backwards compatibility with existing applications. + +This was accomplished in two ways: + +- The abstract controller implementations no longer implement + `ServiceLocatorAwareInterface`, but continue to define the methods that the + interface defines (namely `setServiceLocator()` and `getServiceLocator()`. +- The initializers registered by `Zend\Mvc\Service\ServiceManagerConfig` and + `Zend\Mvc\Controller\ControllerManager` now use duck-typing to determine if + an instance requires container injection; if so it will do so. + +However, we also maintain that service locator injection is an anti-pattern; +dependencies should be injected directly into instances instead. As such, +starting in 2.7.0, we now emit a deprecation notice any time an instance is +injected by one of these initializers, and we plan to remove the initializers +for version 3.0. The deprecation notice includes the name of the class, to help +you identify what instances you will need to update before the zend-mvc v3 +release. diff --git a/doc/book/mvc-event.md b/doc/book/mvc-event.md new file mode 100644 index 000000000..60ef30172 --- /dev/null +++ b/doc/book/mvc-event.md @@ -0,0 +1,284 @@ +# The MvcEvent + +zend-mvc defines and utilizes a custom `Zend\EventManager\Event` implementation, +`Zend\Mvc\MvcEvent`. This event is created during `Zend\Mvc\Application::bootstrap()` +and is passed when triggering all application events. Additionally, if your +controllers implement the `Zend\Mvc\InjectApplicationEventInterface`, `MvcEvent` +will be injected into those controllers. + +The `MvcEvent` adds accessors and mutators for the following: + +- `Application` object. +- `Request` object. +- `Response` object. +- `Router` object. +- `RouteMatch` object. +- Result - usually the result of dispatching a controller. +- `ViewModel` object, typically representing the layout view model. + +The methods it defines are: + +- `setApplication($application)` +- `getApplication()` +- `setRequest($request)` +- `getRequest()` +- `setResponse($response)` +- `getResponse()` +- `setRouter($router)` +- `getRouter()` +- `setRouteMatch($routeMatch)` +- `getRouteMatch()` +- `setResult($result)` +- `getResult()` +- `setViewModel($viewModel)` +- `getViewModel()` +- `isError()` +- `setError()` +- `getError()` +- `getController()` +- `setController($name)` +- `getControllerClass()` +- `setControllerClass($class)` + +The `Application`, `Request`, `Response`, `Router`, and `ViewModel` are all +injected during the `bootstrap` event. Following the `route` event, it will be +injected also with the `RouteMatch` object encapsulating the results of routing. + +Since this object is passed around throughout the MVC, it is a common location +for retrieving the results of routing, the router, and the request and response +objects. Additionally, we encourage setting the results of execution in the +event, to allow event listeners to introspect them and utilize them within their +execution. As an example, the results could be passed into a view renderer. + +## Order of events + +The following events are triggered, in the following order: + +Name | Constant | Description +-----------------|----------------------------------|------------ +`bootstrap` | `MvcEvent::EVENT_BOOTSTRAP` | Bootstrap the application by creating the ViewManager. +`route` | `MvcEvent::EVENT_ROUTE` | Perform routing (or route-related actions). +`dispatch` | `MvcEvent::EVENT_DISPATCH` | Dispatch the matched route to a controller/action. +`dispatch.error` | `MvcEvent::EVENT_DISPATCH_ERROR` | Event triggered in case of a problem during dispatch process (e.g., unknown controller). +`render` | `MvcEvent::EVENT_RENDER` | Prepare the data and delegate the rendering to the view layer. +`render.error` | `MvcEvent::EVENT_RENDER_ERROR` | Event triggered in case of a problem during the render process (e.g., no renderer found). +`finish` | `MvcEvent::EVENT_FINISH` | Perform tasks once everything else is done. + +The following sections provide more detail on each event. + +## `MvcEvent::EVENT_BOOTSTRAP` ("bootstrap") + +### Listeners + +The following classes listen to this event (sorted from higher priority to lower +priority): + +Class | Priority | Method Called | Triggers | Description +---------------------------------|---------:|---------------|----------|------------ +`Zend\Mvc\View\Http\ViewManager` | 10000 | `onBootstrap` | none | Prepares the view layer (instantiate a `Zend\Mvc\View\Http\ViewManager`). + +### Triggered By + +This event is triggered by the following classes: + +Class | In Method +-----------------------|---------- +`Zend\Mvc\Application` | `bootstrap` + +## `MvcEvent::EVENT_ROUTE` ("route") + +### Listeners + +The following classes listen to this event (sorted from higher priority to lower +priority): + +Class | Priority | Method Called | Triggers | Description +-------------------------------|---------:|---------------|----------|------------ +`Zend\Mvc\ModuleRouteListener` | 1 | `onRoute` | none | Determines if the module namespace should be prepended to the controller name. This is the case if the route match contains a parameter key matching the `MODULE_NAMESPACE` constant. +`Zend\Mvc\RouteListener` | 1 | `onRoute` | `MvcEvent::EVENT_DISPATCH_ERROR` (if no route is matched) | Tries to match the request to the router and return a `RouteMatch` object. + +### Triggered By + +This event is triggered by the following classes: + +Class | In Method | Description +-----------------------|-----------|------------ +`Zend\Mvc\Application` | `run` | Uses a short circuit callback that allows halting propagation of the event if an error is raised during routing. + +## `MvcEvent::EVENT_DISPATCH` ("dispatch") + +### Listeners + +The following classes listen to this event (sorted from higher priority to lower +priority): + +#### Console context only + +The following listeners are only attached in a console context: + +Class | Priority | Method Called | Description +---------------------------------------------------------|---------:|-----------------------------|------------ +`Zend\Mvc\View\Console\InjectNamedConsoleParamsListener` | 1000 | `injectNamedParams` | Merge all params (route match params and params in the command), and add them to the `Request` object. +`Zend\Mvc\View\Console\CreateViewModelListener` | -80 | `createViewModelFromArray` | If the controller action returns an associative array, this listener casts it to a `ConsoleModel` object. +`Zend\Mvc\View\Console\CreateViewModelListener` | -80 | `createViewModelFromString` | If the controller action returns a string, this listener casts it to a `ConsoleModel` object. +`Zend\Mvc\View\Console\CreateViewModelListener` | -80 | `createViewModelFromNull` | If the controller action returns null, this listener casts it to a `ConsoleModel` object. +`Zend\Mvc\View\Console\InjectViewModelListener` | -100 | `injectViewModel` | Inserts the `ViewModel` (in this case, a `ConsoleModel`) and adds it to the MvcEvent object. It either (a) adds it as a child to the default, composed view model, or (b) replaces it if the result is marked as terminal. + +#### HTTP context only + +The following listeners are only attached in an HTTP context: + +Class | Priority | Method Called | Description +---------------------------------------------|---------:|----------------------------|------------ +`Zend\Mvc\View\Http\CreateViewModelListener` | -80 | `createViewModelFromArray` | If the controller action returns an associative array, this listener casts it to a `ViewModel` object. +`Zend\Mvc\View\Http\CreateViewModelListener` | -80 | `createViewModelFromNull` | If the controller action returns null, this listener casts it to a `ViewModel` object. +`Zend\Mvc\View\Http\RouteNotFoundStrategy` | -90 | `prepareNotFoundViewModel` | Creates and return a 404 `ViewModel`. +`Zend\Mvc\View\Http\InjectTemplateListener` | -90 | `injectTemplate` | Injects a template into the view model, if none present. Template name is derived from the controller found in the route match, and, optionally, the action, if present. +`Zend\Mvc\View\Http\InjectViewModelListener` | -100 | `injectViewModel` | Inserts the `ViewModel` (in this case, a `ViewModel`) and adds it to the `MvcEvent` object. It either (a) adds it as a child to the default, composed view model, or (b) replaces it if the result is marked as terminable. + +#### All contexts + +The following listeners are attached for all contexts: + +Class | Priority | Method Called | Triggers | Description +------------------------------|---------:|---------------|----------|------------ +`Zend\Mvc\DispatchListener` | 1 | `onDispatch` | `MvcEvent::EVENT_DISPATCH_ERROR` (if an exception is raised during dispatch processes) | Try to load the matched controller from the service manager (and throws various exceptions if it does not). +`Zend\Mvc\AbstractController` | 1 | `onDispatch` | none | The `onDispatch` method of the `AbstractController` is an abstract method. In `AbstractActionController`, for instance, it calls the action method. + +### Triggered By + +This event is triggered by the following classes: + +Class | In Method | Description +-----------------------------------------|-------------|------------ +`Zend\Mvc\Application` | `run` | Uses a short circuit callback to halt propagation of the event if an error is raised during routing. +`Zend\Mvc\Controller\AbstractController` | `dispatch` | If a listener returns a `Response` object, it halts propagation. Note: every `AbstractController` listens to this event and executes the `onDispatch` method when it is triggered. + +## `MvcEvent::EVENT_DISPATCH_ERROR` ("dispatch.error") + +### Listeners + +The following classes listen to this event (sorted from higher priority to lower +priority): + +#### Console context only + +The following listeners are only attached in a console context: + +Class | Priority | Method Called | Description +------------------------------------------------|---------:|-----------------------------|------------ +`Zend\Mvc\View\Console\RouteNotFoundStrategy` | 1 | `handleRouteNotFoundError ` | Detect if an error is a "route not found" condition. If a “controller not found” or “invalid controller” error type is encountered, sets the response status code to 404. +`Zend\Mvc\View\Console\ExceptionStrategy` | 1 | `prepareExceptionViewModel` | Create an exception view model, and sets the status code to 404. +`Zend\Mvc\View\Console\InjectViewModelListener` | -100 | `injectViewModel` | Inserts the `ViewModel` (in this case, a `ConsoleModel`) and adds it to the `MvcEvent` object. It either (a) adds it as a child to the default, composed view model, or (b) replaces it if the result is marked as terminable. + +#### HTTP context only + +The following listeners are only attached in an HTTP context: + +Class | Priority | Method Called | Description +---------------------------------------------|---------:|-----------------------------|------------ +`Zend\Mvc\View\Http\RouteNotFoundStrategy` | 1 | `detectNotFoundError` | Detect if an error is a 404 condition. If a “controller not found” or “invalid controller” error type is encountered, sets the response status code to 404. +`Zend\Mvc\View\Http\RouteNotFoundStrategy` | 1 | `prepareNotFoundViewModel` | Create and return a 404 view model. +`Zend\Mvc\View\Http\ExceptionStrategy` | 1 | `prepareExceptionViewModel` | Create an exception view model and set the status code to 404. +`Zend\Mvc\View\Http\InjectViewModelListener` | -100 | `injectViewModel` | Inserts the `ViewModel` (in this case, a `ViewModel`) and adds it to the MvcEvent object. It either (a) adds it as a child to the default, composed view model, or (b) replaces it if the result is marked as terminable. + +#### All contexts + +The following listeners are attached for all contexts: + +Class | Priority | Method Called | Description +----------------------------|---------:|----------------------|------------ +`Zend\Mvc\DispatchListener` | 1 | `reportMonitorEvent` | Used for monitoring when Zend Server is used. + +### Triggered By + +Class | In Method +----------------------------|---------- +`Zend\Mvc\DispatchListener` | `onDispatch` +`Zend\Mvc\DispatchListener` | `marshallControllerNotFoundEvent` +`Zend\Mvc\DispatchListener` | `marshallBadControllerEvent` + +## `MvcEvent::EVENT_RENDER` ("render") + +### Listeners + +The following classes listen to this event (sorted from higher priority to lower +priority): + +#### Console context only + +The following listeners are only attached in a console context: + +Class | Priority | Method Called | Description +-------------------------------------------------|---------:|---------------|------------ +`Zend\Mvc\View\Console\DefaultRenderingStrategy` | -10000 | `render` | Render the view. + +#### HTTP context only + +The following listeners are only attached in an HTTP context: + +Class | Priority | Method Called | Description +----------------------------------------------|---------:|---------------|------------ +`Zend\Mvc\View\Http\DefaultRenderingStrategy` | -10000 | `render` | Render the view. + +### Triggered By + +This event is triggered by the following classes: + +Class | In Method | Description +-----------------------|-------------------|------------ +`Zend\Mvc\Application` | `completeRequest` | This event is triggered just before the `MvcEvent::FINISH` event. + +## `MvcEvent::EVENT_RENDER_ERROR` ("render.error") + +### Listeners + +The following classes listen to this event (sorted from higher priority to lower +priority): + +#### Console context only + +The following listeners are only attached in a console context: + +Class | Priority | Method Called | Description +------------------------------------------------|---------:|-----------------------------|------------ +`Zend\Mvc\View\Console\ExceptionStrategy` | 1 | `prepareExceptionViewModel` | Create an exception view model and set the status code to 404. +`Zend\Mvc\View\Console\InjectViewModelListener` | -100 | `injectViewModel` | Inserts the `ViewModel` (in this case, a `ConsoleModel`) and adds it to the `MvcEvent` object. It either (a) adds it as a child to the default, composed view model, or (b) replaces it if the result is marked as terminable. + +#### HTTP context only + +The following listeners are only attached in an HTTP context: + +Class | Priority | Method Called | Description +------------------------------------------------|---------:|-----------------------------|------------ +`Zend\Mvc\View\Console\ExceptionStrategy` | 1 | `prepareExceptionViewModel` | Create an exception view model and set the status code to 404. +`Zend\Mvc\View\Console\InjectViewModelListener` | | | + +### Triggered By + +This event is triggered by the following classes: + +Class | In Method | Description +----------------------------------------------|-----------|------------ +`Zend\Mvc\View\Http\DefaultRenderingStrategy` | `render` | This event is triggered if an exception is raised during rendering. + +## `MvcEvent::EVENT_FINISH` ("finish") + +### Listeners + +The following classes listen to this event (sorted from higher priority to lower +priority): + +Class | Priority | Method Called | Description +--------------------------------|---------:|----------------|------------ +`Zend\Mvc\SendResponseListener` | -10000 | `sendResponse` | Triggers the `SendResponseEvent` in order to prepare the response (see the next chapter for more information about `SendResponseEvent`). + +### Triggered By + +This event is triggered by the following classes: + +Class | In Method | Description +-----------------------|-------------------|------------ +`Zend\Mvc\Application` | `run` | This event is triggered once the `MvcEvent::ROUTE` event returns a correct `ResponseInterface`. +`Zend\Mvc\Application` | `run` | This event is triggered once the `MvcEvent::DISPATCH` event returns a correct `ResponseInterface`. +`Zend\Mvc\Application` | `completeRequest` | This event is triggered after `MvcEvent::RENDER` (at this point, the view is already rendered). diff --git a/doc/book/plugins.md b/doc/book/plugins.md new file mode 100644 index 000000000..e25b1f84b --- /dev/null +++ b/doc/book/plugins.md @@ -0,0 +1,507 @@ +# Controller Plugins + +When using any of the abstract controller implementations shipped with zend-mvc, +or if you implement the `setPluginManager` method in your custom controllers, +you have access to a number of pre-built plugins. Additionally, you can register +your own custom plugins with the manager. + +The built-in plugins are: + +- [Zend\\Mvc\\Controller\\Plugin\\AcceptableViewModelSelector](#acceptableviewmodelselector-plugin) +- [Zend\\Mvc\\Controller\\Plugin\\FlashMessenger](#flashmessenger-plugin) +- [Zend\\Mvc\\Controller\\Plugin\\Forward](#forward-plugin) +- [Zend\\Mvc\\Controller\\Plugin\\Identity](#identity-plugin) +- [Zend\\Mvc\\Controller\\Plugin\\Layout](#layout-plugin) +- [Zend\\Mvc\\Controller\\Plugin\\Params](#params-plugin) +- [Zend\\Mvc\\Controller\\Plugin\\PostRedirectGet](#postredirectget-plugin) +- [Zend\\Mvc\\Controller\\Plugin\\FilePostRedirectGet](#file-postredirectget-plugin) +- [Zend\\Mvc\\Controller\\Plugin\\Redirect](#redirect-plugin) +- [Zend\\Mvc\\Controller\\Plugin\\Url](#url-plugin) + +If your controller implements the `setPluginManager()`, `getPluginManager()` and +`plugin()` methods, you can access these using their shortname via the `plugin()` +method: + +```php +$plugin = $this->plugin('url'); +``` + +For an extra layer of convenience, this shipped abstract controller +implementations have `__call()` methods defined that allow you to retrieve +plugins via method calls: + +```php +$plugin = $this->url(); +``` + +## AcceptableViewModelSelector Plugin + +The `AcceptableViewModelSelector` is a helper that can be used to select an +appropriate view model based on user defined criteria will be tested against the +Accept header in the request. + +As an example: + +```php +use Zend\Mvc\Controller\AbstractActionController; +use Zend\View\Model\JsonModel; + +class SomeController extends AbstractActionController +{ + protected $acceptCriteria = [ + 'Zend\View\Model\JsonModel' => [ + 'application/json', + ], + 'Zend\View\Model\FeedModel' => [ + 'application/rss+xml', + ], + ]; + + public function apiAction() + { + $viewModel = $this->acceptableViewModelSelector($this->acceptCriteria); + + // Potentially vary execution based on model returned + if ($viewModel instanceof JsonModel) { + // ... + } + } +} +``` + +The above would return a standard `Zend\View\Model\ViewModel` instance if the +criteria is not met, and the specified view model types if the specific criteria +is met. Rules are matched in order, with the first match "winning." + +## FlashMessenger Plugin + +The `FlashMessenger` is a plugin designed to create and retrieve self-expiring, +session-based messages. It exposes a number of methods: + +- `setSessionManager(Zend\Session\ManagerInterface $manager) : FlashMessenger`: + Allows you to specify an alternate session manager, if desired. + +- `getSessionManager() : Zend\Session\ManagerInterface`: Allows you to retrieve + the session manager registered. + +- `getContainer() : Zend\Session\Container`: Returns the + `Zend\Session\Container` instance in which the flash messages are stored. + +- `setNamespace(string $namespace = 'default') : FlashMessenger`: + Allows you to specify a specific namespace in the container in which to store + or from which to retrieve flash messages. + +- `getNamespace() : string`: retrieves the name of the flash message namespace. + +- `addMessage(string $message) : FlashMessenger`: Allows you to add a message to + the current namespace of the session container. + +- `hasMessages() : bool`: Lets you determine if there are any flash messages + from the current namespace in the session container. + +- `getMessages() : array`: Retrieves the flash messages from the current + namespace of the session container + +- `clearMessages() : bool`: Clears all flash messages in current namespace of + the session container. Returns `true` if messages were cleared, `false` if + none existed. + +- `hasCurrentMessages() : bool`: Indicates whether any messages were added + during the current request. + +- `getCurrentMessages() : array`: Retrieves any messages added during the + current request. + +- `clearCurrentMessages() : bool`: Removes any messages added during the current + request. Returns `true` if current messages were cleared, `false` if none + existed. + +- `clearMessagesFromContainer() : bool`: Clear all messages from the container. + Returns `true` if any messages were cleared, `false` if none existed. + +This plugin also provides four meaningful namespaces, namely: `INFO`, `ERROR`, +`WARNING`, and `SUCCESS`. The following functions are related to these +namespaces: + +- `addInfoMessage(string $message): FlashMessenger`: Add a message to "info" + namespace. + +- `hasCurrentInfoMessages() : bool`: Check to see if messages have been added to + "info" namespace within this request. + +- `addWarningMessage(string $message) : FlashMessenger`: Add a message to + "warning" namespace. + +- `hasCurrentWarningMessages() : bool`: Check to see if messages have been added + to "warning" namespace within this request. + +- `addErrorMessage(string $message) : FlashMessenger`: Add a message to "error" + namespace. + +- `hasCurrentErrorMessages() : bool`: Check to see if messages have been added + to "error" namespace within this request. + +- `addSuccessMessage(string $message) : FlashMessenger`: Add a message to + "success" namespace. + +- `hasCurrentSuccessMessages() :bool`: Check to see if messages have been added + to "success" namespace within this request. + +Additionally, the `FlashMessenger` implements both `IteratorAggregate` and +`Countable`, allowing you to iterate over and count the flash messages in the +current namespace within the session container. + +### Examples + +```php +public function processAction() +{ + // ... do some work ... + $this->flashMessenger()->addMessage('You are now logged in.'); + return $this->redirect()->toRoute('user-success'); +} + +public function successAction() +{ + $return = ['success' => true]; + $flashMessenger = $this->flashMessenger(); + if ($flashMessenger->hasMessages()) { + $return['messages'] = $flashMessenger->getMessages(); + } + return $return; +} +``` + +## Forward Plugin + +Occasionally, you may want to dispatch additional controllers from within the +matched controller. For example, you might use this approach to build up +"widgetized" content. The `Forward` plugin helps enable this. + +For the `Forward` plugin to work, the controller calling it must be +`ServiceLocatorAware`; otherwise, the plugin will be unable to retrieve a +configured and injected instance of the requested controller. + +The plugin exposes a single method, `dispatch()`, which takes two arguments: + +- `$name`, the name of the controller to invoke. This may be either the fully + qualified class name, or an alias defined and recognized by the + `ServiceManager` instance attached to the invoking controller. +- `$params` is an optional array of parameters with which to seed a `RouteMatch` + object for purposes of this specific request. Meaning the parameters will be + matched by their key to the routing identifiers in the config (otherwise + non-matching keys are ignored) + +`Forward` returns the results of dispatching the requested controller; it is up +to the developer to determine what, if anything, to do with those results. One +recommendation is to aggregate them in any return value from the invoking +controller. + +As an example: + +```php +$foo = $this->forward()->dispatch('foo', ['action' => 'process']); +return [ + 'somekey' => $somevalue, + 'foo' => $foo, +]; +``` + +## Identity Plugin + +The `Identity` plugin allows retrieving the identity from the +`AuthenticationService`. + +For the `Identity` plugin to work, a `Zend\Authentication\AuthenticationService` +name or alias must be defined and recognized by the `ServiceManager`. + +`Identity` returns the identity in the `AuthenticationService` or `null` if no +identity is available. + +As an example: + +```php +public function testAction() +{ + if ($user = $this->identity()) { + // someone is logged ! + } else { + // not logged in + } +} +``` + +When invoked, the `Identity` plugin will look for a service by the name or alias +`Zend\Authentication\AuthenticationService` in the `ServiceManager`. You can +provide this service to the `ServiceManager` in a configuration file: + +```php +// In a configuration file... +use Zend\Authentication\AuthenticationService; + +return [ + 'service_manager' => [ + 'aliases' => [ + AuthenticationService::class => 'my_auth_service', + ], + 'invokables' => [ + 'my_auth_service' => AuthenticationService::class, + ], + ], +]; +``` + +The `Identity` plugin exposes two methods: + +- `setAuthenticationService(AuthenticationService $authenticationService) : void`: + Sets the authentication service instance to be used by the plugin. + +- `getAuthenticationService() : AuthenticationService`: Retrieves the current + authentication service instance if any is attached. + +## Layout Plugin + +The `Layout` plugin allows changing layout templates from within controller actions. + +It exposes a single method, `setTemplate()`, which takes one argument, +`$template`, the name of the template to set. + +As an example: + +```php +$this->layout()->setTemplate('layout/newlayout'); +``` + +It also implements the `__invoke` magic method, which allows calling the plugin +as a method call: + +```php +$this->layout('layout/newlayout'); +``` + +## Params Plugin + +The `Params` plugin allows accessing parameters in actions from different sources. + +It exposes several methods, one for each parameter source: + +- `fromFiles(string $name = null, mixed $default = null): array|ArrayAccess|null`: + For retrieving all or one single **file**. If `$name` is null, all files will + be returned. + +- `fromHeader(string $header = null, mixed $default = null) : null|Zend\Http\Header\HeaderInterface`: + For retrieving all or one single **header** parameter. If `$header` is null, + all header parameters will be returned. + +- `fromPost(string $param = null, mixed $default = null) : mixed`: For + retrieving all or one single **post** parameter. If `$param` is null, all post + parameters will be returned. + +- `fromQuery(string $param = null, mixed $default = null) : mixed`: For + retrieving all or one single **query** parameter. If `$param` is null, all + query parameters will be returned. + +- `fromRoute(string $param = null, mixed $default = null) : mixed`: For + retrieving all or one single **route** parameter. If `$param` is null, all + route parameters will be returned. + +The plugin also implements the `__invoke` magic method, providing a shortcut +for invoking the `fromRoute` method: + +```php +$this->params()->fromRoute('param', $default); +// or +$this->params('param', $default); +``` + +## Post/Redirect/Get Plugin + +When a user sends a POST request (e.g. after submitting a form), their browser +will try to protect them from sending the POST again, breaking the back button, +causing browser warnings and pop-ups, and sometimes reposting the form. Instead, +when receiving a POST, we should store the data in a session container and +redirect the user to a GET request. + +This plugin can be invoked with two arguments: + +- `$redirect`, a string containing the redirect location, which can either be a + named route or a URL, based on the contents of the second parameter. +- `$redirectToUrl`, a boolean that when set to `TRUE`, causes the first + parameter to be treated as a URL instead of a route name (this is required + when redirecting to a URL instead of a route). This argument defaults to + `false`. + +When no arguments are provided, the current matched route is used. + +### Example Usage + +```php +// Pass in the route/url you want to redirect to after the POST +$prg = $this->prg('/user/register', true); + +if ($prg instanceof \Zend\Http\PhpEnvironment\Response) { + // Returned a response to redirect us. + return $prg; +} + +if ($prg === false) { + // This wasn't a POST request, but there were no params in the flash + // messenger; this is probably the first time the form was loaded. + return ['form' => $myForm]; +} + +// $prg is an array containing the POST params from the previous request +$form->setData($prg); + +// ... your form processing code here +``` + +## File Post/Redirect/Get Plugin + +While similar to the [Post/Redirect/Get Plugin](#postredirectget-plugin), +the File PRG Plugin will work for forms with file inputs. The difference is in +the behavior: The File PRG Plugin will interact directly with your form instance +and the file inputs, rather than *only* returning the POST params from the +previous request. + +By interacting directly with the form, the File PRG Plugin will turn off any +file inputs `required` flags for already uploaded files (for a partially valid +form state), as well as run the file input filters to move the uploaded files +into a new location (configured by the user). + +> ### Files must be relocated on upload +> +> You **must** attach a filter for moving the uploaded files to a new location, such as the +> [RenameUpload Filter](http://zendframework.github.io/zend-filter/file/#renameupload), +> or else your files will be removed upon the redirect. + +This plugin is invoked with three arguments: + +- `$form`: the form instance. +- `$redirect`: (Optional) a string containing the redirect location, which can + either be a named route or a URL, based on the contents of the third + parameter. If this argument is not provided, it will default to the current + matched route. +- `$redirectToUrl`: (Optional) a boolean that when set to `TRUE`, causes the + second parameter to be treated as a URL instead of a route name (this is + required when redirecting to a URL instead of a route). This argument defaults + to `false`. + +### Example Usage + +```php +$myForm = new Zend\Form\Form('my-form'); +$myForm->add([ + 'type' => 'Zend\Form\Element\File', + 'name' => 'file', +]); + +// NOTE: Without a filter to move the file, +// our files will disappear between the requests +$myForm->getInputFilter()->getFilterChain()->attach( + new Zend\Filter\File\RenameUpload([ + 'target' => './data/tmpuploads/file', + 'randomize' => true, + ]) +); + +// Pass in the form and optional the route/url you want to redirect to after the POST +$prg = $this->fileprg($myForm, '/user/profile-pic', true); + +if ($prg instanceof \Zend\Http\PhpEnvironment\Response) { + // Returned a response to redirect us. + return $prg; +} + +if ($prg === false) { + // First time the form was loaded. + return array('form' => $myForm); +} + +// Form was submitted. +// $prg is now an array containing the POST params from the previous request, +// but we don't have to apply it to the form since that has already been done. + +// Process the form +if ($form->isValid()) { + // ...Save the form... + return $this->redirect()->toRoute('/user/profile-pic/success'); +} + +// Form not valid, but file uploads might be valid and uploaded +$fileErrors = $form->get('file')->getMessages(); +if (empty($fileErrors)) { + $tempFile = $form->get('file')->getValue(); +} +``` + +## Redirect Plugin + +Redirections are quite common operations within applications. If done manually, +you will need to do the following steps: + +- Assemble a url using the router. +- Create and inject a "Location" header into the `Response` object, pointing to + the assembled URL. +- Set the status code of the `Response` object to one of the 3xx HTTP statuses. + +The `Redirect` plugin does this work for you. It offers three methods: + +- `toRoute(string $route = null, array $params = array(), array $options = array(), boolean $reuseMatchedParams = false) : Zend\Http\Response`: + Redirects to a named route, using the provided `$params` and `$options` to + assembled the URL. + +- `toUrl(string $url) : Zend\Http\Response`: Simply redirects to the given URL. + +- `refresh() : Zend\Http\Response`: Refresh to current route. + +In each case, the `Response` object is returned. If you return this immediately, +you can effectively short-circuit execution of the request. + +> ### Requires MvcEvent +> +> This plugin requires that the controller invoking it implements +> `InjectApplicationEventInterface`, and thus has an `MvcEvent` composed, as it +> retrieves the router from the event object. + +As an example: + +```php +return $this->redirect()->toRoute('login-success'); +``` + +## Url Plugin + +You may need to generate URLs from route definitions within your controllers; +for example, to seed the view, generate headers, etc. While the `MvcEvent` +object composes the router, doing so manually would require this workflow: + +```php +$router = $this->getEvent()->getRouter(); +$url = $router->assemble($params, ['name' => 'route-name']); +``` + +The `Url` helper makes this slightly more convenient: + +```php +$url = $this->url()->fromRoute('route-name', $params); +``` + +The `fromRoute()` method is the only public method defined, and is used to +generate a URL string from the provided parameters. It has the following +signature: + +- `fromRoute(string $route = null, array $params = [], array $options = [], bool $reuseMatchedParams = false): string`, where: + - `$name`: the name of the route to use for URL generation. + - `$params`: Any parameter substitutions to use with the named route. + - `$options`: Options used by the router when generating the URL (e.g., `force_canonical`, `query`, etc.). + - `$reuseMatchedParams`: Whether or not to use route match parameters from the + current URL when generating the new URL. This will only affect cases where + the specified `$name` matches the currently matched route; the default is + `true`. + +> ### Requires MvcEvent +> +> This plugin requires that the controller invoking it implements +> `InjectApplicationEventInterface`, and thus has an `MvcEvent` composed, as it +> retrieves the router from the event object. diff --git a/doc/book/quick-start.md b/doc/book/quick-start.md new file mode 100644 index 000000000..39055ea36 --- /dev/null +++ b/doc/book/quick-start.md @@ -0,0 +1,335 @@ +# Quick Start + +Now that you have basic knowledge of applications, modules, and how they are +each structured, we'll show you the easy way to get started. + +## Install the Zend Skeleton Application + +The easiest way to get started is to install the skeleton application via +Composer. + +If you have not yet done so, [install Composer](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx). + +Once you have, use the `create-project` command to create a new application: + +```bash +$ git create-project -sdev zendframework/skeleton-application my-application +``` + +## Create a New Module + +By default, one module is provided with the `ZendSkeletonApplication`, named +"Application". It provides a controller to handle the "home" page of the +application, the layout template, and templates for 404 and error pages. + +Typically, you will not need to touch this other than to provide an alternate +entry page for your site and/or alternate error page. + +Additional functionality will be provided by creating new modules. + +To get you started with modules, we recommend using the `ZendSkeletonModule` as +a base. Download it from here: + +* [ZendSkeletonModule zip package](https://github.com/zendframework/ZendSkeletonModule/zipball/master) +* [ZendSkeletonModule tarball](https://github.com/zendframework/ZendSkeletonModule/tarball/master) + +Deflate the package, and rename the directory "ZendSkeletonModule" to reflect +the name of the new module you want to create; when done, move the module into +your new project's `module/` directory. + +At this point, it's time to create some functionality. + +## Update the Module Class + +Let's update the `Module` class. We'll want to make sure the namespace is correct, +configuration is enabled and returned, and that we setup autoloading on +initialization. Since we're actively working on this module, the class list will +be in flux; we probably want to be pretty lenient in our autoloading approach, +so let's keep it flexible by using the `StandardAutoloader`. Let's begin. + +First, let's have `autoload_classmap.php` return an empty array: + +```php + array( + 'template_path_stack' => array( + '' => __DIR__ . '/../view' + ), + ), +); +``` + +Fill in `module-name` with a lowercased, dash-separated version of your module +name; e.g., "ZendUser" would become "zend-user". + +Next, edit the namespace declaration of the `Module.php` file. Replace the +following line: + +```php +namespace ZendSkeletonModule; +``` + +with the namespace you want to use for your application. + +Next, rename the directory `src/ZendSkeletonModule` to `src/`, +and the directory `view/zend-skeleton-module` to `src/`. + +At this point, you now have your module configured properly. Let's create a +controller! + +## Create a Controller + +Controllers are objects that implement `Zend\Stdlib\DispatchableInterface`. This +means they need to implement a `dispatch()` method that takes minimally a +`Request` object as an argument. + +In practice, though, this would mean writing logic to branch based on matched +routing within every controller. As such, we've created several base controller +classes for you to start with: + +- `Zend\Mvc\Controller\AbstractActionController` allows routes to match an + "action". When matched, a method named after the action will be called by the + controller. As an example, if you had a route that returned "foo" for the + "action" key, the "fooAction" method would be invoked. +- `Zend\Mvc\Controller\AbstractRestfulController` introspects the `Request` to + determine what HTTP method was used, and calls a method according to that. + - `GET` will call either the `getList()` method, or, if an "id" was matched + during routing, the `get()` method (with that identifer value). + - `POST` will call the `create()` method, passing in the `$_POST` values. + - `PUT` expects an "id" to be matched during routing, and will call the + `update()` method, passing in the identifier, and any data found in the raw + post body. + - `DELETE` expects an "id" to be matched during routing, and will call the + `delete()` method. +- `Zend\Mvc\Controller\AbstractConsoleController` extends from + `AbstractActionController`, but provides methods for retrieving the + `Zend\Console\Adapter\AdapterInterface` instance, and ensuring that execution + fails in non-console environments. + +To get started, we'll create a "hello world"-style controller, with a single +action. First, create the file `HelloController.php` in the directory +`src//Controller`. Edit it in your favorite text editor or IDE, +and insert the following contents: + +```php +\Controller; + +use Zend\Mvc\Controller\AbstractActionController; +use Zend\View\Model\ViewModel; + +class HelloController extends AbstractActionController +{ + public function worldAction() + { + $message = $this->params()->fromQuery('message', 'foo'); + return new ViewModel(['message' => $message]); + } +} +``` + +So, what are we doing here? + +- We're creating an action controller. +- We're defining an action, "world". +- We're pulling a message from the query parameters (yes, this is a superbly bad + idea in production! Always sanitize your inputs!). +- We're returning a `ViewModel` with an array of values to be processed later. + +We return a `ViewModel`. The view layer will use this when rendering the view, +pulling variables and the template name from it. By default, you can omit the +template name, and it will resolve to "lowercase-controller-name/lowercase-action-name". +However, you can override this to specify something different by calling +`setTemplate()` on the `ViewModel` instance. Typically, templates will resolve +to files with a ".phtml" suffix in your module's `view` directory. + +So, with that in mind, let's create a view script. + +## Create a View Script + +Create the directory `view//hello`. Inside that directory, create a +file named `world.phtml`. Inside that, paste in the following: + +```php +

Greetings!

+ +

You said "escapeHtml($message) ?>".

+``` + +That's it. Save the file. + +> ### Escaping output +> +> What is the method `escapeHtml()`? It's actually a [view helper](http://framework.zend.com/manual/current/en/modules/zend.view.helpers.html), +> and it's designed to help mitigate XSS attacks. Never trust user input; if you +> are at all uncertain about the source of a given variable in your view script, +> escape it using one of the provided escape view helpers depending on the type +> of data you have. + +## View scripts for module names with subnamespaces + +As per PSR-0, modules should be named following the rule: `\\*`. +However, the default controller class to template mapping does not work very +well with those: it will remove subnamespace. + +To address that issue, new mapping rules were introduced with version 2.3.0. To +maintain backwards compatibility, that mapping is not enabled by default. To +enable it, you need to add your module namespace to a whitelist in your module +configuration: + +```php +'view_manager' => array( + // Controller namespace to template map + // or whitelisting for controller FQCN to template mapping + 'controller_map' => array( + '' => true, + ), +), +``` + +Once you have, you can create the directory `view///hello`. Inside +that directory, create a file named `world.phtml`. Inside that, paste the +following: + +```php +

Greetings!

+ +

You said "escapeHtml($message) ?>".

+``` + +## Create a Route + +Now that we have a controller and a view script, we need to create a route to it. + +> ### Default routing +> +> `ZendSkeletonModule` ships with a "default route" that will likely get +> you to this action. That route is defined roughly as +> `/{module}/{controller}/{action}`, which means that the path +> `/zend-user/hello/world` will map to `ZendUser\Controller\HelloController::worldAction()` +> (assuming the module name were `ZendUser`). +> +> We're going to create an explicit route in this example, as +> creating explicit routes is a recommended practice. The application will look for a +> `Zend\Mvc\Router\RouteStackInterface` instance to setup routing. The default generated router is a +> `Zend\Mvc\Router\Http\TreeRouteStack`. +> +> To use the "default route" functionality, you will need to edit the shipped +> routing definition in the module's `config/module.config.php`, and replace: +> +> - `/module-specific-root` with a module-specific root path. +> - `ZendSkeletonModule\Controller` with `\Controller`. + +Additionally, we need to tell the application we have a controller: + +```php +// module.config.php +return [ + 'controllers' => [ + 'invokables' => [ + '\Controller\Index' => '\Controller\IndexController', + // Do similar for each other controller in your module + ], + ], + // ... other configuration ... +]; +``` + +> ### Controller services +> +> We inform the application about controllers we expect to have in the +> application. This is to prevent somebody requesting any service the +> `ServiceManager` knows about in an attempt to break the application. The +> dispatcher uses a special, scoped container that will only pull controllers +> that are specifically registered with it, either as invokable classes or via +> factories. + +Open your `config/module.config.php` file, and modify it to add to the "routes" +and "controller" parameters so it reads as follows: + +```php +return [ + 'router' => [ + 'routes' => [ + '-hello-world' => [ + 'type' => 'Literal', + 'options' => [ + 'route' => '/hello/world', + 'defaults' => [ + 'controller' => '\Controller\Hello', + 'action' => 'world', + ], + ], + ], + ], + ], + 'controllers' => [ + 'invokables' => [ + '\Controller\Hello' => '\Controller\HelloController', + ], + ], + // ... other configuration ... +]; +``` + +## Tell the Application About our Module + +One problem: we haven't told our application about our new module! + +By default, modules are not utilized unless we tell the module manager about them. +As such, we need to notify the application about them. + +Remember the `config/application.config.php` file? Let's modify it to add our +new module. Once done, it should read as follows: + +```php + array( + 'Application', + '', + ), + 'module_listener_options' => array( + 'module_paths' => array( + './module', + './vendor', + ), + ), +); +``` + +Replace `` with the namespace of your module. + +## Test it Out! + +Now we can test things out! Create a new vhost pointing its document root to the `public` directory +of your application, and fire it up in a browser. You should see the default homepage template of +ZendSkeletonApplication. + +Now alter the location in your URL to append the path "/hello/world", and load the page. You should +now get the following content: + +```html +

Greetings!

+ +

You said "foo".

+``` + +Now alter the location to append "?message=bar" and load the page. You should now get: + +```html +

Greetings!

+ +

You said "bar".

+``` + +Congratulations! You've created your first ZF2 MVC module! diff --git a/doc/book/routing.md b/doc/book/routing.md new file mode 100644 index 000000000..494001dec --- /dev/null +++ b/doc/book/routing.md @@ -0,0 +1,725 @@ +# Routing + +Routing is the act of matching a request to a given controller. + +Typically, routing will examine the request URI, and attempt to match the URI +path segment against provided constraints. If the constraints match, a set of +"matches" are returned, one of which should be the controller name to execute. +Routing can utilize other portions of the request URI or environment as well. +For example, the host or scheme, query parameters, headers, request method, and +more. + +The base unit of routing is a `Route`: + +```php +namespace Zend\Mvc\Router; + +use Zend\Stdlib\RequestInterface as Request; + +interface RouteInterface +{ + public static function factory(array $options = []); + public function match(Request $request); + public function assemble(array $params = [], array $options = []); +} +``` + +A `Route` accepts a `Request`, and determines if it matches. If so, it returns a +`RouteMatch` object: + +```php +namespace Zend\Mvc\Router; + +class RouteMatch +{ + public function __construct(array $params); + public function setMatchedRouteName($name); + public function getMatchedRouteName(); + public function setParam($name, $value); + public function getParams(); + public function getParam($name, $default = null); +} +``` + +Typically, when a `Route` matches, it will define one or more parameters. These +are passed into the `RouteMatch`, and objects may query the `RouteMatch` for +their values. + +```php +$id = $routeMatch->getParam('id', false); +if (! $id) { + throw new Exception('Required identifier is missing!'); +} +$entity = $resource->get($id); +``` + +Usually you will have multiple routes you wish to test against. In order to +facilitate this, you will use a route aggregate, usually implementing +`RouteStack`: + +```php +namespace Zend\Mvc\Router; + +interface RouteStackInterface extends RouteInterface +{ + public function addRoute($name, $route, $priority = null); + public function addRoutes(array $routes); + public function removeRoute($name); + public function setRoutes(array $routes); +} +``` + +Routes will be queried in a LIFO order, and hence the reason behind the name +`RouteStack`. zend-mvc provides two implementations of this interface, +`SimpleRouteStack` and `TreeRouteStack`. In each, you register routes either one +at a time using `addRoute()`, or in bulk using `addRoutes()`. + +```php +// One at a time: +$route = Literal::factory([ + 'route' => '/foo', + 'defaults' => [ + 'controller' => 'foo-index', + 'action' => 'index', + ], +]); +$router->addRoute('foo', $route); + +// In bulk: +$router->addRoutes([ + // using already instantiated routes: + 'foo' => $route, + + // providing configuration to allow lazy-loading routes: + 'bar' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/bar', + 'defaults' => [ + 'controller' => 'bar-index', + 'action' => 'index', + ], + ], + ], +]); +``` + +## Router Types + +Two routers are provided, the `SimpleRouteStack` and `TreeRouteStack`. Each +works with the above interface, but utilize slightly different options and +execution paths. By default, the zend-mvc uses the `TreeRouteStack` as the +router. + +### SimpleRouteStack + +This router takes individual routes that provide their full matching logic in +one go, and loops through them in LIFO order until a match is found. As such, +routes that will match most often should be registered last, and least common +routes first. Additionally, you will need to ensure that routes that potentially +overlap are registered such that the most specific match will match first (i.e., +register later). Alternatively, you can set priorities by giving the priority as +third parameter to the `addRoute()` method, specifying the priority in the route +specifications or setting the priority property within a route instance before +adding it to the route stack. + +### TreeRouteStack + +`Zend\Mvc\Router\Http\TreeRouteStack` provides the ability to register trees of +routes, and uses a B-tree algorithm to match routes. As such, you register a +single route with many children. + +A `TreeRouteStack` will consist of the following configuration: + +- A base "route", which describes the base match needed, the root of the tree. +- An optional `route_plugins`, which is a configured + `Zend\Mvc\Router\RoutePluginManager` that can lazy-load routes. +- The option `may_terminate`, which hints to the router that no other segments + will follow it. +- An optional `child_routes` array, which contains additional routes that stem + from the base "route" (i.e., build from it). Each child route can itself be a + `TreeRouteStack` if desired; in fact, the `Part` route works exactly this way. + +When a route matches against a `TreeRouteStack`, the matched parameters from +each segment of the tree will be returned. + +A `TreeRouteStack` can be your sole route for your application, or describe +particular path segments of the application. + +An example of a `TreeRouteStack` is provided in the documentation of the `Part` +route. + +## HTTP Route Types + +zend-mvc ships with the following HTTP route types. + +### Zend\\Mvc\\Router\\Http\\Hostname + +The `Hostname` route attempts to match the hostname registered in the request +against specific criteria. Typically, this will be in one of the following +forms: + +* `subdomain.domain.tld` +* `:subdomain.domain.tld` + +In the above, the second route would return a "subdomain" key as part of the +route match. + +For any given hostname segment, you may also provide a constraint. As an +example, if the "subdomain" segment needed to match only if it started with "fw" +and contained exactly 2 digits following, the following route would be needed: + +```php +$route = Hostname::factory([ + 'route' => ':subdomain.domain.tld', + 'constraints' => [ + 'subdomain' => 'fw\d{2}', + ], +]); +``` + +In the above example, only a "subdomain" key will be returned in the +`RouteMatch`. If you wanted to also provide other information based on matching, +or a default value to return for the subdomain, you need to also provide +defaults. + +```php +$route = Hostname::factory([ + 'route' => ':subdomain.domain.tld', + 'constraints' => [ + 'subdomain' => 'fw\d{2}', + ], + 'defaults' => [ + 'type' => 'json', + ], +]); +``` + +When matched, the above will return two keys in the `RouteMatch`, "subdomain" +and "type". + +### Zend\\Mvc\\Router\\Http\\Literal + +The `Literal` route is for doing exact matching of the URI path. Configuration +therefore is solely the path you want to match, and the "defaults", or +parameters you want returned on a match. + +```php +$route = Literal::factory([ + 'route' => '/foo', + 'defaults' => [ + 'controller' => 'Application\Controller\IndexController', + 'action' => 'foo', + ], +]); +``` + +The above route would match a path "/foo", and return the key "action" in the +`RouteMatch`, with the value "foo". + +### Zend\\Mvc\\Router\\Http\\Method + +The `Method` route is used to match the HTTP method or 'verb' specified in the +request (See RFC 2616 Sec. 5.1.1). It can optionally be configured to match +against multiple methods by providing a comma-separated list of method tokens. + +```php +$route = Method::factory([ + 'verb' => 'post,put', + 'defaults' => [ + 'controller' => 'Application\Controller\IndexController', + 'action' => 'form-submit', + ], +]); +``` + +The above route would match an http "POST" or "PUT" request and return a +`RouteMatch` object containing a key "action" with a value of "form-submit". + +### Zend\\Mvc\\Router\\Http\\Part + +A `Part` route allows crafting a tree of possible routes based on segments of +the URI path. It actually extends the `TreeRouteStack`. + +`Part` routes are difficult to describe, so we'll simply provide a sample one +here. + +```php +$route = Part::factory([ + 'route' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/', + 'defaults' => [ + 'controller' => 'Application\Controller\IndexController', + 'action' => 'index', + ], + ], + ], + 'route_plugins' => $routePlugins, + 'may_terminate' => true, + 'child_routes' => [ + 'blog' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/blog', + 'defaults' => [ + 'controller' => 'Application\Controller\BlogController', + 'action' => 'index', + ], + ], + 'may_terminate' => true, + 'child_routes' => [ + 'rss' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/rss', + 'defaults' => [ + 'action' => 'rss', + ] + ], + 'may_terminate' => true, + 'child_routes' => [ + 'subrss' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/sub', + 'defaults' => [ + 'action' => 'subrss', + ], + ], + ], + ], + ], + ], + ], + 'forum' => [ + 'type' => 'literal', + 'options' => [ + 'route' => 'forum', + 'defaults' => [ + 'controller' => 'Application\Controller\ForumController', + 'action' => 'index', + ], + ], + ], + ], +]); +``` + +The above would match the following: + +- `/` would load the "Index" controller, "index" action. +- `/blog` would load the "Blog" controller, "index" action. +- `/blog/rss` would load the "Blog" controller, "rss" action. +- `/blog/rss/sub` would load the "Blog" controller, "subrss" action. +- `/forum` would load the "Forum" controller, "index" action. + +You may use any route type as a child route of a `Part` route. + +> ### Part routes are an implementation detail +> +> `Part` routes are not meant to be used directly. When you add definitions for +> `child_routes` to any route type, that route will become a `Part` route. As +> already said, describing `Part` routes with words is difficult, so hopefully +> the additional [examples at the end](#http-routing-examples) will provide +> further insight. + +> ### Route plugins +> +> In the above example, the `$routePlugins` is an instance of +> `Zend\Mvc\Router\RoutePluginManager`, containing essentially the following +> configuration: +> +> ```php +> $routePlugins = new Zend\Mvc\Router\RoutePluginManager(); +> $plugins = [ +> 'hostname' => 'Zend\Mvc\Router\Http\Hostname', +> 'literal' => 'Zend\Mvc\Router\Http\Literal', +> 'part' => 'Zend\Mvc\Router\Http\Part', +> 'regex' => 'Zend\Mvc\Router\Http\Regex', +> 'scheme' => 'Zend\Mvc\Router\Http\Scheme', +> 'segment' => 'Zend\Mvc\Router\Http\Segment', +> 'wildcard' => 'Zend\Mvc\Router\Http\Wildcard', +> 'query' => 'Zend\Mvc\Router\Http\Query', +> 'method' => 'Zend\Mvc\Router\Http\Method', +> ]; +> foreach ($plugins as $name => $class) { +> $routePlugins->setInvokableClass($name, $class); +> } +> ``` +> +> When using `Zend\Mvc\Router\Http\TreeRouteStack`, the `RoutePluginManager` is +> set up by default, and the developer does not need to worry about autoloading +> of standard HTTP routes. + +### Zend\\Mvc\\Router\\Http\\Regex + +A `Regex` route utilizes a regular expression to match against the URI path. Any +valid regular expression is allowed; our recommendation is to use named captures +for any values you want to return in the `RouteMatch`. + +Since regular expression routes are often complex, you must specify a "spec" or +specification to use when assembling URLs from regex routes. The spec is simply +a string; replacements are identified using `%keyname%` within the string, with +the keys coming from either the captured values or named parameters passed to +the `assemble()` method. + +Just like other routes, the `Regex` route can accept "defaults", parameters to +include in the `RouteMatch` when successfully matched. + +```php +$route = Regex::factory([ + 'regex' => '/blog/(?[a-zA-Z0-9_-]+)(\.(?(json|html|xml|rss)))?', + 'defaults' => [ + 'controller' => 'Application\Controller\BlogController', + 'action' => 'view', + 'format' => 'html', + ], + 'spec' => '/blog/%id%.%format%', +]); +``` + +The above would match `/blog/001-some-blog_slug-here.html`, and return four +items in the `RouteMatch`, an "id", the "controller", the "action", and the +"format". When assembling a URL from this route, the "id" and "format" values +would be used to fill the specification. + +### Zend\\Mvc\\Router\\Http\\Scheme + +The `Scheme` route matches the URI scheme only, and must be an exact match. As +such, this route, like the `Literal` route, simply takes what you want to match +and the "defaults", parameters to return on a match. + +```php +$route = Scheme::factory([ + 'scheme' => 'https', + 'defaults' => [ + 'https' => true, + ], +]); +``` + +The above route would match the "https" scheme, and return the key "https" in +the `RouteMatch` with a boolean `true` value. + +### Zend\\Mvc\\Router\\Http\\Segment + +A `Segment` route allows matching any segment of a URI path. Segments are +denoted using a colon, followed by alphanumeric characters; if a segment is +optional, it should be surrounded by brackets. As an example, `/:foo[/:bar]` +would match a `/` followed by text and assign it to the key "foo"; if any +additional `/` characters are found, any text following the last one will be +assigned to the key "bar". + +The separation between literal and named segments can be anything. For example, +the above could be done as `/:foo{-}[-:bar]` as well. The `{-}` after the `:foo` +parameter indicates a set of one or more delimiters, after which matching of the +parameter itself ends. + +Each segment may have constraints associated with it. Each constraint should +simply be a regular expression expressing the conditions under which that +segment should match. + +Also, as you can in other routes, you may provide defaults to use; these are +particularly useful when using optional segments. + +As a complex example: + +```php +$route = Segment::factory([ + 'route' => '/:controller[/:action]', + 'constraints' => [ + 'controller' => '[a-zA-Z][a-zA-Z0-9_-]+', + 'action' => '[a-zA-Z][a-zA-Z0-9_-]+', + ], + 'defaults' => [ + 'controller' => 'Application\Controller\IndexController', + 'action' => 'index', + ], +]); +``` + +### Zend\\Mvc\\Router\\Http\\Query (Deprecated) + +> #### Potential security issue +> +> Misuse of this route can lead to potential security issues. + +> #### Deprecated +> +> This route part is deprecated since you can now add query parameters without a +> query route. + +The `Query` route part allows you to specify and capture query string parameters +for a given route. + +The intention of the `Query` part is that you do not instantiate it in its own +right, but use it as a child of another route part. + +An example of its usage would be: + +```php +$route = Part::factory([ + 'route' => [ + 'type' => 'literal', + 'options' => [ + 'route' => 'page', + 'defaults' => [], + ], + ], + 'may_terminate' => true, + 'route_plugins' => $routePlugins, + 'child_routes' => [ + 'query' => [ + 'type' => 'Query', + 'options' => [ + 'defaults' => [ + 'foo' => 'bar', + ], + ], + ], + ], +]); +``` + +This then allows you to create query strings using the url view helper. + +```php +$this->url( + 'page/query', + [ + 'name' => 'my-test-page', + 'format' => 'rss', + 'limit' => 10, + ] +); +``` + +Per the above example, you must add `/query` (the name we gave to our query +route segment) to your route name in order to append a query string. If you do +not specify `/query` in the route name, then no query string will be appended. + +Our example "page" route has only one defined parameter of "name" +(`/page[/:name]`), meaning that the remaining parameters of "format" and "limit" +will then be appended as a query string. + +The output from our example should then be +`/page/my-test-page?format=rss&limit=10` + +### Zend\\Mvc\\Router\\Http\\Wildcard (Deprecated) + +> #### Potential security issue +> +> Misuse of this route type can lead to potential security issues. + +> #### Deprecated +> +> This route type is deprecated. Use the `Segment` route type. + +The `Wildcard` route type matches all remaining segments of a URI path. + +## HTTP Routing Examples + +Most of the routing definitions will be done in module configuration files, so +the following examples will show how to set up routes in config files. + +### Simple example with two literal routes + +```php +return [ + 'router' => [ + 'routes' => [ + // Literal route named "home" + 'home' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/', + 'defaults' => [ + 'controller' => 'Application\Controller\IndexController', + 'action' => 'index', + ], + ], + ], + // Literal route named "contact" + 'contact' => [ + 'type' => 'literal', + 'options' => [ + 'route' => 'contact', + 'defaults' => [ + 'controller' => 'Application\Controller\ContactController', + 'action' => 'form', + ], + ], + ], + ], + ], +]; +``` + +### A complex example with child routes + +```php +return [ + 'router' => [ + 'routes' => [ + // Literal route named "home" + 'home' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/', + 'defaults' => [ + 'controller' => 'Application\Controller\IndexController', + 'action' => 'index', + ], + ], + ], + // Literal route named "blog", with child routes + 'blog' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/blog', + 'defaults' => [ + 'controller' => 'Application\Controller\BlogController', + 'action' => 'index', + ], + ], + 'may_terminate' => true, + 'child_routes' => [ + // Segment route for viewing one blog post + 'post' => [ + 'type' => 'segment', + 'options' => [ + 'route' => '/[:slug]', + 'constraints' => [ + 'slug' => '[a-zA-Z0-9_-]+', + ], + 'defaults' => [ + 'action' => 'view', + ], + ], + ], + // Literal route for viewing blog RSS feed + 'rss' => [ + 'type' => 'literal', + 'options' => [ + 'route' => '/rss', + 'defaults' => [ + 'action' => 'rss', + ], + ], + ], + ], + ], + ], + ], +]; +``` + +When using child routes, naming of the routes follows the `parent/child` +pattern, so to use the child routes from the above example: + +```php +echo $this->url('blog'); // gives "/blog" +echo $this->url('blog/post', ['slug' => 'my-post']); // gives "/blog/my-post" +echo $this->url('blog/rss'); // gives "/blog/rss" +``` + +### An example with multiple Hostnames and subdomains within a single application + +```php +return [ + 'router' => [ + 'routes' => [ + 'modules.zendframework.com' => [ + 'type' => 'Zend\Mvc\Router\Http\Hostname', + 'options' => [ + 'route' => ':4th.[:3rd.]:2nd.:1st', // domain levels from right to left + 'contraints' => [ + '4th' => 'modules', + '3rd' => '.*?', // optional 3rd level domain such as .ci, .dev or .test + '2nd' => 'zendframework', + '1st' => 'com', + ], + // Purposely omit default controller and action + // to let the child routes control the route match + ], + // child route controllers may span multiple modules as desired + 'child_routes' => [ + 'index' => [ + 'type' => 'Zend\Mvc\Router\Http\Literal', + 'options' => [ + 'route' => '/', + 'defaults' => [ + 'controller' => 'Module\Controller\Index', + 'action' = > 'index', + ], + ], + 'may_terminate' => true, + ], + ], + ], + 'packages.zendframework.com' => [ + 'type' => 'Zend\Mvc\Router\Http\Hostname', + 'options' => [ + 'route' => ':4th.[:3rd.]:2nd.:1st', // domain levels from right to left + 'contraints' => [ + '4th' => 'packages', + '3rd' => '.*?', // optional 3rd level domain such as .ci, .dev or .test + '2nd' => 'zendframework', + '1st' => 'com', + ], + // Purposely omit default controller and action + // to let the child routes control the route match + ], + // child route controllers may span multiple modules as desired + 'child_routes' => [ + 'index' => [ + 'type' => 'Zend\Mvc\Router\Http\Literal', + 'options' => [ + 'route' => '/', + 'defaults' => [ + 'controller' => 'Package\Controller\Index', + 'action' = > 'index', + ], + ], + 'may_terminate' => true, + ], + ], + ], + ], + ], +]; +``` + +The above would match the following: + +- `modules.zendframework.com` would dispatch the `Index` controller's `index` + action of the `Module` module. +- `modules.ci.zendframework.com` would dispatch the `Index` controller's `index` + action of the `Module` module. +- `packages.zendframework.com` would dispatch the `Index` controller's `index` + action of the `Package` module. +- `packages.dev.zendframework.com` would dispatch the `Index` controller's + `index` action of the `Package` module. + +The `Url` controller plugin or view helper may be used to generate URLs +following the above example: + +```php +// reuse the route matched parameters to generate URLs +echo $this->url('modules.zendframework.com/index', [], [], true); +echo $this->url('packages.zendframework.com/index', [], [], true); +``` + +> ### may_terminate and child_routes placement +> +> When defining child routes pay attention that the `may_terminate` and +> `child_routes` definitions are in same level as the `options` and `type` +> definitions. A common pitfall is to have those two definitions nested in +> `options`, which will not result in the desired routes. + +## Console Route Types + +zend-mvc also allows routing Console based applications; console routes are +explained in the [zend-console routing documentation](http://zendframework.github.io/zend-console/routes/). diff --git a/doc/book/send-response-event.md b/doc/book/send-response-event.md new file mode 100644 index 000000000..fe9d79e52 --- /dev/null +++ b/doc/book/send-response-event.md @@ -0,0 +1,33 @@ +# The SendResponse Event + +zend-mvc defines and utilizes a custom `Zend\EventManager\Event` for updating +the response object prior to emitting it, `Zend\Mvc\ResponseSender\SendResponseEvent`. +The event allows listeners to set response headers and content. + +The methods it defines are: + +- `setResponse($response)` +- `getResponse()` +- `setContentSent()` +- `contentSent()` +- `setHeadersSent()` +- `headersSent()` + +## Listeners + +Currently, three listeners are listening to this event at different priorities based on which +listener is used most. + +Class | Priority | Method Called | Description +------------------------------------------------------------ | -------: | ------------- | ----------- +`Zend\Mvc\SendResponseListener\PhpEnvironmentResponseSender` | -1000 | `__invoke` | This is used in HTTP contexts (this is the most often used). +`Zend\Mvc\SendResponseListener\ConsoleResponseSender` | -2000 | `__invoke` | This is used in console contexts. +`Zend\Mvc\SendResponseListener\SimpleStreamResponseSender` | -3000 | `__invoke` | + +Because each listener has negative priority, adding your own logic to modify the +`Response` involves adding a new listener without priority (as priority defaults +to 1); thus, your own listener will execute before any of the defaults. + +## Triggered By + +This event is executed when the `MvcEvent::FINISH` event is triggered, with a priority of -10000. diff --git a/doc/book/services.md b/doc/book/services.md new file mode 100644 index 000000000..ef895f77e --- /dev/null +++ b/doc/book/services.md @@ -0,0 +1,783 @@ +# Default Services + +The default and recommended way to write zend-mvc applications uses a set of +services defined in the `Zend\Mvc\Service` namespace. This chapter details what +each of those services are, the classes they represent, and the configuration +options available. + +Many of the services are provided by other components, and the factories and +abstract factories themselves are defined in the individual components. We will +cover those factories in this chapter, however, as usage is generally the same +between each. + +## Theory of Operation + +To allow easy configuration of all the different parts of the MVC system, a +somewhat complex set of services and their factories has been created. We'll try +to give a simplified explanation of the process. + +When a `Zend\Mvc\Application` is created, a `Zend\ServiceManager\ServiceManager` +object is created and configured via `Zend\Mvc\Service\ServiceManagerConfig`. +The `ServiceManagerConfig` gets the configuration from +`config/application.config.php` (or some other application configuration you +passed to the `Application` when creating it). From all the service and +factories provided in the `Zend\Mvc\Service` namespace, `ServiceManagerConfig` +is responsible of configuring only three: `SharedEventManager`, `EventManager`, +and `ModuleManager`. + +After this, the `Application` fetches the `ModuleManager`. At this point, the +`ModuleManager` further configures the `ServiceManager` with services and +factories provided in `Zend\Mvc\Service\ServiceListenerFactory`. This approach +allows us to keep the main application configuration concise, and to give the +developer the power to configure different parts of the MVC system from within +the modules, overriding any default configuration in these MVC services. + +## ServiceManager + +As a quick review, the following service types may be configured: + +- **Invokable services**, which map a service name to a class that has no + constructor or a constructor that accepts no arguments. +- **Factories**, which map a service name to a factory which will create and + return an object. A factory receives the service manager as an argument, and + may be any PHP callable, or a class or object that implements + `Zend\ServiceManager\FactoryInterface`. +- **Abstract factories**, which are factories that can create any number of + named services that share the same instantiation pattern; examples include + database adapters, cache adapters, loggers, etc. The factory receives the + service manager as an argument, the resolved service name, and the requested + service name; it **must** be a class or object implementing + `Zend\ServiceManager\AbstractFactoryInterface`. See the section on + [abstract factories](http://zendframework.github.io/zend-servicemanager/configuring-the-service-manager/#abstract-factories) + for configuration information. +- **Aliases**, which alias one service name to another. Aliases can also + reference other aliases. +- **Initializers**, which receive the newly created instance and the service + manager, and which can be used to perform additional initialization tasks. The + most common use case is to test the instance against specific "Aware" + interfaces, and, if matching, inject them with the appropriate service. +- **Delegators**, which typically *decorate* retrieval of a service to either + substitute an alternate service, decorate the created service, or perform + pre/post initialization tasks when creating a service. +- **Lazy services**, which are decorators for services with expensive + initialization; the service manager essentially returns a proxy service that + defers initialization until the first call is made to the service. +- **Plugin managers**, which are specialized service managers used to manage + objects that are of a related type, such as view helpers, controller plugins, + controllers, etc. Plugin managers accept configuration just like service + managers, and as such can compose each of the service types listed above. + They are also `ServiceLocatorAware`, and will be injected with the application + service manager instance, giving factories and abstract factories access to + application-level services when needed. See the heading + [Plugin managers](#plugin-managers) for a list of available plugin managers. + +The application service manager is referenced directly during bootstrapping, and has the following +services configured out of the box. + +### Invokable services + +- `DispatchListener`, mapping to `Zend\Mvc\DispatchListener`. +- `RouteListener`, mapping to `Zend\Mvc\RouteListener`. +- `SendResponseListener`, mapping to `Zend\Mvc\SendResponseListener`. +- `SharedEventManager`, mapping to `Zend\EventManager\SharedEventManager`. + +### Factories + +- `Application`, mapping to `Zend\Mvc\Service\ApplicationFactory`. + +- `Config`, mapping to `Zend\Mvc\Service\ConfigFactory`. Internally, this + pulls the `ModuleManager` service, calls its `loadModules()` method, and + retrieves the merged configuration from the module event. As such, this + service contains the entire, merged application configuration. + +- `ControllerManager`, mapping to `Zend\Mvc\Service\ControllerLoaderFactory`. + This creates an instance of `Zend\Mvc\Controller\ControllerManager`, passing + the service manager instance. Additionally, it uses the + `DiStrictAbstractServiceFactory` service, effectively allowing you to fall + back to DI in order to retrieve your controllers. If you want to use + `Zend\Di` to retrieve your controllers, you must white-list them in your DI + configuration under the `allowed_controllers` key (otherwise, they will just + be ignored). The `ControllerManager` provides initializers for the + following: + + - If the controller implements `Zend\ServiceManager\ServiceLocatorAwareInterface` + (or the methods it defines), an instance of the `ServiceManager` will be + injected into it. + + - If the controller implements `Zend\EventManager\EventManagerAwareInterface`, + an instance of the `EventManager` will be injected into it. + + - Finally, an initializer will inject it with the `ControllerPluginManager` + service, as long as the `setPluginManager` method is implemented. + +- `ControllerPluginManager`, mapping to + `Zend\Mvc\Service\ControllerPluginManagerFactory`. This instantiates the + `Zend\Mvc\Controller\PluginManager` instance, passing it the service manager + instance. It also uses the `DiAbstractServiceFactory` service, effectively + allowing you to fall back to DI in order to retrieve your [controller plugins](controller-plugins.md). + It registers a set of default controller plugins, and contains an + initializer for injecting plugins with the current controller. + +- `ConsoleAdapter`, mapping to `Zend\Mvc\Service\ConsoleAdapterFactory`. This + grabs the `Config` service, pulls from the `console` key, and do the + following: + + - If the `adapter` subkey is present, it is used to get the adapter + instance, otherwise, `Zend\Console\Console::detectBestAdapter()` will be + called to configure an adapter instance. + + - If the `charset` subkey is present, the value is used to set the adapter + charset. + +- `ConsoleRouter`, mapping to `Zend\Mvc\Service\ConsoleRouterFactory`. This + grabs the `Config` service, and pulls from the `console` key and `router` + subkey, configuring a `Zend\Mvc\Router\Console\SimpleRouteStack` instance. + +- `ConsoleViewManager`, mapping to `Zend\Mvc\Service\ConsoleViewManagerFactory`. + This creates and returns an instance of `Zend\Mvc\View\Console\ViewManager`, + which in turn registers and initializes a number of console-specific view + services. + +- `DependencyInjector`, mapping to `Zend\Mvc\Service\DiFactory`. This pulls + the `Config` service, and looks for a "di" key; if found, that value is used + to configure a new `Zend\Di\Di` instance. + +- `DiAbstractServiceFactory`, mapping to + `Zend\Mvc\Service\DiAbstractServiceFactoryFactory`. This creates an instance + of `Zend\ServiceManager\Di\DiAbstractServiceFactory` injecting the `Di` + service instance. That instance is attached to the service manager as an + abstract factory, effectively enabling DI as a fallback for providing + services. + +- `DiServiceInitializer`, mapping to `Zend\Mvc\Service\DiServiceInitializerFactory`. + This creates an instance of `Zend\ServiceManager\Di\DiServiceInitializer` + injecting the `Di` service and the service manager itself. + +- `DiStrictAbstractServiceFactory`, mapping to `Zend\Mvc\Service\DiStrictAbstractServiceFactoryFactory`. + This creates an instance of `Zend\Mvc\Service\DiStrictAbstractServiceFactoryFactory`, + injecting the `Di` service instance. + +- `EventManager`, mapping to `Zend\Mvc\Service\EventManagerFactory`. This + factory returns a *discrete* instance of `Zend\EventManager\EventManager` on + each request. This service is not shared by default, allowing the ability to + have an `EventManager` per service, with a shared `SharedEventManager` + injected in each. + +- `FilterManager`, mapping to `Zend\Mvc\Service\FilterManagerFactory`. This + instantiates the `Zend\Filter\FilterPluginManager` instance, passing it the + service manager instance; this is used to manage filters for [filter chains](http://zendframework.github.io/zend-filter/filter-chains/). + It also uses the `DiAbstractServiceFactory` service, effectively allowing + you to fall back to DI in order to retrieve filters. + +- `FormElementManager`, mapping to `Zend\Mvc\Service\FormElementManagerFactory`. + This instantiates the `Zend\Form\FormElementManager` instance, passing it + the service manager instance; this is used to manage [form elements](http://framework.zend.com/manual/current/en/zend.form.elements.intro.html). + It also uses the `DiAbstractServiceFactory` service, effectively allowing + you to fall back to DI in order to retrieve form elements. + +- `HttpRouter`, mapping to `Zend\Mvc\Service\HttpRouterFactory`. This grabs + the `Config` service, and pulls from the `router` key, configuring a + `Zend\Mvc\Router\Http\TreeRouteStack` instance. + +- `HttpViewManager`, mapping to `Zend\Mvc\Service\HttpViewManagerFactory`. + This creates and returns an instance of `Zend\Mvc\View\Http\ViewManager`, + which in turn registers and initializes a number of HTTP-specific view + services. + +- `HydratorManager`, mapping to `Zend\Mvc\Service\HydratorManagerFactory`. + This creates and returns an instance of `Zend\Stdlib\Hydrator\HydratorPluginManager`, + which can be used to manage and persist hydrator instances. + +- `InputFilterManager`, mapping to `Zend\Mvc\Service\InputFilterManagerFactory`. + This creates and returns an instance of `Zend\InputFilter\InputFilterPluginManager`, + which can be used to manage and persist input filter instances. + +- `ModuleManager`, mapping to `Zend\Mvc\Service\ModuleManagerFactory`. This is + perhaps the most complex factory in the MVC stack. It expects that an + `ApplicationConfig` service has been injected, with keys for + `module_listener_options` and `modules`; see the quick start for samples. + It creates an instance of `Zend\ModuleManager\Listener\DefaultListenerAggregate`, + using the `module_listener_options` retrieved. It then checks if a service + with the name `ServiceListener` exists; if not, it sets a factory with that + name mapping to `Zend\Mvc\Service\ServiceListenerFactory`. A bunch of + service listeners will be added to the `ServiceListener`, like listeners for + the `getServiceConfig`, `getControllerConfig`, `getControllerPluginConfig`, + and `getViewHelperConfig` module methods. Next, it retrieves the + `EventManager` service, and attaches the above listeners. It instantiates a + `Zend\ModuleManager\ModuleEvent` instance, setting the "ServiceManager" + parameter to the service manager object. Finally, it instantiates a + `Zend\ModuleManager\ModuleManager` instance, and injects the `EventManager` + and `ModuleEvent`. + +- `MvcTranslator`, mapping to `Zend\Mvc\Service\TranslatorServiceFactory`, and + returning an instance of `Zend\Mvc\I18n\Translator`, which extends + `Zend\I18n\Translator\Translator` and implements `Zend\Validator\Translator\TranslatorInterface`, + allowing the instance to be used anywhere a translator may be required in + the framework. + +- `PaginatorPluginManager`, mapping to `Zend\Mvc\Service\PaginatorPluginManagerFactory`. + This instantiates the `Zend\Paginator\AdapterPluginManager` instance, + passing it the service manager instance. This is used to manage + [paginator adapters](http://framework.zend.com/manual/current/en/zend.paginator.usage.paginating.adapters.html). + It also uses the `DiAbstractServiceFactory` service, effectively allowing + you to fall back to DI in order to retrieve paginator adapters. + +- `Request`, mapping to `Zend\Mvc\Service\RequestFactory`. The factory is used + to create and return a request instance, according to the current + environment. If the current environment is a console environment, it will + create a `Zend\Console\Request`; otherwise, for HTTP environments, it + creates a `Zend\Http\PhpEnvironment\Request`. + +- `Response`, mapping to `Zend\Mvc\Service\ResponseFactory`. The factory is + used to create and return a response instance, according to the current + environment. If the current environment is a console environment, it will + create a `Zend\Console\Response`; otherwise, for HTTP environments, it + creates a `Zend\Http\PhpEnvironment\Response`. + +- `Router`, mapping to `Zend\Mvc\Service\RouterFactory`. If in a console + enviroment, it proxies to the `ConsoleRouter` service; otherwise, it proxies + to the `HttpRouter` service. + +- `RoutePluginManager`, mapping to `Zend\Mvc\Service\RoutePluginManagerFactory`. + This instantiates the `Zend\Mvc\Router\RoutePluginManager` instance, passing + it the service manager instance; this is used to manage [route types](routing.md#http-route-types). + It also uses the `DiAbstractServiceFactory` service, effectively allowing + you to fall back to DI in order to retrieve route types. + +- `SerializerAdapterManager`, mapping to `Zend\Mvc\Service\SerializerAdapterPluginManagerFactory`, + which returns an instance of `Zend\Serializer\AdapterPluginManager`. This is + a plugin manager for managing serializer adapter instances. + +- `ServiceListener`, mapping to `Zend\Mvc\Service\ServiceListenerFactory`. The + factory is used to instantiate the `ServiceListener`, while allowing easy + extending. It checks if a service with the name `ServiceListenerInterface` + exists, which must implement `Zend\ModuleManager\Listener\ServiceListenerInterface`, + before instantiating the default `ServiceListener`. + In addition to this, it retrieves the `ApplicationConfig` and looks for the + `service_listener_options` key. This allows you to register own listeners + for module methods and configuration keys to create an own service manager; + see the [application configuration options](#application-configuration-options) for samples. + +- `ValidatorManager`, mapping to `Zend\Mvc\Service\ValidatorManagerFactory`. + This instantiates the `Zend\Validator\ValidatorPluginManager` instance, + passing it the service manager instance. This is used to manage + [validators](http://framework.zend.com/manual/current/en/zend.validator.set.html). + It also uses the `DiAbstractServiceFactory` service, effectively allowing + you to fall back to DI in order to retrieve validators. + +- `ViewFeedRenderer`, mapping to `Zend\Mvc\Service\ViewFeedRendererFactory`, + which returns an instance of `Zend\View\Renderer\FeedRenderer`, used to + render feeds. + +- `ViewFeedStrategy`, mapping to `Zend\Mvc\Service\ViewFeedStrategyFactory`, + which returns an instance of `Zend\View\Strategy\FeedStrategy`, used to + select the `ViewFeedRenderer` given the appropriate criteria. + +- `ViewHelperManager`, mapping to `Zend\Mvc\Service\ViewHelperManagerFactory`, + which returns an instance of `Zend\View\HelperManager`. This is a plugin + manager for managing view helper instances. + +- `ViewJsonRenderer`, mapping to `Zend\Mvc\Service\ViewJsonRendererFactory`, + which returns an instance of `Zend\View\Renderer\JsonRenderer`, used to + render JSON structures. + +- `ViewJsonStrategy`, mapping to `Zend\Mvc\Service\ViewJsonStrategyFactory`, + which returns an instance of `Zend\View\Strategy\JsonStrategy`, used to + select the `ViewJsonRenderer` given the appropriate criteria. + +- `ViewManager`, mapping to `Zend\Mvc\Service\ViewManagerFactory`. The factory + is used to create and return a view manager, according to the current + environment. If the current environment is a console environment, it will + create a `Zend\Mvc\View\Console\ViewManager`; otherwise, for HTTP + environments, it returns a `Zend\Mvc\View\Http\ViewManager`. + +- `ViewResolver`, mapping to `Zend\Mvc\Service\ViewResolverFactory`, which + creates and returns the aggregate view resolver. It also attaches the + `ViewTemplateMapResolver` and `ViewTemplatePathStack` services to it. + +- `ViewTemplateMapResolver`, mapping to `Zend\Mvc\Service\ViewTemplateMapResolverFactory`, + which creates, configures and returns the `Zend\View\Resolver\TemplateMapResolver`. + +- `ViewTemplatePathStack`, mapping to `Zend\Mvc\Service\ViewTemplatePathStackFactory`, + which creates, configures and returns the `Zend\View\Resolver\TemplatePathStack`. + +### Abstract factories + +- `Zend\Cache\Service\StorageCacheAbstractServiceFactory` (opt-in; registered + by default in the skeleton application). +- `Zend\Db\Adapter\AdapterAbstractServiceFactory` (opt-in). +- `Zend\Form\FormAbstractServiceFactory` is registered by default. +- `Zend\Log\LoggerAbstractServiceFactory` (opt-in; registered by default in the skeleton application). + +### Aliases + +- `Configuration`, mapping to the `Config` service. +- `Console`, mapping to the `ConsoleAdapter` service. +- `Di`, mapping to the `DependencyInjector` service. +- `Zend\Di\LocatorInterface`, mapping to the `DependencyInjector` service. +- `Zend\EventManager\EventManagerInterface`, mapping to the `EventManager` + service. This is mainly to ensure that when falling through to DI, classes + are still injected via the `ServiceManager`. +- `Zend\Mvc\Controller\PluginManager`, mapping to the + `ControllerPluginManager` service. This is mainly to ensure that when + falling through to DI, classes are still injected via the `ServiceManager`. +- `Zend\View\Resolver\TemplateMapResolver`, mapping to the + `ViewTemplateMapResolver` service. +- `Zend\View\Resolver\TemplatePathStack`, mapping to the + `ViewTemplatePathStack` service. +- `Zend\View\Resolver\AggregateResolver`, mapping to the `ViewResolver` service. +- `Zend\View\Resolver\ResolverInterface`, mapping to the `ViewResolver` service. + +### Initializers + +- For objects that implement `Zend\EventManager\EventManagerAwareInterface`, + the `EventManager` service will be retrieved and injected. This service is + **not** shared, though each instance it creates is injected with a shared + instance of `SharedEventManager`. + +- For objects that implement `Zend\ServiceManager\ServiceLocatorAwareInterface` + (or the methods it defines), the `ServiceManager` will inject itself into + the object. + +- The `ServiceManager` registers itself as the `ServiceManager` service, and + aliases itself to the class names `Zend\ServiceManager\ServiceLocatorInterface` + and `Zend\ServiceManager\ServiceManager`. + +## Abstract Factories + +As noted in the previous section, Zend Framework provides a number of abstract +service factories by default. Each is noted below, along with sample +configuration. + +In each instance, the abstract factory looks for a top-level configuration key, +consisting of key/value pairs where the key is the service name, and the value +is the configuration to use to create the given service. + +### Zend\\Cache\\Service\\StorageCacheAbstractServiceFactory + +This abstract factory is opt-in, but registered by default in the skeleton application. It uses the +top-level configuration key "caches". + +```php +return [ + 'caches' => [ + 'Cache\Transient' => [ + 'adapter' => 'redis', + 'ttl' => 60, + 'plugins' => [ + 'exception_handler' => [ + 'throw_exceptions' => false, + ], + ], + ], + 'Cache\Persistence' => [ + 'adapter' => 'filesystem', + 'ttl' => 86400, + ], + ], +]; +``` + +See the [cache documentation](https://zendframework.github.io/zend-cache/storage/adapter/) +for more configuration options. + +### Zend\\Db\\Adapter\\AdapterAbstractServiceFactory + +This abstract factory is opt-in. It uses the top-level configuration key "db", +with a subkey "adapters". + +```php +return [ + 'db' => ['adapters' => [ + 'Db\ReadOnly' => [ + 'driver' => 'Pdo_Sqlite', + 'database' => 'data/db/users.db', + ], + 'Db\Writeable' => [ + 'driver' => 'Mysqli', + 'database' => 'users', + 'username' => 'developer', + 'password' => 'developer_password', + ], + ]], +]; +``` + +See the [DB adapter documentation](http://framework.zend.com/manual/current/en/zend.db.adapter.html) +for more configuration options. + +### Zend\\Form\\FormAbstractServiceFactory + +This abstract factory is registered by default. It uses the top-level +configuration key "forms". It makes use of the `FilterManager`, +`FormElementManager`, `HydratorManager`, `InputFilterManager`, and +`ValidatorManager` plugin managers in order to allow instantiation and creation +of form objects and all related objects in the form hierarchy. + +```php +return [ + 'forms' => [ + 'Form\Foo' => [ + 'hydrator' => 'ObjectProperty', + 'type' => 'Zend\Form\Form', + 'elements' => [ + [ + 'spec' => [ + 'type' => 'Zend\Form\Element\Email', + 'name' => 'email', + 'options' => [ + 'label' => 'Your email address', + ], + ], + ], + ], + ], + ], +]; +``` + +Form configuration follows the same configuration you would use with a form +factory; the primary difference is that all plugin managers have already been +injected for you, allowing you the possibility of custom objects or +substitutions. + +See the [form factory documentation](http://framework.zend.com/manual/current/en/zend.form.quick-start.factory.html) +for more configuration options. + +### Zend\\Log\\LoggerAbstractServiceFactory + +This abstract factory is opt-in, but registered by default in the skeleton +application. It uses the top-level configuration key "log". + +```php +return [ + 'log' => [ + 'Log\App' => [ + 'writers' => [ + [ + 'name' => 'stream', + 'priority' => 1000, + 'options' => [ + 'stream' => 'data/logs/app.log', + ], + ], + ], + ], + ], +]; +``` + +See the [log documentation](https://zendframework.github.io/zend-log/intro/) +for more configuration options. + +## Plugin Managers + +The following plugin managers are configured by default: + +- **ControllerManager**, corresponding to `Zend\Mvc\Controller\ControllerManager`, + and used to manage controller instances. +- **ControllerPluginManager**, corresponding to `Zend\Mvc\Controller\PluginManager`, + and used to manage controller plugin instances. +- **FilterManager**, corresponding to `Zend\Filter\FilterPluginManager`, and + used to manage filter instances. +- **FormElementManager**, corresponding to `Zend\Form\FormElementManager`, and + used to manage instances of form elements and fieldsets. +- **HydratorManager**, corresponding to `Zend\Stdlib\Hydrator\HydratorPluginManager`, + and used to manage hydrator instances. +- **InputFilterManager**, corresponding to `Zend\InputFilter\InputFilterPluginManager`, + and used to manage input filter instances. +- **RoutePluginManager**, corresponding to `Zend\Mvc\Router\RoutePluginManager`, + and used to manage route instances. +- **SerializerAdapterManager**, corresponding to `Zend\Serializer\AdapterPluginManager`, + and used to manage serializer instances. +- **ValidatorManager**, corresponding to `Zend\Validator\ValidatorPluginManager`, + and used to manage validator instances. +- **ViewHelperManager**, corresponding to `Zend\View\HelperPluginManager`, and + used to manage view helper instances. + +As noted in the previous section, all plugin managers share the same +configuration and service types as the standard service manager; they are simply +scoped, and only allow instances of certain types to be created or registered. +Default types available are listed in the documentation for each component. + +## ViewManager + +The View layer within zend-mvc consists of a large number of collaborators and +event listeners. As such, `Zend\Mvc\View\ViewManager` was created to handle +creation of the various objects, as well as wiring them together and +establishing event listeners. + +The `ViewManager` itself is an event listener on the `bootstrap` event. It +retrieves the `ServiceManager` from the `Application` object, as well as its +composed `EventManager`. + +Configuration for all members of the `ViewManager` fall under the `view_manager` +configuration key, and expect values as noted below. The following services are +created and managed by the `ViewManager`: + +- `ViewHelperManager`, representing and aliased to `Zend\View\HelperPluginManager`. + It is seeded with the `ServiceManager`. Created via the + `Zend\Mvc\Service\ViewHelperManagerFactory`. + + - The `Router` service is retrieved, and injected into the `Url` helper. + + - If the `base_path` key is present, it is used to inject the `BasePath` view + helper; otherwise, the `Request` service is retrieved, and the value of its + `getBasePath()` method is used. + + - If the `base_path_console` key is present, it is used to inject the + `BasePath` view helper for console requests; otherwise, the `Request` + service is retrieved, and the value of its `getBasePath()` method is used. + This can be useful for sending urls in emails via a cronjob. + + - If the `doctype` key is present, it will be used to set the value of the + `Doctype` view helper. + +- `ViewTemplateMapResolver`, representing and aliased to + `Zend\View\Resolver\TemplateMapResolver`. If a `template_map` key is present, + it will be used to seed the template map. + +- `ViewTemplatePathStack`, representing and aliased to + `Zend\View\Resolver\TemplatePathStack`. + + - If a `template_path_stack` key is present, it will be used to seed the + stack. + + - If a `default_template_suffix` key is present, it will be used as the + default suffix for template scripts resolving. + +- `ViewResolver`, representing and aliased to `Zend\View\Resolver\AggregateResolver` + and `Zend\View\Resolver\ResolverInterface`. It is seeded with the + `ViewTemplateMapResolver` and `ViewTemplatePathStack` services as resolvers. + +- `ViewRenderer`, representing and aliased to `Zend\View\Renderer\PhpRenderer` + and `Zend\View\Renderer\RendererInterface`. It is seeded with the + `ViewResolver` and `ViewHelperManager` services. Additionally, the `ViewModel` + helper gets seeded with the `ViewModel` as its root (layout) model. + +- `ViewPhpRendererStrategy`, representing and aliased to + `Zend\View\Strategy\PhpRendererStrategy`. It gets seeded with the + `ViewRenderer` service. + +- `View`, representing and aliased to `Zend\View\View`. It gets seeded with the + `EventManager` service, and attaches the `ViewPhpRendererStrategy` as an + aggregate listener. + +- `DefaultRenderingStrategy`, representing and aliased to + `Zend\Mvc\View\DefaultRenderingStrategy`. If the `layout` key is present, it + is used to seed the strategy's layout template. It is seeded with the `View` + service. + +- `ExceptionStrategy`, representing and aliased to `Zend\Mvc\View\ExceptionStrategy`. + If the `display_exceptions` or `exception_template` keys are present, they are + used to configure the strategy. + +- `RouteNotFoundStrategy`, representing and aliased to `Zend\Mvc\View\RouteNotFoundStrategy` + and `404Strategy`. If the `display_not_found_reason` or `not_found_template` + keys are present, they are used to configure the strategy. + +- `ViewModel`. In this case, no service is registered; the `ViewModel` is + retrieved from the `MvcEvent` and injected with the layout template name. + +The `ViewManager` also creates several other listeners, but does not expose them +as services; these include `Zend\Mvc\View\CreateViewModelListener`, +`Zend\Mvc\View\InjectTemplateListener`, and `Zend\Mvc\View\InjectViewModelListener`. +These, along with `RouteNotFoundStrategy`, `ExceptionStrategy`, and +`DefaultRenderingStrategy` are attached as listeners either to the application +`EventManager` instance or the `SharedEventManager` instance. + +Finally, if you have a `strategies` key in your configuration, the `ViewManager` +will loop over these and attach them in order to the `View` service as +listeners, at a priority of 100 (allowing them to execute before the +`DefaultRenderingStrategy`). + +## Application Configuration Options + +The following options may be used to provide initial configuration for the +`ServiceManager`, `ModuleManager`, and `Application` instances, allowing them to +then find and aggregate the configuration used for the `Config` service, which +is intended for configuring all other objects in the system. These configuration +directives go to the `config/application.config.php` file. + +```php + [ + ], + + // These are various options for the listeners attached to the ModuleManager + 'module_listener_options' => [ + // This should be an array of paths in which modules reside. + // If a string key is provided, the listener will consider that a module + // namespace, the value of that key the specific path to that module's + // Module class. + 'module_paths' => [ + ], + + // An array of paths from which to glob configuration files after + // modules are loaded. These effectively override configuration + // provided by modules themselves. Paths may use GLOB_BRACE notation. + 'config_glob_paths' => [ + ], + + // Whether or not to enable a configuration cache. + // If enabled, the merged configuration will be cached and used in + // subsequent requests. + 'config_cache_enabled' => $booleanValue, + + // The key used to create the configuration cache file name. + 'config_cache_key' => $stringKey, + + // Whether or not to enable a module class map cache. + // If enabled, creates a module class map cache which will be used + // by in future requests, to reduce the autoloading process. + 'module_map_cache_enabled' => $booleanValue, + + // The key used to create the class map cache file name. + 'module_map_cache_key' => $stringKey, + + // The path in which to cache merged configuration. + 'cache_dir' => $stringPath, + + // Whether or not to enable modules dependency checking. + // Enabled by default, prevents usage of modules that depend on other modules + // that weren't loaded. + 'check_dependencies' => $booleanValue, + ], + + // Used to create an own service manager. May contain one or more child arrays. + 'service_listener_options' => [ + [ + 'service_manager' => $stringServiceManagerName, + 'config_key' => $stringConfigKey, + 'interface' => $stringOptionalInterface, + 'method' => $stringRequiredMethodName, + ], + ] + + // Initial configuration with which to seed the ServiceManager. + // Should be compatible with Zend\ServiceManager\Config. + 'service_manager' => [ + ], +]; +``` + +For an example, see the +[ZendSkeletonApplication configuration file](https://github.com/zendframework/ZendSkeletonApplication/blob/master/config/application.config.php). + +## Default Configuration Options + +The following options are available when using the default services configured +by the `ServiceManagerConfig` and `ViewManager`. + +These configuration directives can go to the `config/autoload/{{,*.}global,{,*.}local}.php` +files, or in the `module//config/module.config.php` configuration +files. The merging of these configuration files is done by the `ModuleManager`. +It first merges each module's `module.config.php` file, and then the files in +`config/autoload` (first the `*.global.php` and then the `*.local.php` files). +The order of the merge is relevant so you can override a module's configuration +with your application configuration. If you have both a `config/autoload/my.global.config.php` +and `config/autoload/my.local.config.php`, the local configuration file +overrides the global configuration. + +> ### Do not commit local configuration +> +> Local configuration files are intended to keep sensitive information, such as +> database credentials, and as such, it is highly recommended to keep these +> local configuration files out of your VCS. The `ZendSkeletonApplication`'s +> `config/autoload/.gitignore` file ignores `*.local.php` files by default. + +```php + [ + // Map of controller "name" to class + // This should be used if you do not need to inject any dependencies + // in your controller + 'invokables' => [ + ], + + // Map of controller "name" to factory for creating controller instance + // You may provide either the class name of a factory, or a PHP callback. + 'factories' => [ + ], + ], + + // The following are used to configure controller plugin loader + // Should be compatible with Zend\ServiceManager\Config. + 'controller_plugins' => [ + ], + + // The following are used to configure view helper manager + // Should be compatible with Zend\ServiceManager\Config. + 'view_helpers' => [ + ], + + // The following is used to configure a Zend\Di\Di instance. + // The array should be in a format that Zend\Di\Config can understand. + 'di' => [ + ], + + // Configuration for the Router service + // Can contain any router configuration, but typically will always define + // the routes for the application. See the router documentation for details + // on route configuration. + 'router' => [ + 'routes' => [ + ], + ], + + // ViewManager configuration + 'view_manager' => [ + // Base URL path to the application + 'base_path' => $stringBasePath, + + // Doctype with which to seed the Doctype helper + 'doctype' => $doctypeHelperConstantString, // e.g. HTML5, XHTML1 + + // TemplateMapResolver configuration + // template/path pairs + 'template_map' => [ + ], + + // TemplatePathStack configuration + // module/view script path pairs + 'template_path_stack' => [ + ], + // Default suffix to use when resolving template scripts, if none, 'phtml' is used + 'default_template_suffix' => $templateSuffix, // e.g. 'php' + + // Controller namespace to template map + // or whitelisting for controller FQCN to template mapping + 'controller_map' => [ + ], + + // Layout template name + 'layout' => $layoutTemplateName, // e.g. 'layout/layout' + + // ExceptionStrategy configuration + 'display_exceptions' => $bool, // display exceptions in template + 'exception_template' => $stringTemplateName, // e.g. 'error' + + // RouteNotFoundStrategy configuration + 'display_not_found_reason' => $bool, // display 404 reason in template + 'not_found_template' => $stringTemplateName, // e.g. '404' + + // Additional strategies to attach + // These should be class names or service names of View strategy classes + // that act as ListenerAggregates. They will be attached at priority 100, + // in the order registered. + 'strategies' => [ + 'ViewJsonStrategy', // register JSON renderer strategy + 'ViewFeedStrategy', // register Feed renderer strategy + ], + ], +]; +``` + +For an example, see the +[Application module configuration file](https://github.com/zendframework/ZendSkeletonApplication/blob/master/module/Application/config/module.config.php) +in the ZendSkeletonApplication. diff --git a/doc/book/zend.mvc.controllers.md b/doc/book/zend.mvc.controllers.md deleted file mode 100644 index 9f2a5dca2..000000000 --- a/doc/book/zend.mvc.controllers.md +++ /dev/null @@ -1,243 +0,0 @@ -# Available Controllers - -Controllers in the MVC layer simply need to be objects implementing -`Zend\Stdlib\DispatchableInterface`. That interface describes a single method: - -```php -use Zend\Stdlib\DispatchableInterface; -use Zend\Stdlib\RequestInterface as Request; -use Zend\Stdlib\ResponseInterface as Response; - -class Foo implements DispatchableInterface -{ - public function dispatch(Request $request, Response $response = null) - { - // ... do something, and preferably return a Response ... - } -} -``` - -While this pattern is simple enough, chances are you don't want to implement custom dispatch logic -for every controller (particularly as it's not unusual or uncommon for a single controller to handle -several related types of requests). - -The MVC also defines several interfaces that, when implemented, can provide controllers with -additional capabilities. - -## Common Interfaces Used With Controllers - -### InjectApplicationEvent - -The `Zend\Mvc\InjectApplicationEventInterface` hints to the `Application` instance that it should -inject its `MvcEvent` into the controller itself. Why would this be useful? - -Recall that the `MvcEvent` composes a number of objects: the `Request` and `Response`, naturally, -but also the router, the route matches (a `RouteMatch` instance), and potentially the "result" of -dispatching. - -A controller that has the `MvcEvent` injected, then, can retrieve or inject these. As an example: - -```php -$matches = $this->getEvent()->getRouteMatch(); -$id = $matches->getParam('id', false); -if (!$id) { - $response = $this->getResponse(); - $response->setStatusCode(500); - $this->getEvent()->setResult('Invalid identifier; cannot complete request'); - return; -} -``` - -The `InjectApplicationEventInterface` defines simply two methods: - -```php -public function setEvent(Zend\EventManager\EventInterface $event); -public function getEvent(); -``` - -### ServiceLocatorAware - -In most cases, you should define your controllers such that dependencies are injected by the -application's `ServiceManager`, via either constructor arguments or setter methods. - -However, occasionally you may have objects you wish to use in your controller that are only valid -for certain code paths. Examples include forms, paginators, navigation, etc. In these cases, you may -decide that it doesn't make sense to inject those objects every time the controller is used. - -The `ServiceLocatorAwareInterface` interface hints to the `ServiceManager` that it should inject -itself into the controller. It defines two simple methods: - -```php -use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\ServiceManager\ServiceLocatorAwareInterface; - -public function setServiceLocator(ServiceLocatorInterface $serviceLocator); -public function getServiceLocator(); -``` - -### EventManagerAware - -Typically, it's nice to be able to tie into a controller's workflow without needing to extend it or -hardcode behavior into it. The solution for this at the framework level is to use the -`EventManager`. - -You can hint to the `ServiceManager` that you want an `EventManager` injected by implementing the -interface `EventManagerAwareInterface`, which tells the `ServiceManager` to inject an -`EventManager`. - -You define two methods. The first, a setter, should also set any `EventManager` identifiers you want -to listen on, and the second, a getter, should simply return the composed `EventManager` instance. - -```php -use Zend\EventManager\EventManagerAwareInterface; -use Zend\EventManager\EventManagerInterface; - -public function setEventManager(EventManagerInterface $events); -public function getEventManager(); -``` - -### Controller Plugins - -Code re-use is a common goal for developers. Another common goal is convenience. However, this is -often difficult to achieve cleanly in abstract, general systems. - -Within your controllers, you'll often find yourself repeating tasks from one controller to another. -Some common examples: - -* Generating URLs -* Redirecting -* Setting and retrieving flash messages (self-expiring session messages) -* Invoking and dispatching additional controllers - -To facilitate these actions while also making them available to alternate controller -implementations, we've created a `PluginManager` implementation for the controller layer, -`Zend\Mvc\Controller\PluginManager`, building on the `Zend\ServiceManager\AbstractPluginManager` -functionality. To utilize it, you simply need to implement the `setPluginManager(PluginManager -$plugins)` method, and set up your code to use the controller-specific implementation by default: - -```php -use Zend\Mvc\Controller\PluginManager; - -public function setPluginManager(PluginManager $plugins) -{ - $this->plugins = $plugins; - $this->plugins->setController($this); - - return $this; -} - -public function getPluginManager() -{ - if (!$this->plugins) { - $this->setPluginManager(new PluginManager()); - } - - return $this->plugins; -} - -public function plugin($name, array $options = null) -{ - return $this->getPluginManager()->get($name, $options); -} -``` - -## The AbstractActionController - -Implementing each of the above interfaces is a lesson in redundancy; you won't often want to do it. -As such, we've developed two abstract, base controllers you can extend to get started. - -The first is `Zend\Mvc\Controller\AbstractActionController`. This controller implements each of the -above interfaces, and uses the following assumptions: - -* An "action" parameter is expected in the `RouteMatch` object composed in the attached `MvcEvent`. -If none is found, a `notFoundAction()` is invoked. -* The "action" parameter is converted to a camelCased format and appended with the word "Action" to -create a method name. As examples: "foo" maps to "fooAction", "foo-bar" or "foo.bar" or "foo\_bar" -to "fooBarAction". The controller then checks to see if that method exists. If not, the -`notFoundAction()` method is invoked; otherwise, the discovered method is called. -* The results of executing the given action method are injected into the `MvcEvent`'s "result" -property (via `setResult()`, and accessible via `getResult()`). - -Essentially, a route mapping to an `AbstractActionController` needs to return both "controller" and -"action" keys in its matches. - -Creation of action controllers is then reasonably trivial: - -```php -namespace Foo\Controller; - -use Zend\Mvc\Controller\AbstractActionController; - -class BarController extends AbstractActionController -{ - public function bazAction() - { - return array('title' => __METHOD__); - } - - public function batAction() - { - return array('title' => __METHOD__); - } -} -``` - -### Interfaces and Collaborators - -`AbstractActionController` implements each of the following interfaces: - -* `Zend\Stdlib\DispatchableInterface` -* `Zend\Mvc\InjectApplicationEventInterface` -* `Zend\ServiceManager\ServiceLocatorAwareInterface` -* `Zend\EventManager\EventManagerAwareInterface` - -The composed `EventManager` will be configured to listen on the following contexts: - -* `Zend\Stdlib\DispatchableInterface` -* `Zend\Mvc\Controller\AbstractActionController` -* `Zend\Mvc\Controller\AbstractController` - -Additionally, if you extend the class, it will listen on the extending class's name. - -## The AbstractRestfulController - -The second abstract controller ZF2 provides is `Zend\Mvc\Controller\AbstractRestfulController`. This -controller provides a native RESTful implementation that simply maps HTTP request methods to -controller methods, using the following matrix: - -* **GET** maps to either `get()` or `getList()`, depending on whether or not an "id" parameter is -found in the route matches. If one is, it is passed as an argument to `get()`; if not, `getList()` -is invoked. In the former case, you should provide a representation of the given entity with that -identification; in the latter, you should provide a list of entities. -* **POST** maps to `create()`. That method expects a `$data` argument, usually the `$_POST` -superglobal array. The data should be used to create a new entity, and the response should typically -be an HTTP 201 response with the Location header indicating the URI of the newly created entity and -the response body providing the representation. -* **PUT** maps to `update()`, and requires that an "id" parameter exists in the route matches; that -value is passed as an argument to the method. It should attempt to update the given entity, and, if -successful, return either a 200 or 202 response status, as well as the representation of the entity. -* **DELETE** maps to `delete()`, and requires that an "id" parameter exists in the route matches; -that value is passed as an argument to the method. It should attempt to delete the given entity, -and, if successful, return either a 200 or 204 response status. - -Additionally, you can map "action" methods to the `AbstractRestfulController`, just as you would in -the `AbstractActionController`; these methods will be suffixed with "Action", differentiating them -from the RESTful methods listed above. This allows you to perform such actions as providing forms -used to submit to the various RESTful methods, or to add RPC methods to your RESTful API. - -### Interfaces and Collaborators - -`AbstractRestfulController` implements each of the following interfaces: - -* `Zend\Stdlib\DispatchableInterface` -* `Zend\Mvc\InjectApplicationEventInterface` -* `Zend\ServiceManager\ServiceLocatorAwareInterface` -* `Zend\EventManager\EventManagerAwareInterface` - -The composed `EventManager` will be configured to listen on the following contexts: - -* `Zend\Stdlib\DispatchableInterface` -* `Zend\Mvc\Controller\AbstractRestfulController` -* `Zend\Mvc\Controller\AbstractController` - -Additionally, if you extend the class, it will listen on the extending class's name. diff --git a/doc/book/zend.mvc.examples.md b/doc/book/zend.mvc.examples.md deleted file mode 100644 index 52837742c..000000000 --- a/doc/book/zend.mvc.examples.md +++ /dev/null @@ -1,111 +0,0 @@ -# Examples - -## Controllers - -### Accessing the Request and Response - -When using `AbstractActionController` or `AbstractRestfulController`, the request and response -object are composed directly into the controller as soon as `dispatch()` is called. You may access -them in the following ways: - -```php -// Using explicit accessor methods -$request = $this->getRequest(); -$response = $this->getResponse(); - -// Using direct property access -$request = $this->request; -$response = $this->response; -``` - -Additionally, if your controller implements `InjectApplicationEventInterface` (as both -`AbstractActionController` and `AbstractRestfulController` do), you can access these objects from -the attached `MvcEvent`: - -```php -$event = $this->getEvent(); -$request = $event->getRequest(); -$response = $event->getResponse(); -``` - -The above can be useful when composing event listeners into your controller. - -### Accessing routing parameters - -The parameters returned when routing completes are wrapped in a `Zend\Mvc\Router\RouteMatch` object. -This object is detailed in the section on \[Routing\](zend.mvc.routing). - -Within your controller, if you implement `InjectApplicationEventInterface` (as both -`AbstractActionController` and `AbstractRestfulController` do), you can access this object from the -attached `MvcEvent`: - -```php -$event = $this->getEvent(); -$matches = $event->getRouteMatch(); -``` - -Once you have the `RouteMatch` object, you can pull parameters from it. - -The same can be done using the Params plugin<zend.mvc.controller-plugins.params>. - -### Returning early - -You can effectively short-circuit execution of the application at any point by returning a -`Response` from your controller or any event. When such a value is discovered, it halts further -execution of the event manager, bubbling up to the `Application` instance, where it is immediately -returned. - -As an example, the `Redirect` plugin returns a `Response`, which can be returned immediately so as -to complete the request as quickly as possible. Other use cases might be for returning JSON or XML -results from web service endpoints, returning "401 Unauthorized" results, etc. - -## Bootstrapping - -### Registering module-specific listeners - -Often you may want module-specific listeners. As an example, this would be a simple and effective -way to introduce authorization, logging, or caching into your application. - -Each `Module` class can have an optional `onBootstrap()` method. Typically, you'll do -module-specific configuration here, or setup event listeners for you module here. The -`onBootstrap()` method is called for **every** module on **every** page request and should **only** -be used for performing **lightweight** tasks such as registering event listeners. - -The base `Application` class shipped with the framework has an `EventManager` associated with it, -and once the modules are initialized, it triggers the \[bootstrap\](zend.mvc.mvc-event.bootstrap) -event, with a `getApplication()` method on the event. - -So, one way to accomplish module-specific listeners is to listen to that event, and register -listeners at that time. As an example: - -```php -namespace SomeCustomModule; - -class Module -{ - /** - * @param \Zend\Mvc\MvcEvent $e The MvcEvent instance - * @return void - */ - public function onBootstrap($e) - { - $application = $e->getApplication(); - $config = $application->getConfig(); - $view = $application->getServiceManager()->get('ViewHelperManager'); - // You must have these keys in you application config - $view->headTitle($config['view']['base_title']); - - // This is your custom listener - $listener = new Listeners\ViewListener(); - $listener->setView($view); - $application->getEventManager()->attachAggregate($listener); - } -} -``` - -The above demonstrates several things. First, it demonstrates a listener on the application's -\[bootstrap\](zend.mvc.mvc-event.bootstrap) event (the `onBootstrap()` method). Second, it -demonstrates that listener, and how it can be used to register listeners with the application. It -grabs the `Application` instance; from the `Application`, it is able to grab the attached service -manager and configuration. These are then used to retrieve the view, configure some helpers, and -then register a listener aggregate with the application event manager. diff --git a/doc/book/zend.mvc.intro.md b/doc/book/zend.mvc.intro.md deleted file mode 100644 index bae980863..000000000 --- a/doc/book/zend.mvc.intro.md +++ /dev/null @@ -1,358 +0,0 @@ -# Introduction to the MVC Layer - -`Zend\Mvc` is a brand new MVC implementation designed from the ground up for Zend Framework 2, -focusing on performance and flexibility. - -The MVC layer is built on top of the following components: - -* `Zend\ServiceManager` - Zend Framework provides a set of default service definitions set up at -`Zend\Mvc\Service`. The `ServiceManager` creates and configures your application instance and -workflow. -* `Zend\EventManager` - The MVC is event driven. This component is used everywhere from initial -bootstrapping of the application, through returning response and request calls, to setting and -retrieving routes and matched routes, as well as render views. -* `Zend\Http` - specifically the request and response objects, used within: -* `Zend\Stdlib\DispatchableInterface`. All "controllers" are simply dispatchable objects. - -Within the MVC layer, several sub-components are exposed: - -* `Zend\Mvc\Router` contains classes pertaining to routing a request. In other words, it matches the -request to its respective controller (or dispatchable). -* `Zend\Http\PhpEnvironment` provides a set of decorators for the HTTP `Request` and `Response` -objects that ensure the request is injected with the current environment (including query -parameters, POST parameters, HTTP headers, etc.) -* `Zend\Mvc\Controller`, a set of abstract "controller" classes with basic responsibilities such as -event wiring, action dispatching, etc. -* `Zend\Mvc\Service` provides a set of `ServiceManager` factories and definitions for the default -application workflow. -* `Zend\Mvc\View` provides default wiring for renderer selection, view script resolution, helper -registration, and more; additionally, it provides a number of listeners that tie into the MVC -workflow, providing features such as automated template name resolution, automated view model -creation and injection, and more. - -The gateway to the MVC is the -[Zend\\Mvc\\Application](https://github.com/zendframework/zf2/blob/master/library/Zend/Mvc/Application.php) -object (referred to as `Application` henceforth). Its primary responsibilities are to **bootstrap** -resources, **route** the request, and to retrieve and **dispatch** the controller matched during -routing. Once accomplished, it will **render** the view, and **finish** the request, returning and -sending the response. - -## Basic Application Structure - -The basic application structure follows: - -``` -application_root/ - config/ - application.config.php - autoload/ - global.php - local.php - // etc. - data/ - module/ - vendor/ - public/ - .htaccess - index.php - init_autoloader.php -``` - -The `public/index.php` marshalls all user requests to your website, retrieving an array of -configuration located in `config/application.config.php`. On return, it `run()`s the `Application`, -processing the request and returning a response to the user. - -The `config` directory as described above contains configuration used by the `Zend\ModuleManager` to -load modules and merge configuration (e.g., database configuration and credentials); we will detail -this more later. - -The `vendor` sub-directory should contain any third-party modules or libraries on which your -application depends. This might include Zend Framework, custom libraries from your organization, or -other third-party libraries from other projects. Libraries and modules placed in the `vendor` -sub-directory should not be modified from their original, distributed state. - -Finally, the `module` directory will contain one or more modules delivering your application's -functionality. - -Let's now turn to modules, as they are the basic units of a web application. - -## Basic Module Structure - -A module may contain anything: PHP code, including MVC functionality; library code; view scripts; -and/or or public assets such as images, CSS, and JavaScript. The only requirement -- and even this -is optional -- is that a module acts as a PHP namespace and that it contains a `Module.php` class -under that namespace. This class is eventually consumed by `Zend\ModuleManager` to perform a number -of tasks. - -The recommended module structure follows: - -``` -module_root/ - Module.php - autoload_classmap.php - autoload_function.php - autoload_register.php - config/ - module.config.php - public/ - images/ - css/ - js/ - src/ - / - - test/ - phpunit.xml - bootstrap.php - / - - view/ - / - / - <.phtml files> -``` - -Since a module acts as a namespace, the module root directory should be that namespace. This -namespace could also include a vendor prefix of sorts. As an example a module centered around "User" -functionality delivered by Zend might be named "ZendUser", and this is also what the module root -directory will be named. - -The `Module.php` file directly under the module root directory will be in the module namespace shown -below. - -```php -namespace ZendUser; - -class Module -{ -} -``` - -When an `init()` method is defined, this method will be triggered by a `Zend\ModuleManager` listener -when it loads the module class, and passed an instance of the manager by default. This allows you to -perform tasks such as setting up module-specific event listeners. But be cautious, the `init()` -method is called for **every** module on **every** page request and should **only** be used for -performing **lightweight** tasks such as registering event listeners. Similarly, an `onBootstrap()` -method (which accepts an `MvcEvent` instance) may be defined; it is also triggered for every page -request, and should be used for lightweight tasks as well. - -The three `autoload_*.php` files are not required, but recommended. They provide the following: - - File | Description --------------------------|------------ - `autoload_classmap.php` | Should return an array classmap of class name/filename pairs (with the filenames resolved via the `__DIR__` magic constant). - `autoload_function.php` | Should return a PHP callback that can be passed to `spl_autoload_register()`. Typically, this callback should utilize the map returned by `autoload_classmap.php.` - `autoload_register.php` | should register a PHP callback (is typically returned by `autoload_function.php` with `spl_autoload_register()`. - -The point of these three files is to provide reasonable default mechanisms for autoloading the -classes contained in the module, thus providing a trivial way to consume the module without -requiring `Zend\ModuleManager` (e.g., for use outside a ZF2 application). - -The `config` directory should contain any module-specific configuration. These files may be in any -format `Zend\Config` supports. We recommend naming the main configuration "module.format", and for -PHP-based configuration, "module.config.php". Typically, you will create configuration for the -router as well as for the dependency injector. - -The `src` directory should be a [PSR-0 compliant directory -structure](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) with your -module's source code. Typically, you should at least have one sub-directory named after your module -namespace; however, you can ship code from multiple namespaces if desired. - -The `test` directory should contain your unit tests. Typically, these are written using -[PHPUnit](http://phpunit.de), and contain artifacts related to its configuration (e.g., -`phpunit.xml`, `bootstrap.php`). - -The `public` directory can be used for assets that you may want to expose in your application's -document root. These might include images, CSS files, JavaScript files, etc. How these are exposed -is left to the developer. - -The `view` directory contains view scripts related to your controllers. - -## Bootstrapping an Application - -The `Application` has six basic dependencies. - -* **configuration**, usually an array or object implementing `Traversable`. -* **ServiceManager** instance. -* **EventManager** instance, which, by default, is pulled from the `ServiceManager`, by the service -name "EventManager". -* **ModuleManager** instance, which, by default, is pulled from the `ServiceManager`, by the service -name "ModuleManager". -* **Request** instance, which, by default, is pulled from the `ServiceManager`, by the service name -"Request". -* **Response** instance, which, by default, is pulled from the `ServiceManager`, by the service name -"Response". - -These may be satisfied at instantiation: - -```php -use Zend\EventManager\EventManager; -use Zend\Http\PhpEnvironment; -use Zend\ModuleManager\ModuleManager; -use Zend\Mvc\Application; -use Zend\ServiceManager\ServiceManager; - -$config = include 'config/application.config.php'; - -$serviceManager = new ServiceManager(); -$serviceManager->setService('EventManager', new EventManager()); -$serviceManager->setService('ModuleManager', new ModuleManager($config)); -$serviceManager->setService('Request', new PhpEnvironment\Request()); -$serviceManager->setService('Response', new PhpEnvironment\Response()); - -$application = new Application($config, $serviceManager); -``` - -Once you've done this, there are two additional actions you can take. The first is to "bootstrap" -the application. In the default implementation, this does the following: - -* Attaches the default route listener (`Zend\Mvc\RouteListener`). -* Attaches the default dispatch listener (`Zend\Mvc\DispatchListener`). -* Attaches the `ViewManager` listener (`Zend\Mvc\View\ViewManager`). -* Creates the `MvcEvent`, and injects it with the application, request, and response; it also -retrieves the router (`Zend\Mvc\Router\Http\TreeRouteStack`) at this time and attaches it to the -event. -* Triggers the "bootstrap" event. - -If you do not want these actions, or want to provide alternatives, you can do so by extending the -`Application` class and/or simply coding what actions you want to occur. - -The second action you can take with the configured `Application` is to `run()` it. Calling this -method simply does the following: it triggers the "route" event, followed by the "dispatch" event, -and, depending on execution, the "render" event; when done, it triggers the "finish" event, and then -returns the response instance. If an error occurs during either the "route" or "dispatch" event, a -"dispatch.error" event is triggered as well. - -This is a lot to remember in order to bootstrap the application; in fact, we haven't covered all the -services available by default yet. You can greatly simplify things by using the default -`ServiceManager` configuration shipped with the MVC. - -```php -use Zend\Loader\AutoloaderFactory; -use Zend\Mvc\Service\ServiceManagerConfig; -use Zend\ServiceManager\ServiceManager; - -// setup autoloader -AutoloaderFactory::factory(); - -// get application stack configuration -$configuration = include 'config/application.config.php'; - -// setup service manager -$serviceManager = new ServiceManager(new ServiceManagerConfig()); -$serviceManager->setService('ApplicationConfig', $configuration); - -// load modules -- which will provide services, configuration, and more -$serviceManager->get('ModuleManager')->loadModules(); - -// bootstrap and run application -$application = $serviceManager->get('Application'); -$application->bootstrap(); -$application->run(); -``` - -You can make this even simpler by using the `init()` method of the `Application`. This is a static -method for quick and easy initialization of the Application. - -```php -use Zend\Loader\AutoloaderFactory; -use Zend\Mvc\Application; -use Zend\Mvc\Service\ServiceManagerConfig; -use Zend\ServiceManager\ServiceManager; - -// setup autoloader -AutoloaderFactory::factory(); - -// get application stack configuration -$configuration = include 'config/application.config.php'; - -// The init() method does something very similar with the previous example. -Application::init($configuration)->run(); -``` - -The `init()` method will basically do the following: - -* Grabs the application configuration and pulls from the `service_manager` key, creating a -`ServiceManager` instance with it and with the default services shipped with `Zend\Mvc`; -* Create a service named `ApplicationConfig` with the application configuration array; -* Grabs the `ModuleManager` service and load the modules; -* `bootstrap()`s the `Application` and returns its instance; - -> ### Note -If you use the `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. - -You'll note that you have a great amount of control over the workflow. Using the `ServiceManager`, -you have fine-grained control over what services are available, how they are instantiated, and what -dependencies are injected into them. Using the `EventManager`'s priority system, you can intercept -any of the application events ("bootstrap", "route", "dispatch", "dispatch.error", "render", and -"finish") anywhere during execution, allowing you to craft your own application workflows as needed. - -## Bootstrapping a Modular Application - -While the previous approach largely works, where does the configuration come from? When we create a -modular application, the assumption will be that it's from the modules themselves. How do we get -that information and aggregate it, then? - -The answer is via `Zend\ModuleManager\ModuleManager`. This component allows you to specify where -modules exist. Then, it will locate each module and initialize it. Module classes can tie into -various listeners on the `ModuleManager` in order to provide configuration, services, listeners, and -more to the application. Sounds complicated? It's not. - -### Configuring the Module Manager - -The first step is configuring the module manager. Simply inform the module manager which modules to -load, and potentially provide configuration for the module listeners. - -Remember the `application.config.php` from earlier? We're going to provide some configuration. - -```php - array( - /* ... */ - ), - 'module_listener_options' => array( - 'module_paths' => array( - './module', - './vendor', - ), - ), -); -``` - -As we add modules to the system, we'll add items to the `modules` array. - -Each `Module` class that has configuration it wants the `Application` to know about should define a -`getConfig()` method. That method should return an array or `Traversable` object such as -`Zend\Config\Config`. As an example: - -```php -namespace ZendUser; - -class Module -{ - public function getConfig() - { - return include __DIR__ . '/config/module.config.php' - } -} -``` - -There are a number of other methods you can define for tasks ranging from providing autoloader -configuration, to providing services to the `ServiceManager`, to listening to the bootstrap event. -The ModuleManager -documentation <zend.module-manager.intro> goes into more detail on these. - -## Conclusion - -The ZF2 MVC layer is incredibly flexible, offering an opt-in, easy to create modular infrastructure, -as well as the ability to craft your own application workflows via the `ServiceManager` and -`EventManager`. The `ModuleManager` is a lightweight and simple approach to enforcing a modular -architecture that encourages clean separation of concerns and code re-use. diff --git a/doc/book/zend.mvc.mvc-event.md b/doc/book/zend.mvc.mvc-event.md deleted file mode 100644 index 5bdccaacb..000000000 --- a/doc/book/zend.mvc.mvc-event.md +++ /dev/null @@ -1,282 +0,0 @@ -# The MvcEvent - -The MVC layer of Zend Framework 2 incorporates and utilizes a custom `Zend\EventManager\Event` -implementation -`Zend\Mvc\MvcEvent`. This event is created during -`Zend\Mvc\Application::bootstrap()` and is passed directly to all the events that method triggers. -Additionally, if your controllers implement the `Zend\Mvc\InjectApplicationEventInterface`, -`MvcEvent` will be injected into those controllers. - -The `MvcEvent` adds accessors and mutators for the following: - -* `Application` object. -* `Request` object. -* `Response` object. -* `Router` object. -* `RouteMatch` object. -* Result - usually the result of dispatching a controller. -* `ViewModel` object, typically representing the layout view model. - -The methods it defines are: - -* `setApplication($application)` -* `getApplication()` -* `setRequest($request)` -* `getRequest()` -* `setResponse($response)` -* `getResponse()` -* `setRouter($router)` -* `getRouter()` -* `setRouteMatch($routeMatch)` -* `getRouteMatch()` -* `setResult($result)` -* `getResult()` -* `setViewModel($viewModel)` -* `getViewModel()` -* `isError()` -* `setError()` -* `getError()` -* `getController()` -* `setController($name)` -* `getControllerClass()` -* `setControllerClass($class)` - -The `Application`, `Request`, `Response`, `Router`, and `ViewModel` are all injected during the -`bootstrap` event. Following the `route` event, it will be injected also with the `RouteMatch` -object encapsulating the results of routing. - -Since this object is passed around throughout the MVC, it is a common location for retrieving the -results of routing, the router, and the request and response objects. Additionally, we encourage -setting the results of execution in the event, to allow event listeners to introspect them and -utilize them within their execution. As an example, the results could be passed into a view -renderer. - -## Order of events - -The following events are triggered, in the following order: - -| Name | Constant | Description | -|------------------|----------------------------------|----------------------------------------------------------------------------------------| -| `bootstrap` | `MvcEvent::EVENT_BOOTSTRAP` | Bootstrap the application by creating the ViewManager. | -| `route` | `MvcEvent::EVENT_ROUTE` | Perform all the route work (matching...). | -| `dispatch` | `MvcEvent::EVENT_DISPATCH` | Dispatch the matched route to a controller/action. | -| `dispatch.error` | `MvcEvent::EVENT_DISPATCH_ERROR` | Event triggered in case of a problem during dispatch process (unknown controller...). | -| `render` | `MvcEvent::EVENT_RENDER` | Prepare the data and delegate the rendering to the view layer. | -| `render.error` | `MvcEvent::EVENT_RENDER_ERROR` | Event triggered in case of a problem during the render process (no renderer found...). | -| `finish` | `MvcEvent::EVENT_FINISH` | Perform any task once everything is done. | - -Those events are extensively describe in the following sections. - -## MvcEvent::EVENT\_BOOTSTRAP - -### Listeners - -The following classes are listening to this event (they are sorted from higher priority to lower -priority): - -| Class | Priority | Method Called | Itself Triggers | Description | -|----------------------------------|----------|---------------|-----------------|---------------------------------------------------------------------------| -| `Zend\Mvc\View\Http\ViewManager` | 10000 | `onBootstrap` | none | Prepares the view layer (instantiate a `Zend\Mvc\View\Http\ViewManager`). | - -### Triggerers - -This event is triggered by the following classes: - -| Class | In Method | -|------------------------|-------------| -| `Zend\Mvc\Application` | `bootstrap` | - -## MvcEvent::EVENT\_ROUTE - -### Listeners - -The following classes are listening to this event (they are sorted from higher priority to lower -priority): - -| Class | Priority | Method Called | Itself Triggers | Description | -|--------------------------------|----------|---------------|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\ModuleRouteListener` | 1 | `onRoute` | none | This listener determines if the module namespace should be prepended to the controller name. This is the case if the route match contains a parameter key matching the `MODULE_NAMESPACE` constant. | -| `Zend\Mvc\RouteListener` | 1 | `onRoute` | `MvcEvent::EVENT_DISPATCH_ERROR` (if no route is matched) | Tries to match the request to the router and return a RouteMatch object. | - -### Triggerers - -This event is triggered by the following classes: - -| Class | In Method | Description | -|------------------------|-----------|---------------------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\Application` | `run` | It also has a short circuit callback that allows to stop the propagation of the event if an error is raised during the routing. | - -## MvcEvent::EVENT\_DISPATCH - -### Listeners - -The following classes are listening to this event (they are sorted from higher priority to lower -priority): - -#### Console context only - -Those listeners are only attached in a Console context: - -| Class | Priority | Method Called | Description | -|----------------------------------------------------------|----------|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\View\Console\InjectNamedConsoleParamsListener` | 1000 | `injectNamedParams` | Merge all the params (route matched params and params in the command) and add them to the Request object. | -| `Zend\Mvc\View\Console\CreateViewModelListener` | -80 | `createViewModelFromArray` | If the controller action returned an associative array, it casts it to a `ConsoleModel` object. | -| `Zend\Mvc\View\Console\CreateViewModelListener` | -80 | `createViewModelFromString` | If the controller action returned a string, it casts it to a `ConsoleModel` object. | -| `Zend\Mvc\View\Console\CreateViewModelListener` | -80 | `createViewModelFromNull` | If the controller action returned null, it casts it to a `ConsoleModel` object. | -| `Zend\Mvc\View\Console\InjectViewModelListener` | -100 | `injectViewModel` | Inserts the `ViewModel` (in this case, a `ConsoleModel`) and adds it to the MvcEvent object. It either (a) adds it as a child to the default, composed view model, or (b) replaces it if the result is marked as terminable. | - -#### Http context only - -Those listeners are only attached in a Http context: - -| Class | Priority | Method Called | Description | -|----------------------------------------------|----------|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\View\Http\CreateViewModelListener` | -80 | `createViewModelFromArray` | If the controller action returned an associative array, it casts it to a `ViewModel` object. | -| `Zend\Mvc\View\Http\CreateViewModelListener` | -80 | `createViewModelFromNull` | If the controller action returned null, it casts it to a `ViewModel` object. | -| `Zend\Mvc\View\Http\RouteNotFoundStrategy` | -90 | `prepareNotFoundViewModel` | It creates and return a 404 `ViewModel`. | -| `Zend\Mvc\View\Http\InjectTemplateListener` | -90 | `injectTemplate` | Inject a template into the view model, if none present. Template is derived from the controller found in the route match, and, optionally, the action, if present. | -| `Zend\Mvc\View\Http\InjectViewModelListener` | -100 | `injectViewModel` | Inserts the `ViewModel` (in this case, a `ViewModel`) and adds it to the MvcEvent object. It either (a) adds it as a child to the default, composed view model, or (b) replaces it if the result is marked as terminable. | - -#### All contexts - -Those listeners are attached for both contexts: - -| Class | Priority | Method Called | Itself Triggers | Description | -|-------------------------------|----------|---------------|----------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\DispatchListener` | 1 | `onDispatch` | `MvcEvent::EVENT_DISPATCH_ERROR` (if an exception is raised during dispatch processes) | Try to load the matched controller from the service manager (and throws various exceptions if it does not). | -| `Zend\Mvc\AbstractController` | 1 | `onDispatch` | none | The `onDispatch` method of the `AbstractController` is an abstract method. In `AbstractActionController` for instance, it simply calls the action method. | - -### Triggerers - -This event is triggered by the following classes: - -| Class | In Method | Description | -|------------------------------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\Application` | `run` | It also has a short circuit callback that allows to stop the propagation of the event if an error is raised during the routing. | -| `Zend\Mvc\Controller\AbstractController` | `dispatch` | If a listener returns a `Response` object, it stops propagation. Note: every `AbstractController` listen to this event and execute the `onDispatch` method when it is triggered. | - -## MvcEvent::EVENT\_DISPATCH\_ERROR - -### Listeners - -The following classes are listening to this event (they are sorted from higher priority to lower -priority): - -#### Console context only - -| Class | Priority | Method Called | Description | -|-------------------------------------------------|----------|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\View\Console\RouteNotFoundStrategy` | 1 | `handleRouteNotFoundError ` | Detect if an error is a route not found condition. If a “controller not found” or “invalid controller” error type is encountered, sets the response status code to 404. | -| `Zend\Mvc\View\Console\ExceptionStrategy` | 1 | `prepareExceptionViewModel` | Create an exception view model and set the status code to 404. | -| `Zend\Mvc\View\Console\InjectViewModelListener` | -100 | `injectViewModel` | Inserts the `ViewModel` (in this case, a `ConsoleModel`) and adds it to the MvcEvent object. It either (a) adds it as a child to the default, composed view model, or (b) replaces it if the result is marked as terminable. | - -#### Http context only - -Those listeners are only attached in a Http context: - -| Class | Priority | Method Called | Description | -|----------------------------------------------|----------|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\View\Http\RouteNotFoundStrategy` | 1 | `detectNotFoundError` | Detect if an error is a 404 condition. If a “controller not found” or “invalid controller” error type is encountered, sets the response status code to 404. | -| `Zend\Mvc\View\Http\RouteNotFoundStrategy` | 1 | `prepareNotFoundViewModel` | Create and return a 404 view model. | -| `Zend\Mvc\View\Http\ExceptionStrategy` | 1 | `prepareExceptionViewModel` | Create an exception view model and set the status code to 404 | -| `Zend\Mvc\View\Http\InjectViewModelListener` | -100 | `injectViewModel` | Inserts the `ViewModel` (in this case, a `ViewModel`) and adds it to the MvcEvent object. It either (a) adds it as a child to the default, composed view model, or (b) replaces it if the result is marked as terminable. | - -#### All contexts - -Those listeners are attached for both contexts: - -| Class | Priority | Method Called | Description | -|-----------------------------|----------|----------------------|----------------------------------------------| -| `Zend\Mvc\DispatchListener` | 1 | `reportMonitorEvent` | Used to monitoring when Zend Server is used. | - -### Triggerers - -| Class | In Method | -|-----------------------------|-----------------------------------| -| `Zend\Mvc\DispatchListener` | `onDispatch` | -| `Zend\Mvc\DispatchListener` | `marshallControllerNotFoundEvent` | -| `Zend\Mvc\DispatchListener` | `marshallBadControllerEvent` | - -## MvcEvent::EVENT\_RENDER - -### Listeners - -The following classes are listening to this event (they are sorted from higher priority to lower -priority): - -#### Console context only - -Those listeners are only attached in a Console context: - -| Class | Priority | Method Called | Description | -|--------------------------------------------------|----------|---------------|------------------| -| `Zend\Mvc\View\Console\DefaultRenderingStrategy` | -10000 | `render` | Render the view. | - -#### Http context only - -Those listeners are only attached in a Http context: - -| Class | Priority | Method Called | Description | -|-----------------------------------------------|----------|---------------|------------------| -| `Zend\Mvc\View\Http\DefaultRenderingStrategy` | -10000 | `render` | Render the view. | - -### Triggerers - -This event is triggered by the following classes: - -| Class | In Method | Description | -|------------------------|-------------------|-------------------------------------------------------------------| -| `Zend\Mvc\Application` | `completeRequest` | This event is triggered just before the `MvcEvent::FINISH` event. | - -## MvcEvent::EVENT\_RENDER\_ERROR - -### Listeners - -The following classes are listening to this event (they are sorted from higher priority to lower -priority): - -#### Console context only - -Those listeners are only attached in a Console context: - -| Class | Priority | Method Called | Description | -|-------------------------------------------------|----------|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\View\Console\ExceptionStrategy` | 1 | `prepareExceptionViewModel` | Create an exception view model and set the status code to 404. | -| `Zend\Mvc\View\Console\InjectViewModelListener` | -100 | `injectViewModel` | Inserts the `ViewModel` (in this case, a `ConsoleModel`) and adds it to the MvcEvent object. It either (a) adds it as a child to the default, composed view model, or (b) replaces it if the result is marked as terminable. | - -#### Http context only - -Those listeners are only attached in a Http context: - -| Class | Priority | Method Called | Description | -|-------------------------------------------------|----------|-----------------------------|----------------------------------------------------------------| -| `Zend\Mvc\View\Console\ExceptionStrategy` | 1 | `prepareExceptionViewModel` | Create an exception view model and set the status code to 404. | -| `Zend\Mvc\View\Console\InjectViewModelListener` | | | | - -### Triggerers - -This event is triggered by the following classes: - -| Class | In Method | Description | -|-----------------------------------------------|-----------|---------------------------------------------------------------------| -| `Zend\Mvc\View\Http\DefaultRenderingStrategy` | `render` | This event is triggered if an exception is raised during rendering. | - -## MvcEvent::EVENT\_FINISH - -### Listeners - -The following classes are listening to this event (they are sorted from higher priority to lower -priority): - -| Class | Priority | Method Called | Description | -|---------------------------------|----------|----------------|------------------------------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\SendResponseListener` | -10000 | `sendResponse` | It triggers the `SendResponseEvent` in order to prepare the response (see the next page for more information about `SendResponseEvent`). | - -### Triggerers - -This event is triggered by the following classes: - -| Class | In Method | Description | -|------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------| -| `Zend\Mvc\Application` | `run` | This event is triggered once the `MvcEvent::ROUTE` event returns a correct `ResponseInterface`. | -| `Zend\Mvc\Application` | `run` | This event is triggered once the `MvcEvent::DISPATCH` event returns a correct `ResponseInterface`. | -| `Zend\Mvc\Application` | `completeRequest` | This event is triggered after the `MvcEvent::RENDER` (this means that, at this point, the view is already rendered). | diff --git a/doc/book/zend.mvc.plugins.md b/doc/book/zend.mvc.plugins.md deleted file mode 100644 index 01fa6f293..000000000 --- a/doc/book/zend.mvc.plugins.md +++ /dev/null @@ -1,545 +0,0 @@ -# Controller Plugins - -When using the `AbstractActionController` or `AbstractRestfulController`, or if you implement the -`setPluginManager` method in your custom controllers, you have access to a number of pre-built -plugins. Additionally, you can register your own custom plugins with the manager. - -The built-in plugins are: - -* \[Zend\\Mvc\\Controller\\Plugin\\AcceptableViewModelSelector\](zend.mvc.controller-plugins.acceptableviewmodelselector) -* \[Zend\\Mvc\\Controller\\Plugin\\FlashMessenger\](zend.mvc.controller-plugins.flashmessenger) -* \[Zend\\Mvc\\Controller\\Plugin\\Forward\](zend.mvc.controller-plugins.forward) -* \[Zend\\Mvc\\Controller\\Plugin\\Identity\](zend.mvc.controller-plugins.identity) -* \[Zend\\Mvc\\Controller\\Plugin\\Layout\](zend.mvc.controller-plugins.layout) -* \[Zend\\Mvc\\Controller\\Plugin\\Params\](zend.mvc.controller-plugins.params) -* \[Zend\\Mvc\\Controller\\Plugin\\PostRedirectGet\](zend.mvc.controller-plugins.postredirectget) -* \[Zend\\Mvc\\Controller\\Plugin\\Redirect\](zend.mvc.controller-plugins.redirect) -* \[Zend\\Mvc\\Controller\\Plugin\\Url\](zend.mvc.controller-plugins.url) - -If your controller implements the `setPluginManager`, `getPluginManager` and `plugin` methods, you -can access these using their shortname via the `plugin()` method: - -```php -$plugin = $this->plugin('url'); -``` - -For an extra layer of convenience, both `AbstractActionController` and `AbstractRestfulController` -have `__call()` implementations that allow you to retrieve plugins via method calls: - -```php -$plugin = $this->url(); -``` - -## AcceptableViewModelSelector Plugin - -The `AcceptableViewModelSelector` is a helper that can be used to select an appropriate view model -based on user defined criteria will be tested against the Accept header in the request. - -As an example: - -```php -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\JsonModel; - -class SomeController extends AbstractActionController -{ - protected $acceptCriteria = array( - 'Zend\View\Model\JsonModel' => array( - 'application/json', - ), - 'Zend\View\Model\FeedModel' => array( - 'application/rss+xml', - ), - ); - - public function apiAction() - { - $viewModel = $this->acceptableViewModelSelector($this->acceptCriteria); - - // Potentially vary execution based on model returned - if ($viewModel instanceof JsonModel) { - // ... - } - } -} -``` - -The above would return a standard `Zend\View\Model\ViewModel` instance if the criteria is not met, -and the specified view model types if the specific criteria is met. Rules are matched in order, with -the first match "winning." - -## FlashMessenger Plugin - -The `FlashMessenger` is a plugin designed to create and retrieve self-expiring, session-based -messages. It exposes a number of methods: - -setSessionManager(Zend\\Session\\ManagerInterface $manager) - -> Allows you to specify an alternate session manager, if desired. -rtype -`Zend\Mvc\Controller\Plugin\FlashMessenger` -getSessionManager() - -> Allows you to retrieve the session manager registered. -rtype -`Zend\Session\ManagerInterface` -getContainer() - -> Returns the `Zend\Session\Container` instance in which the flash messages are stored. -rtype -`Zend\Session\Container` -setNamespace(string $namespace = 'default') - -> Allows you to specify a specific namespace in the container in which to store or from which to -retrieve flash messages. -rtype -`Zend\Mvc\Controller\Plugin\FlashMessenger` -- `getNamespace()` retrieves the name of the flash message namespace. - -getNamespace() - -> Retrieves the name of the flash message namespace. -rtype -`string` -addMessage(string $message) - -> Allows you to add a message to the current namespace of the session container. -rtype -`Zend\Mvc\Controller\Plugin\FlashMessenger` -hasMessages() - -> Lets you determine if there are any flash messages from the current namespace in the session -container. -rtype -`boolean` -getMessages() - -> Retrieves the flash messages from the current namespace of the session container -rtype -`array` -clearMessages() - -> Clears all flash messages in current namespace of the session container. Returns `true` if -messages were cleared, `false` if none existed. -rtype -`boolean` -hasCurrentMessages() - -> Indicates whether any messages were added during the current request. -rtype -`boolean` -getCurrentMessages() - -> Retrieves any messages added during the current request. -rtype -`array` -clearCurrentMessages() - -> Removes any messages added during the current request. Returns `true` if current messages were -cleared, `false` if none existed. -rtype -`boolean` -clearMessagesFromContainer() - -> Clear all messages from the container. Returns `true` if any messages were cleared, `false` if -none existed. -rtype -`boolean` -This plugin also provides four meaningful namespaces, namely: INFO, ERROR, WARNING, SUCCESS. The -following functions are related to these namespaces: - -addInfoMessage() - -> Add a message to "info" namespace - -> rtype -`Zend\Mvc\Controller\Plugin\FlashMessenger` -hasCurrentInfoMessages() - -> Check to see if messages have been added to "info" namespace within this request - -> rtype -`boolean` -addWarningMessage() - -> Add a message to "warning" namespace - -> rtype -`Zend\Mvc\Controller\Plugin\FlashMessenger` -hasCurrentWarningMessages() - -> Check to see if messages have been added to "warning" namespace within this request - -> rtype -`boolean` -addErrorMessage() - -> Add a message to "error" namespace - -> rtype -`Zend\Mvc\Controller\Plugin\FlashMessenger` -hasCurrentErrorMessages() - -> Check to see if messages have been added to "error" namespace within this request - -> rtype -`boolean` -addSuccessMessage() - -> Add a message to "success" namespace - -> rtype -`Zend\Mvc\Controller\Plugin\FlashMessenger` -hasCurrentSuccessMessages() - -> Check to see if messages have been added to "success" namespace within this request - -> rtype -`boolean` -Additionally, the `FlashMessenger` implements both `IteratorAggregate` and `Countable`, allowing you -to iterate over and count the flash messages in the current namespace within the session container. - -### Examples - -```php -public function processAction() -{ - // ... do some work ... - $this->flashMessenger()->addMessage('You are now logged in.'); - return $this->redirect()->toRoute('user-success'); -} - -public function successAction() -{ - $return = array('success' => true); - $flashMessenger = $this->flashMessenger(); - if ($flashMessenger->hasMessages()) { - $return['messages'] = $flashMessenger->getMessages(); - } - return $return; -} -``` - -## Forward Plugin - -Occasionally, you may want to dispatch additional controllers from within the matched controller -- -for instance, you might use this approach to build up "widgetized" content. The `Forward` plugin -helps enable this. - -For the `Forward` plugin to work, the controller calling it must be `ServiceLocatorAware`; -otherwise, the plugin will be unable to retrieve a configured and injected instance of the requested -controller. - -The plugin exposes a single method, `dispatch()`, which takes two arguments: - -* `$name`, the name of the controller to invoke. This may be either the fully qualified class name, -or an alias defined and recognized by the `ServiceManager` instance attached to the invoking -controller. -* `$params` is an optional array of parameters with which to seed a `RouteMatch` object for purposes -of this specific request. Meaning the parameters will be matched by their key to the routing -identifiers in the config (otherwise non-matching keys are ignored) - -`Forward` returns the results of dispatching the requested controller; it is up to the developer to -determine what, if anything, to do with those results. One recommendation is to aggregate them in -any return value from the invoking controller. - -As an example: - -```php -$foo = $this->forward()->dispatch('foo', array('action' => 'process')); -return array( - 'somekey' => $somevalue, - 'foo' => $foo, -); -``` - -## Identity Plugin - -The `Identity` plugin allows for getting the identity from the `AuthenticationService`. - -For the `Identity` plugin to work, a `Zend\Authentication\AuthenticationService` name or alias must -be defined and recognized by the `ServiceManager`. - -`Identity` returns the identity in the `AuthenticationService` or null if no identity is available. - -As an example: - -```php -public function testAction() -{ - if ($user = $this->identity()) { - // someone is logged ! - } else { - // not logged in - } -} -``` - -When invoked, the `Identity` plugin will look for a service by the name or alias -`Zend\Authentication\AuthenticationService` in the `ServiceManager`. You can provide this service to -the `ServiceManager` in a configuration file: - -```php -// In a configuration file... -return array( - 'service_manager' => array( - 'aliases' => array( - 'Zend\Authentication\AuthenticationService' => 'my_auth_service', - ), - 'invokables' => array( - 'my_auth_service' => 'Zend\Authentication\AuthenticationService', - ), - ), -); -``` - -The `Identity` plugin exposes two methods: - -setAuthenticationService(Zend\\Authentication\\AuthenticationService $authenticationService) - -> Sets the authentication service instance to be used by the plugin. -rtype -`void` -getAuthenticationService() - -> Retrieves the current authentication service instance if any is attached. -rtype -`Zend\Authentication\AuthenticationService` - -## Layout Plugin - -The `Layout` plugin allows for changing layout templates from within controller actions. - -It exposes a single method, `setTemplate()`, which takes one argument: - -* `$template`, the name of the template to set. - -As an example: - -```php -$this->layout()->setTemplate('layout/newlayout'); -``` - -It also implements the `__invoke` magic method, which allows for even easier setting of the -template: - -```php -$this->layout('layout/newlayout'); -``` - -## Params Plugin - -The `Params` plugin allows for accessing parameters in actions from different sources. - -It exposes several methods, one for each parameter source: - -fromFiles(string $name = null, mixed $default = null) - -> For retrieving all or one single **file**. If `$name` is null, all files will be returned. -rtype -`array|ArrayAccess|null` -fromHeader(string $header = null, mixed $default = null) - -> For retrieving all or one single **header** parameter. If `$header` is null, all header parameters -will be returned. -rtype -`null|Zend\Http\Header\HeaderInterface` -fromPost(string $param = null, mixed $default = null) - -> For retrieving all or one single **post** parameter. If `$param` is null, all post parameters will -be returned. -rtype -`mixed` -fromQuery(string $param = null, mixed $default = null) - -> For retrieving all or one single **query** parameter. If `$param` is null, all query parameters -will be returned. -rtype -`mixed` -fromRoute(string $param = null, mixed $default = null) - -> For retrieving all or one single **route** parameter. If `$param` is null, all route parameters -will be returned. -rtype -`mixed` -It also implements the `__invoke` magic method, which allows for short circuiting to the `fromRoute` -method: - -```php -$this->params()->fromRoute('param', $default); -// or -$this->params('param', $default); -``` - -## Post/Redirect/Get Plugin - -When a user sends a POST request (e.g. after submitting a form), their browser will try to protect -them from sending the POST again, breaking the back button, causing browser warnings and pop-ups, -and sometimes reposting the form. Instead, when receiving a POST, we should store the data in a -session container and redirect the user to a GET request. - -This plugin can be invoked with two arguments: - -* `$redirect`, a string containing the redirect location which can either be a named route or a URL, -based on the contents of the second parameter. -* `$redirectToUrl`, a boolean that when set to TRUE, causes the first parameter to be treated as a -URL instead of a route name (this is required when redirecting to a URL instead of a route). This -argument defaults to false. - -When no arguments are provided, the current matched route is used. - -### Example Usage - -```php -// Pass in the route/url you want to redirect to after the POST -$prg = $this->prg('/user/register', true); - -if ($prg instanceof \Zend\Http\PhpEnvironment\Response) { - // returned a response to redirect us - return $prg; -} elseif ($prg === false) { - // this wasn't a POST request, but there were no params in the flash messenger - // probably this is the first time the form was loaded - return array('form' => $myForm); -} - -// $prg is an array containing the POST params from the previous request -$form->setData($prg); - -// ... your form processing code here -``` - -## File Post/Redirect/Get Plugin - -While similar to the standard \[Post/Redirect/Get -Plugin\](zend.mvc.controller-plugins.postredirectget), the File PRG Plugin will work for forms with -file inputs. The difference is in the behavior: The File PRG Plugin will interact directly with your -form instance and the file inputs, rather than *only* returning the POST params from the previous -request. - -By interacting directly with the form, the File PRG Plugin will turn off any file inputs' `required` -flags for already uploaded files (for a partially valid form state), as well as run the file input -filters to move the uploaded files into a new location (configured by the user). - -> ### Warning -You **must** attach a Filter for moving the uploaded files to a new location, such as the -\[RenameUpload Filter\](zend.filter.file.rename-upload), or else your files will be removed upon the -redirect. - -This plugin can be invoked with three arguments: - -* `$form`: the form instance. -* `$redirect`: (Optional) a string containing the redirect location which can either be a named -route or a URL, based on the contents of the third parameter. If this argument is not provided, it -will default to the current matched route. -* `$redirectToUrl`: (Optional) a boolean that when set to TRUE, causes the second parameter to be -treated as a URL instead of a route name (this is required when redirecting to a URL instead of a -route). This argument defaults to false. - -### Example Usage - -```php -$myForm = new Zend\Form\Form('my-form'); -$myForm->add(array( - 'type' => 'Zend\Form\Element\File', - 'name' => 'file', -)); -// NOTE: Without a filter to move the file, -// our files will disappear between the requests -$myForm->getInputFilter()->getFilterChain()->attach( - new Zend\Filter\File\RenameUpload(array( - 'target' => './data/tmpuploads/file', - 'randomize' => true, - )) -); - -// Pass in the form and optional the route/url you want to redirect to after the POST -$prg = $this->fileprg($myForm, '/user/profile-pic', true); - -if ($prg instanceof \Zend\Http\PhpEnvironment\Response) { - // Returned a response to redirect us - return $prg; -} elseif ($prg === false) { - // First time the form was loaded - return array('form' => $myForm); -} - -// Form was submitted. -// $prg is now an array containing the POST params from the previous request, -// but we don't have to apply it to the form since that has already been done. - -// Process the form -if ($form->isValid()) { - // ...Save the form... - return $this->redirect()->toRoute('/user/profile-pic/success'); -} else { - // Form not valid, but file uploads might be valid and uploaded - $fileErrors = $form->get('file')->getMessages(); - if (empty($fileErrors)) { - $tempFile = $form->get('file')->getValue(); - } -} -``` - -## Redirect Plugin - -Redirections are quite common operations within applications. If done manually, you will need to do -the following steps: - -* Assemble a url using the router -* Create and inject a "Location" header into the `Response` object, pointing to the assembled URL -* Set the status code of the `Response` object to one of the 3xx HTTP statuses. - -The `Redirect` plugin does this work for you. It offers three methods: - -toRoute(string $route = null, array $params = array(), array $options = array(), boolean -$reuseMatchedParams = false) - -> Redirects to a named route, using the provided `$params` and `$options` to assembled the URL. -rtype -`Zend\Http\Response` -toUrl(string $url) - -> Simply redirects to the given URL. -rtype -`Zend\Http\Response` -refresh() - -> Refresh to current route -rtype -`Zend\Http\Response` -In each case, the `Response` object is returned. If you return this immediately, you can effectively -short-circuit execution of the request. - -> ### Note -This plugin requires that the controller invoking it implements `InjectApplicationEventInterface`, -and thus has an `MvcEvent` composed, as it retrieves the router from the event object. - -As an example: - -```php -return $this->redirect()->toRoute('login-success'); -``` - -## Url Plugin - -Often you may want to generate URLs from route definitions within your controllers -- in order to -seed the view, generate headers, etc. While the `MvcEvent` object composes the router, doing so -manually would require this workflow: - -```php -$router = $this->getEvent()->getRouter(); -$url = $router->assemble($params, array('name' => 'route-name')); -``` - -The `Url` helper makes this slightly more convenient: - -```php -$url = $this->url()->fromRoute('route-name', $params); -``` - -The `fromRoute()` method is the only public method defined, and has the following signature: - -> ### Note -This plugin requires that the controller invoking it implements `InjectApplicationEventInterface`, -and thus has an `MvcEvent` composed, as it retrieves the router from the event object. diff --git a/doc/book/zend.mvc.quick-start.md b/doc/book/zend.mvc.quick-start.md deleted file mode 100644 index abeca3dea..000000000 --- a/doc/book/zend.mvc.quick-start.md +++ /dev/null @@ -1,382 +0,0 @@ -# Quick Start - -Now that you have basic knowledge of applications, modules, and how they are each structured, we'll -show you the easy way to get started. - -## Install the Zend Skeleton Application - -The easiest way to get started is to grab the sample application and module repositories. This can -be done in the following ways. - -### Using Composer - -Simply clone the `ZendSkeletonApplication` repository: - -```bash -prompt> git clone git://github.com/zendframework/ZendSkeletonApplication.git my-application -``` - -Then run [Composer](http://getcomposer.org/)'s `install` command to install the ZF library and any -other configured dependencies: - -```bash -prompt> php ./composer.phar install -``` - -### Using Git - -Simply clone the `ZendSkeletonApplication` repository, using the `--recursive` option, which will -also grab ZF. - -```bash -prompt> git clone --recursive git://github.com/zendframework/ZendSkeletonApplication.git -my-application -``` - -### Manual Installation - -* Download a tarball of the `ZendSkeletonApplication` repository: -* Zip: -* Tarball: -* Deflate the archive you selected and rename the parent directory according to your project needs; -we use "my-application" throughout this document. -* Install Zend Framework, and either have its library on your PHP `include_path`, symlink the -library into your project's "library", or install it directly into your application using Pyrus. - -## Create a New Module - -By default, one module is provided with the `ZendSkeletonApplication`, named "Application". It -simply provides a controller to handle the "home" page of the application, the layout template, and -templates for 404 and error pages. - -Typically, you will not need to touch this other than to provide an alternate entry page for your -site and/or alternate error page. - -Additional functionality will be provided by creating new modules. - -To get you started with modules, we recommend using the `ZendSkeletonModule` as a base. Download it -from here: - -* Zip: -* Tarball: - -Deflate the package, and rename the directory "ZendSkeletonModule" to reflect the name of the new -module you want to create; when done, move the module into your new project's `module/` directory. - -At this point, it's time to create some functionality. - -## Update the Module Class - -Let's update the module class. We'll want to make sure the namespace is correct, configuration is -enabled and returned, and that we setup autoloading on initialization. Since we're actively working -on this module, the class list will be in flux, we probably want to be pretty lenient in our -autoloading approach, so let's keep it flexible by using the `StandardAutoloader`. Let's begin. - -First, let's have `autoload_classmap.php` return an empty array: - -```php - array( - 'template_path_stack' => array( - '' => __DIR__ . '/../view' - ), - ), -); -``` - -Fill in "module-name" with a lowercased, dash-separated version of your module name -- e.g., -"ZendUser" would become "zend-user". - -Next, edit the `Module.php` file to read as follows: - -```php -namespace ; - -use Zend\ModuleManager\Feature\AutoloaderProviderInterface; -use Zend\ModuleManager\Feature\ConfigProviderInterface; - -class Module implements AutoloaderProviderInterface, ConfigProviderInterface -{ - public function getAutoloaderConfig() - { - return array( - 'Zend\Loader\ClassMapAutoloader' => array( - __DIR__ . '/autoload_classmap.php', - ), - 'Zend\Loader\StandardAutoloader' => array( - 'namespaces' => array( - __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__, - ), - ), - ); - } - - public function getConfig() - { - return include __DIR__ . '/config/module.config.php'; - } -} -``` - -At this point, you now have your module configured properly. Let's create a controller! - -## Create a Controller - -Controllers are simply objects that implement `Zend\Stdlib\DispatchableInterface`. This means they -need to implement a `dispatch()` method that takes minimally a `Request` object as an argument. - -In practice, though, this would mean writing logic to branch based on matched routing within every -controller. As such, we've created two base controller classes for you to start with: - -* `Zend\Mvc\Controller\AbstractActionController` allows routes to match an "action". When matched, a -method named after the action will be called by the controller. As an example, if you had a route -that returned "foo" for the "action" key, the "fooAction" method would be invoked. -* `Zend\Mvc\Controller\AbstractRestfulController` introspects the `Request` to determine what HTTP -method was used, and calls a method according to that. -* `GET` will call either the `getList()` method, or, if an "id" was matched during routing, the -`get()` method (with that identifer value). -* `POST` will call the `create()` method, passing in the `$_POST` values. -* `PUT` expects an "id" to be matched during routing, and will call the `update()` method, passing -in the identifier, and any data found in the raw post body. -* `DELETE` expects an "id" to be matched during routing, and will call the `delete()` method. - -To get started, we'll simply create a "hello world"-style controller, with a single action. First, -create the directory `src//Controller`, and then create the file `HelloController.php` -inside it. Edit it in your favorite text editor or IDE, and insert the following contents: - -```php -\Controller; - -use Zend\Mvc\Controller\AbstractActionController; -use Zend\View\Model\ViewModel; - -class HelloController extends AbstractActionController -{ - public function worldAction() - { - $message = $this->params()->fromQuery('message', 'foo'); - return new ViewModel(array('message' => $message)); - } -} -``` - -So, what are we doing here? - -* We're creating an action controller. -* We're defining an action, "world". -* We're pulling a message from the query parameters (yes, this is a superbly bad idea in production! -Always sanitize your inputs!). -* We're returning a ViewModel with an array of values to be processed later. - -We return a `ViewModel`. The view layer will use this when rendering the view, pulling variables and -the template name from it. By default, you can omit the template name, and it will resolve to -"lowercase-controller-name/lowercase-action-name". However, you can override this to specify -something different by calling `setTemplate()` on the `ViewModel` instance. Typically, templates -will resolve to files with a ".phtml" suffix in your module's `view` directory. - -So, with that in mind, let's create a view script. - -## Create a View Script - -Create the directory `view//hello`. Inside that directory, create a file named -`world.phtml`. Inside that, paste in the following: - -```php -

Greetings!

- -

You said "escapeHtml($message) ?>".

-``` - -That's it. Save the file. - -> ### Note -What is the method `escapeHtml()`? It's actually a \[view helper\](zend.view.helpers), and it's -designed to help mitigate *XSS* attacks. Never trust user input; if you are at all uncertain about -the source of a given variable in your view script, escape it using one of the \[provided escape -view helper\](zend.view.helpers) depending on the type of data you have. - -## View scripts for module names with subnamespaces - -As per PSR-0, module should be named following this rule: `\\(\)*` Default -controller class to template mapping does not work very well with those: it will remove -subnamespace. - -To address that issue new mapping was introduced since 2.3.0. To maintain backwards compatibility -that mapping is not enabled by default. To enable it, you need to add your module namespace to -whitelist in your module config: - -```php -'view_manager' => array( - // Controller namespace to template map - // or whitelisting for controller FQCN to template mapping - 'controller_map' => array( - '' => true, - ), -), -``` - -Now, create the directory `view///hello`. Inside that directory, create a file named -`world.phtml`. Inside that, paste in the following: - -```php -

Greetings!

- -

You said "escapeHtml($message) ?>".

-``` - -## Create a Route - -Now that we have a controller and a view script, we need to create a route to it. - -> ### Note -`ZendSkeletonApplication` ships with a "default route" that will likely get you to this action. That -route basically expects "/{module}/{controller}/{action}", which allows you to specify this: -"/zend-user/hello/world". We're going to create a route here mainly for illustration purposes, as -creating explicit routes is a recommended practice. The application will look for a -`Zend\Mvc\Router\RouteStack` instance to setup routing. The default generated router is a -`Zend\Mvc\Router\Http\TreeRouteStack`. -To use the "default route" functionality, you will need to have the following route definition in -your module. Replace <module-name> with the name of your module. -```php -// module.config.php -return array( - '' => array( - 'type' => 'Literal', - 'options' => array( - 'route' => '/', - 'defaults' => array( - 'controller' => '\Controller\Index', - 'action' => 'index', - ), - ), - 'may_terminate' => true, - 'child_routes' => array( - 'default' => array( - 'type' => 'Segment', - 'options' => array( - 'route' => '/[:controller[/:action]]', - 'constraints' => array( - 'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', - 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', - ), - 'defaults' => array( - ), - ), - ), - ), - ), - // ... other configuration ... -); -``` - -Additionally, we need to tell the application we have a controller: - -```php -// module.config.php -return array( - 'controllers' => array( - 'invokables' => array( - '\Controller\Index' => '\Controller\IndexController', - // Do similar for each other controller in your module - ), - ), - // ... other configuration ... -); -``` - -> ### Note -We inform the application about controllers we expect to have in the application. This is to prevent -somebody requesting any service the `ServiceManager` knows about in an attempt to break the -application. The dispatcher uses a special, scoped container that will only pull controllers that -are specifically registered with it, either as invokable classes or via factories. - -Open your `config/module.config.php` file, and modify it to add to the "routes" and "controller" -parameters so it reads as follows: - -```php -return array( - 'router' => array( - 'routes' => array( - '-hello-world' => array( - 'type' => 'Literal', - 'options' => array( - 'route' => '/hello/world', - 'defaults' => array( - 'controller' => '\Controller\Hello', - 'action' => 'world', - ), - ), - ), - ), - ), - 'controllers' => array( - 'invokables' => array( - '\Controller\Hello' => '\Controller\HelloController', - ), - ), - // ... other configuration ... -); -``` - -## Tell the Application About our Module - -One problem: we haven't told our application about our new module! - -By default, modules are not parsed unless we tell the module manager about them. As such, we need to -notify the application about them. - -Remember the `config/application.config.php` file? Let's modify it to add our new module. Once done, -it should read as follows: - -```php - array( - 'Application', - '', - ), - 'module_listener_options' => array( - 'module_paths' => array( - './module', - './vendor', - ), - ), -); -``` - -Replace `` with the namespace of your module. - -## Test it Out! - -Now we can test things out! Create a new vhost pointing its document root to the `public` directory -of your application, and fire it up in a browser. You should see the default homepage template of -ZendSkeletonApplication. - -Now alter the location in your URL to append the path "/hello/world", and load the page. You should -now get the following content: - -```html -

Greetings!

- -

You said "foo".

-``` - -Now alter the location to append "?message=bar" and load the page. You should now get: - -```html -

Greetings!

- -

You said "bar".

-``` - -Congratulations! You've created your first ZF2 MVC module! diff --git a/doc/book/zend.mvc.routing.md b/doc/book/zend.mvc.routing.md deleted file mode 100644 index 59f4bbb9d..000000000 --- a/doc/book/zend.mvc.routing.md +++ /dev/null @@ -1,703 +0,0 @@ -# Routing - -Routing is the act of matching a request to a given controller. - -Typically, routing will examine the request URI, and attempt to match the URI path segment against -provided constraints. If the constraints match, a set of "matches" are returned, one of which should -be the controller name to execute. Routing can utilize other portions of the request URI or -environment as well -- for example, the host or scheme, query parameters, headers, request method, -and more. - -Routing has been written from the ground up for Zend Framework 2.0. Execution is quite similar, but -the internal workings are more consistent, performant, and often simpler. - -> ### Note -If you are a developer with knowledge of the routing system in Zend Framework 1.x, you should know -that some of the old terminology does not apply in Zend Framework 2.x. In the new routing system we -don't have a router as such, as every route can match and assemble URIs by themselves, which makes -them routers, too. -That said, in most cases the developer does not need to worry about this, because Zend Framework 2.x -will take care of this "under the hood". The work of the router will be done by -`Zend\Mvc\Router\SimpleRouteStack` or `Zend\Mvc\Router\Http\TreeRouteStack`. - -The base unit of routing is a `Route`: - -```php -namespace Zend\Mvc\Router; - -use Zend\Stdlib\RequestInterface as Request; - -interface RouteInterface -{ - public static function factory(array $options = array()); - public function match(Request $request); - public function assemble(array $params = array(), array $options = array()); -} -``` - -A `Route` accepts a `Request`, and determines if it matches. If so, it returns a `RouteMatch` -object: - -```php -namespace Zend\Mvc\Router; - -class RouteMatch -{ - public function __construct(array $params); - public function setMatchedRouteName($name); - public function getMatchedRouteName(); - public function setParam($name, $value); - public function getParams(); - public function getParam($name, $default = null); -} -``` - -Typically, when a `Route` matches, it will define one or more parameters. These are passed into the -`RouteMatch`, and objects may query the `RouteMatch` for their values. - -```php -$id = $routeMatch->getParam('id', false); -if (!$id) { - throw new Exception('Required identifier is missing!'); -} -$entity = $resource->get($id); -``` - -Usually you will have multiple routes you wish to test against. In order to facilitate this, you -will use a route aggregate, usually implementing `RouteStack`: - -```php -namespace Zend\Mvc\Router; - -interface RouteStackInterface extends RouteInterface -{ - public function addRoute($name, $route, $priority = null); - public function addRoutes(array $routes); - public function removeRoute($name); - public function setRoutes(array $routes); -} -``` - -Typically, routes should be queried in a LIFO order, and hence the reason behind the name -`RouteStack`. Zend Framework provides two implementations of this interface, `SimpleRouteStack` and -`TreeRouteStack`. In each, you register routes either one at a time using `addRoute()`, or in bulk -using `addRoutes()`. - -```php -// One at a time: -$route = Literal::factory(array( - 'route' => '/foo', - 'defaults' => array( - 'controller' => 'foo-index', - 'action' => 'index', - ), -)); -$router->addRoute('foo', $route); - -// In bulk: -$router->addRoutes(array( - // using already instantiated routes: - 'foo' => $route, - - // providing configuration to allow lazy-loading routes: - 'bar' => array( - 'type' => 'literal', - 'options' => array( - 'route' => '/bar', - 'defaults' => array( - 'controller' => 'bar-index', - 'action' => 'index', - ), - ), - ), -)); -``` - -## Router Types - -Two routers are provided, the `SimpleRouteStack` and `TreeRouteStack`. Each works with the above -interface, but utilize slightly different options and execution paths. By default, the `Zend\Mvc` -uses the `TreeRouteStack` as the router. - -### SimpleRouteStack - -This router simply takes individual routes that provide their full matching logic in one go, and -loops through them in LIFO order until a match is found. As such, routes that will match most often -should be registered last, and least common routes first. Additionally, you will need to ensure that -routes that potentially overlap are registered such that the most specific match will match first -(i.e., register later). Alternatively, you can set priorities by giving the priority as third -parameter to the `addRoute()` method, specifying the priority in the route specifications or setting -the priority property within a route instance before adding it to the route stack. - -### TreeRouteStack - -`Zend\Mvc\Router\Http\TreeRouteStack` provides the ability to register trees of routes, and will use -a B-tree algorithm to match routes. As such, you register a single route with many children. - -A `TreeRouteStack` will consist of the following configuration: - -* A base "route", which describes the base match needed, the root of the tree. -* An optional "route\_plugins", which is a configured `Zend\Mvc\Router\RoutePluginManager` that can -lazy-load routes. -* The option "may\_terminate", which hints to the router that no other segments will follow it. -* An optional "child\_routes" array, which contains additional routes that stem from the base -"route" (i.e., build from it). Each child route can itself be a `TreeRouteStack` if desired; in -fact, the `Part` route works exactly this way. - -When a route matches against a `TreeRouteStack`, the matched parameters from each segment of the -tree will be returned. - -A `TreeRouteStack` can be your sole route for your application, or describe particular path segments -of the application. - -An example of a `TreeRouteStack` is provided in the documentation of the `Part` route. - -## HTTP Route Types - -Zend Framework 2.0 ships with the following HTTP route types. - -### Zend\\Mvc\\Router\\Http\\Hostname - -The `Hostname` route attempts to match the hostname registered in the request against specific -criteria. Typically, this will be in one of the following forms: - -* "subdomain.domain.tld" -* ":subdomain.domain.tld" - -In the above, the second route would return a "subdomain" key as part of the route match. - -For any given hostname segment, you may also provide a constraint. As an example, if the "subdomain" -segment needed to match only if it started with "fw" and contained exactly 2 digits following, the -following route would be needed: - -```php -$route = Hostname::factory(array( - 'route' => ':subdomain.domain.tld', - 'constraints' => array( - 'subdomain' => 'fw\d{2}', - ), -)); -``` - -In the above example, only a "subdomain" key will be returned in the `RouteMatch`. If you wanted to -also provide other information based on matching, or a default value to return for the subdomain, -you need to also provide defaults. - -```php -$route = Hostname::factory(array( - 'route' => ':subdomain.domain.tld', - 'constraints' => array( - 'subdomain' => 'fw\d{2}', - ), - 'defaults' => array( - 'type' => 'json', - ), -)); -``` - -When matched, the above will return two keys in the `RouteMatch`, "subdomain" and "type". - -### Zend\\Mvc\\Router\\Http\\Literal - -The `Literal` route is for doing exact matching of the URI path. Configuration therefore is solely -the path you want to match, and the "defaults", or parameters you want returned on a match. - -```php -$route = Literal::factory(array( - 'route' => '/foo', - 'defaults' => array( - 'controller' => 'Application\Controller\IndexController', - 'action' => 'foo', - ), -)); -``` - -The above route would match a path "/foo", and return the key "action" in the `RouteMatch`, with the -value "foo". - -### Zend\\Mvc\\Router\\Http\\Method - -The `Method` route is used to match the http method or 'verb' specified in the request (See RFC 2616 -Sec. 5.1.1). It can optionally be configured to match against multiple methods by providing a -comma-separated list of method tokens. - -```php -$route = Method::factory(array( - 'verb' => 'post,put', - 'defaults' => array( - 'controller' => 'Application\Controller\IndexController', - 'action' => 'form-submit', - ), -)); -``` - -The above route would match an http "POST" or "PUT" request and return a `RouteMatch` object -containing a key "action" with a value of "form-submit". - -### Zend\\Mvc\\Router\\Http\\Part - -A `Part` route allows crafting a tree of possible routes based on segments of the URI path. It -actually extends the `TreeRouteStack`. - -`Part` routes are difficult to describe, so we'll simply provide a sample one here. - -```php -$route = Part::factory(array( - 'route' => array( - 'type' => 'literal', - 'options' => array( - 'route' => '/', - 'defaults' => array( - 'controller' => 'Application\Controller\IndexController', - 'action' => 'index', - ), - ), - ), - 'route_plugins' => $routePlugins, - 'may_terminate' => true, - 'child_routes' => array( - 'blog' => array( - 'type' => 'literal', - 'options' => array( - 'route' => '/blog', - 'defaults' => array( - 'controller' => 'Application\Controller\BlogController', - 'action' => 'index', - ), - ), - 'may_terminate' => true, - 'child_routes' => array( - 'rss' => array( - 'type' => 'literal', - 'options' => array( - 'route' => '/rss', - 'defaults' => array( - 'action' => 'rss', - ) - ), - 'may_terminate' => true, - 'child_routes' => array( - 'subrss' => array( - 'type' => 'literal', - 'options' => array( - 'route' => '/sub', - 'defaults' => array( - 'action' => 'subrss', - ), - ), - ), - ), - ), - ), - ), - 'forum' => array( - 'type' => 'literal', - 'options' => array( - 'route' => 'forum', - 'defaults' => array( - 'controller' => 'Application\Controller\ForumController', - 'action' => 'index', - ), - ), - ), - ), -)); -``` - -The above would match the following: - -* "/" would load the "Index" controller, "index" action. -* "/blog" would load the "Blog" controller, "index" action. -* "/blog/rss" would load the "Blog" controller, "rss" action. -* "/blog/rss/sub" would load the "Blog" controller, "subrss" action. -* "/forum" would load the "Forum" controller, "index" action. - -You may use any route type as a child route of a `Part` route. - -> ### Note -`Part` routes are not meant to be used directly. When you add definitions for `child_routes` to any -route type, that route will become a `Part` route. As already said, describing `Part` routes with -words is difficult, so hopefully the additional \[examples at the -end\](zend.mvc.routing.http-route-types.examples) will provide further insight. - -> ### Note -In the above example, the `$routePlugins` is an instance of `Zend\Mvc\Router\RoutePluginManager`. -```php -$routePlugins = new Zend\Mvc\Router\RoutePluginManager(); -$plugins = array( - 'hostname' => 'Zend\Mvc\Router\Http\Hostname', - 'literal' => 'Zend\Mvc\Router\Http\Literal', - 'part' => 'Zend\Mvc\Router\Http\Part', - 'regex' => 'Zend\Mvc\Router\Http\Regex', - 'scheme' => 'Zend\Mvc\Router\Http\Scheme', - 'segment' => 'Zend\Mvc\Router\Http\Segment', - 'wildcard' => 'Zend\Mvc\Router\Http\Wildcard', - 'query' => 'Zend\Mvc\Router\Http\Query', - 'method' => 'Zend\Mvc\Router\Http\Method', -); -foreach ($plugins as $name => $class) { - $routePlugins->setInvokableClass($name, $class); -} -``` -When using `Zend\Mvc\Router\Http\TreeRouteStack`, the `RoutePluginManager` is set up by default, and -the developer does not need to worry about the autoloading of standard HTTP routes. - -### Zend\\Mvc\\Router\\Http\\Regex - -A `Regex` route utilizes a regular expression to match against the URI path. Any valid regular -expression is allowed; our recommendation is to use named captures for any values you want to return -in the `RouteMatch`. - -Since regular expression routes are often complex, you must specify a "spec" or specification to use -when assembling URLs from regex routes. The spec is simply a string; replacements are identified -using "%keyname%" within the string, with the keys coming from either the captured values or named -parameters passed to the `assemble()` method. - -Just like other routes, the `Regex` route can accept "defaults", parameters to include in the -`RouteMatch` when successfully matched. - -```php -$route = Regex::factory(array( - 'regex' => '/blog/(?[a-zA-Z0-9_-]+)(\.(?(json|html|xml|rss)))?', - 'defaults' => array( - 'controller' => 'Application\Controller\BlogController', - 'action' => 'view', - 'format' => 'html', - ), - 'spec' => '/blog/%id%.%format%', -)); -``` - -The above would match "/blog/001-some-blog\_slug-here.html", and return four items in the -`RouteMatch`, an "id", the "controller", the "action", and the "format". When assembling a URL from -this route, the "id" and "format" values would be used to fill the specification. - -### Zend\\Mvc\\Router\\Http\\Scheme - -The `Scheme` route matches the URI scheme only, and must be an exact match. As such, this route, -like the `Literal` route, simply takes what you want to match and the "defaults", parameters to -return on a match. - -```php -$route = Scheme::factory(array( - 'scheme' => 'https', - 'defaults' => array( - 'https' => true, - ), -)); -``` - -The above route would match the "https" scheme, and return the key "https" in the `RouteMatch` with -a boolean `true` value. - -### Zend\\Mvc\\Router\\Http\\Segment - -A `Segment` route allows matching any segment of a URI path. Segments are denoted using a colon, -followed by alphanumeric characters; if a segment is optional, it should be surrounded by brackets. -As an example, "/:foo\[/:bar\]" would match a "/" followed by text and assign it to the key "foo"; -if any additional "/" characters are found, any text following the last one will be assigned to the -key "bar". - -The separation between literal and named segments can be anything. For example, the above could be -done as "/:foo{-}\[-:bar\] as well. The {-} after the :foo parameter indicates a set of one or more -delimiters, after which matching of the parameter itself ends. - -Each segment may have constraints associated with it. Each constraint should simply be a regular -expression expressing the conditions under which that segment should match. - -Also, as you can in other routes, you may provide defaults to use; these are particularly useful -when using optional segments. - -As a complex example: - -```php -$route = Segment::factory(array( - 'route' => '/:controller[/:action]', - 'constraints' => array( - 'controller' => '[a-zA-Z][a-zA-Z0-9_-]+', - 'action' => '[a-zA-Z][a-zA-Z0-9_-]+', - ), - 'defaults' => array( - 'controller' => 'Application\Controller\IndexController', - 'action' => 'index', - ), -)); -``` - -### Zend\\Mvc\\Router\\Http\\Query (Deprecated) - -> ### Warning -#### Potential security issue -A misuse of this route part can lead to a potential security issue. - -> ### Note -#### Deprecated -This route part is deprecated since you can now add query parameters without a query route. - -The `Query` route part allows you to specify and capture query string parameters for a given route. - -The intention of the `Query` part is that you do not instantiate it in its own right but to use it -as a child of another route part. - -An example of its usage would be - -```php -$route = Part::factory(array( - 'route' => array( - 'type' => 'literal', - 'options' => array( - 'route' => 'page', - 'defaults' => array( - ), - ), - ), - 'may_terminate' => true, - 'route_plugins' => $routePlugins, - 'child_routes' => array( - 'query' => array( - 'type' => 'Query', - 'options' => array( - 'defaults' => array( - 'foo' => 'bar', - ), - ), - ), - ), -)); -``` - -As you can see, it's pretty straight forward to specify the query part. This then allows you to -create query strings using the url view helper. - -```php -$this->url( - 'page/query', - array( - 'name' => 'my-test-page', - 'format' => 'rss', - 'limit' => 10, - ) -); -``` - -As you can see above, you must add "/query" to your route name in order to append a query string. If -you do not specify "/query" in the route name then no query string will be appended. - -Our example "page" route has only one defined parameter of "name" ("/page\[/:name\]"), meaning that -the remaining parameters of "format" and "limit" will then be appended as a query string. - -The output from our example should then be "/page/my-test-page?format=rss&limit=10" - -### Zend\\Mvc\\Router\\Http\\Wildcard (Deprecated) - -> ### Warning -#### Potential security issue -A misuse of this route type can lead to a potential security issue. - -> ### Note -#### Deprecated -This route type is deprecated. Use the `Segment` route type. - -The `Wildcard` route type matches all segments of a URI path, like in version 1 of Zend Framework. - -## HTTP Routing Examples - -Most of the routing definitions will be done in module configuration files, so the following -examples will show how to set up routes in config files. - -### Simple example with two literal routes - -```php -return array( - 'router' => array( - 'routes' => array( - // Literal route named "home" - 'home' => array( - 'type' => 'literal', - 'options' => array( - 'route' => '/', - 'defaults' => array( - 'controller' => 'Application\Controller\IndexController', - 'action' => 'index', - ), - ), - ), - // Literal route named "contact" - 'contact' => array( - 'type' => 'literal', - 'options' => array( - 'route' => 'contact', - 'defaults' => array( - 'controller' => 'Application\Controller\ContactController', - 'action' => 'form', - ), - ), - ), - ), - ), -); -``` - -### A complex example with child routes - -```php -return array( - 'router' => array( - 'routes' => array( - // Literal route named "home" - 'home' => array( - 'type' => 'literal', - 'options' => array( - 'route' => '/', - 'defaults' => array( - 'controller' => 'Application\Controller\IndexController', - 'action' => 'index', - ), - ), - ), - // Literal route named "blog", with child routes - 'blog' => array( - 'type' => 'literal', - 'options' => array( - 'route' => '/blog', - 'defaults' => array( - 'controller' => 'Application\Controller\BlogController', - 'action' => 'index', - ), - ), - 'may_terminate' => true, - 'child_routes' => array( - // Segment route for viewing one blog post - 'post' => array( - 'type' => 'segment', - 'options' => array( - 'route' => '/[:slug]', - 'constraints' => array( - 'slug' => '[a-zA-Z0-9_-]+', - ), - 'defaults' => array( - 'action' => 'view', - ), - ), - ), - // Literal route for viewing blog RSS feed - 'rss' => array( - 'type' => 'literal', - 'options' => array( - 'route' => '/rss', - 'defaults' => array( - 'action' => 'rss', - ), - ), - ), - ), - ), - ), - ), -); -``` - -When using child routes, naming of the routes follows the `parent/child` pattern, so to use the -child routes from the above example: - -```php -echo $this->url('blog'); // gives "/blog" -echo $this->url('blog/post', array('slug' => 'my-post')); // gives "/blog/my-post" -echo $this->url('blog/rss'); // gives "/blog/rss" -``` - -### An example with multiple Hostnames and subdomains within a single application - -```php -return array( - 'router' => array( - 'routes' => array( - 'modules.zendframework.com' => array( - 'type' => 'Zend\Mvc\Router\Http\Hostname', - 'options' => array( - 'route' => ':4th.[:3rd.]:2nd.:1st', // domain levels from right to left - 'contraints' => array( - '4th' => 'modules', - '3rd' => '.*?', // optional 3rd level domain such as .ci, .dev or .test - '2nd' => 'zendframework', - '1st' => 'com', - ), - // Purposely omit default controller and action - // to let the child routes control the route match - ), - // child route controllers may span multiple modules as desired - 'child_routes' => array( - 'index' => array( - 'type' => 'Zend\Mvc\Router\Http\Literal', - 'options' => array( - 'route' => '/', - 'defaults' => array( - 'controller' => 'Module\Controller\Index', - 'action' = > 'index', - ), - ), - 'may_terminate' => true, - ), - ), - ), - 'packages.zendframework.com' => array( - 'type' => 'Zend\Mvc\Router\Http\Hostname', - 'options' => array( - 'route' => ':4th.[:3rd.]:2nd.:1st', // domain levels from right to left - 'contraints' => array( - '4th' => 'packages', - '3rd' => '.*?', // optional 3rd level domain such as .ci, .dev or .test - '2nd' => 'zendframework', - '1st' => 'com', - ), - // Purposely omit default controller and action - // to let the child routes control the route match - ), - // child route controllers may span multiple modules as desired - 'child_routes' => array( - 'index' => array( - 'type' => 'Zend\Mvc\Router\Http\Literal', - 'options' => array( - 'route' => '/', - 'defaults' => array( - 'controller' => 'Package\Controller\Index', - 'action' = > 'index', - ), - ), - 'may_terminate' => true, - ), - ), - ), - ), - ), -); -``` - -The above would match the following: - -* `modules.zendframework.com` would dispatch the `Index` controller's `index` action of the `Module` -module. -* `modules.ci.zendframework.com` would dispatch the `Index` controller's `index` action of the -`Module` module. -* `packages.zendframework.com` would dispatch the `Index` controller's `index` action of the -`Package` module. -* `packages.dev.zendframework.com` would dispatch the `Index` controller's `index` action of the -`Package` module. - -The `Url` controller plugin or view helper may be used to generate URLs following the above example: - -```php -// reuse the route matched parameters to generate URLs -echo $this->url('modules.zendframework.com/index', array(), array(), true); -echo $this->url('packages.zendframework.com/index', array(), array(), true); -``` - -> ### Warning -When defining child routes pay attention that the `may_terminate` and `child_routes` definitions are -in same level as the `options` and `type` definitions. A common pitfall is to have those two -definitions nested in `options`, which will not result in the desired routes. - -## Console Route Types - -Zend Framework 2.0 also comes with routes for writing Console based applications, which is explained -in the \[Console routes and routing\](zend.console.routes) section. diff --git a/doc/book/zend.mvc.send-response-event.md b/doc/book/zend.mvc.send-response-event.md deleted file mode 100644 index ffb75200a..000000000 --- a/doc/book/zend.mvc.send-response-event.md +++ /dev/null @@ -1,33 +0,0 @@ -# The SendResponseEvent - -The MVC layer of Zend Framework 2 also incorporates and utilizes a custom `Zend\EventManager\Event` -implementation located at `Zend\Mvc\ResponseSender\SendResponseEvent`. This event allows listeners -to update the response object, by setting headers and content. - -The methods it defines are: - -* `setResponse($response)` -* `getResponse()` -* `setContentSent()` -* `contentSent()` -* `setHeadersSent()` -* `headersSent()` - -## Listeners - -Currently, three listeners are listening to this event at different priorities based on which -listener is used most. - -Class | Priority | Method Called | Description ------------------------------------------------------------- | -------- | ------------- | ----------- -`Zend\Mvc\SendResponseListener\PhpEnvironmentResponseSender` | -1000 | `__invoke` | This is used in context of HTTP (this is the most often used). -`Zend\Mvc\SendResponseListener\ConsoleResponseSender` | -2000 | `__invoke` | This is used in context of Console. -`Zend\Mvc\SendResponseListener\SimpleStreamResponseSender` | -3000 | `__invoke` | - -Because all these listeners have negative priorities, adding your own logic to modify `Response` -object is easy: just add a new listener without any priority (it will default to 1) and it will -always be executed first. - -## Triggerers - -This event is executed when MvcEvent::FINISH event is triggered, with a priority of -10000. diff --git a/doc/book/zend.mvc.services.md b/doc/book/zend.mvc.services.md deleted file mode 100644 index bf0934bf9..000000000 --- a/doc/book/zend.mvc.services.md +++ /dev/null @@ -1,645 +0,0 @@ -# Default Services - -The default and recommended way to write Zend Framework applications uses a set of services defined -in the `Zend\Mvc\Service` namespace. This chapter details what each of those services are, the -classes they represent, and the configuration options available. - -Many of the services are provided by other components, and the factories and abstract factories -themselves are defined in the individual components. We will cover those factories in this chapter, -however, as usage is generally the same between each. - -## Theory of Operation - -To allow easy configuration of all the different parts of the MVC system, a somewhat complex set of -services and their factories has been created. We'll try to give a simplified explanation of the -process. - -When a `Zend\Mvc\Application` is created, a `Zend\ServiceManager\ServiceManager` object is created -and configured via `Zend\Mvc\Service\ServiceManagerConfig`. The `ServiceManagerConfig` gets the -configuration from `application.config.php` (or some other application configuration you passed to -the `Application` when creating it). From all the service and factories provided in the -`Zend\Mvc\Service` namespace, `ServiceManagerConfig` is responsible of configuring only three: -`SharedEventManager`, `EventManager`, and `ModuleManager`. - -After this, the `Application` calls for the `ModuleManager`. At this point, the `ModuleManager` -further configures the `ServiceManager` with services and factories provided in -`Zend\Mvc\Service\ServiceListenerFactory`. This approach allows us to keep the main application -configuration concise, and to give the developer the power to configure different parts of the MVC -system from within the modules, overriding any default configuration in these MVC services. - -## ServiceManager - -As a quick review, the following service types may be configured: - -* **Invokable services**, which map a service name to a class that has no constructor or a -constructor that accepts no arguments. -* **Factories**, which map a service name to a factory which will create and return an object. A -factory receives the service manager as an argument, and may be any PHP callable, or a class or -object that implements `Zend\ServiceManager\FactoryInterface`. -* **Abstract factories**, which are factories that can create any number of named services that -share the same instantiation pattern; examples include database adapters, cache adapters, loggers, -etc. The factory receives the service manager as an argument, the resolved service name, and the -requested service name; it **must** be a class or object implementing -`Zend\ServiceManager\AbstractFactoryInterface`. See the section on abstract factories - <zend.mvc.services.abstract-factories> for configuration information. -* **Aliases**, which alias one service name to another. Aliases can also reference other aliases. -* **Initializers**, which receive the newly created instance and the service manager, and which can -be used to perform additional initialization tasks. The most common use case is to test the instance -against specific "Aware" interfaces, and, if matching, inject them with the appropriate service. -* **Plugin managers**, which are specialized service managers used to manage objects that are of a -related type, such as view helpers, controller plugins, controllers, etc. Plugin managers accept -configuration just like service managers, and as such can compose invokable services, factories, -abstract factories, aliases, and initializers. They are also `ServiceLocatorAware`, and will be -injected with the application service manager instance, giving factories and abstract factories -access to application-level services when needed. See the heading Plugin managers - <zend.mvc.services.plugin-managers> for a list of available plugin managers. - -The application service manager is referenced directly during bootstrapping, and has the following -services configured out of the box. - -* **Invokable services** - * `DispatchListener`, mapping to `Zend\Mvc\DispatchListener`. - * `RouteListener`, mapping to `Zend\Mvc\RouteListener`. - * `SendResponseListener`, mapping to `Zend\Mvc\SendResponseListener`. - * `SharedEventManager`, mapping to `Zend\EventManager\SharedEventManager`. -* **Factories** - * `Application`, mapping to `Zend\Mvc\Service\ApplicationFactory`. - * `Config`, mapping to `Zend\Mvc\Service\ConfigFactory`. Internally, this pulls the `ModuleManager` -service, and calls its `loadModules()` method, and retrieves the merged configuration from the -module event. As such, this service contains the entire, merged application configuration. - * `ControllerManager`, mapping to `Zend\Mvc\Service\ControllerLoaderFactory`. This creates an -instance of `Zend\Mvc\Controller\ControllerManager`, passing the service manager instance. -Additionally, it uses the `DiStrictAbstractServiceFactory` service -- effectively allowing -you to fall back to DI in order to retrieve your controllers. If you want to use `Zend\Di` to -retrieve your controllers, you must white-list them in your DI configuration under the -`allowed_controllers` key (otherwise, they will just be ignored). -The `ControllerManager` will add an initializer that will do the following: - * If the controller implements the `Zend\ServiceManager\ServiceLocatorAwareInterface` -interface, an instance of the `ServiceManager` will be injected into it. - * If the controller implements the `Zend\EventManager\EventManagerAwareInterface` -interface, an instance of the `EventManager` will be injected into it. - * Finally, an initializer will inject it with the `ControllerPluginManager` service, as -long as the `setPluginManager` method is implemented. - * `ControllerPluginManager`, mapping to `Zend\Mvc\Service\ControllerPluginManagerFactory`. This -instantiates the `Zend\Mvc\Controller\PluginManager` instance, passing it the service manager -instance. It also uses the `DiAbstractServiceFactory` service -- effectively allowing you to fall -back to DI in order to retrieve your controller plugins - <zend.mvc.controller-plugins>. -It registers a set of default controller plugins, and contains an initializer for injecting -plugins with the current controller. - * `ConsoleAdapter`, mapping to `Zend\Mvc\Service\ConsoleAdapterFactory`. This grabs the `Config` -service, pulls from the `console` key, and do the following: - * If the `adapter` subkey is present, it is used to get the adapter instance, otherwise, -`Zend\Console\Console::detectBestAdapter()` will be called to configure an adapter instance. - * If the `charset` subkey is present, the is used to set the adapter charset. - * `ConsoleRouter`, mapping to `Zend\Mvc\Service\RouterFactory`. This grabs the `Config` service, and -pulls from the `console` key and `router` subkey, configuring a -`Zend\Mvc\Router\Console\SimpleRouteStack` instance. - * `ConsoleViewManager`, mapping to `Zend\Mvc\Service\ConsoleViewManagerFactory`. This creates and -returns an instance of `Zend\Mvc\View\Console\ViewManager`, which in turn registers and initializes -a number of console-specific view services. - * `DependencyInjector`, mapping to `Zend\Mvc\Service\DiFactory`. This pulls the `Config` service, -and looks for a "di" key; if found, that value is used to configure a new `Zend\Di\Di` instance. - * `DiAbstractServiceFactory`, mapping to `Zend\Mvc\Service\DiAbstractServiceFactoryFactory`. This -creates an instance of `Zend\ServiceManager\Di\DiAbstractServiceFactory` injecting the `Di` service -instance. That instance is attached to the service manager as an abstract factory -- effectively -enabling DI as a fallback for providing services. - * `DiServiceInitializer`, mapping to `Zend\Mvc\Service\DiServiceInitializerFactory`. This creates an -instance of `Zend\ServiceManager\Di\DiServiceInitializer` injecting the `Di` service and the service -manager itself. - * `DiStrictAbstractServiceFactory`, mapping to -`Zend\Mvc\Service\DiStrictAbstractServiceFactoryFactory`. This creates an instance of -`Zend\Mvc\Service\DiStrictAbstractServiceFactoryFactory` injecting the `Di` service instance. - * `EventManager`, mapping to `Zend\Mvc\Service\EventManagerFactory`. This factory returns a new -instance of `Zend\EventManager\EventManager` on each request. This service is not shared by default, -allowing the ability to have an `EventManager` per service, with a shared `SharedEventManager` -injected in each. - * `FilterManager`, mapping to `Zend\Mvc\Service\FilterManagerFactory`. This instantiates the -`Zend\Filter\FilterPluginManager` instance, passing it the service manager instance -- this is used -to manage filters for the \[filter chains\](zend.filter.filter\_chains). It also uses the -`DiAbstractServiceFactory` service -- effectively allowing you to fall back to DI in order to -retrieve filters. - * `FormElementManager`, mapping to `Zend\Mvc\Service\FormElementManagerFactory`. This instantiates -the `Zend\Form\FormElementManager` instance, passing it the service manager instance -- this is used -to manage \[form elements\](zend.form.elements.intro). It also uses the `DiAbstractServiceFactory` -service -- effectively allowing you to fall back to DI in order to retrieve form elements. - * `HttpRouter`, mapping to `Zend\Mvc\Service\RouterFactory`. This grabs the `Config` service, and -pulls from the `router` key, configuring a `Zend\Mvc\Router\Http\TreeRouteStack` instance. - * `HttpViewManager`, mapping to `Zend\Mvc\Service\HttpViewManagerFactory`. This creates and returns -an instance of `Zend\Mvc\View\Http\ViewManager`, which in turn registers and initializes a number of -HTTP-specific view services. - * `HydratorManager`, mapping to `Zend\Mvc\Service\HydratorManagerFactory`. This creates and returns -an instance of `Zend\Stdlib\Hydrator\HydratorPluginManager`, which can be used to manage and persist -hydrator instances. - * `InputFilterManager`, mapping to `Zend\Mvc\Service\InputFilterManagerFactory`. This creates and -returns an instance of `Zend\InputFilter\InputFilterPluginManager`, which can be used to manage and -persist input filter instances. - * `ModuleManager`, mapping to `Zend\Mvc\Service\ModuleManagerFactory`. -This is perhaps the most complex factory in the MVC stack. It expects that an -`ApplicationConfig` service has been injected, with keys for `module_listener_options` and -`modules`; see the quick start for samples. -It instantiates an instance of `Zend\ModuleManager\Listener\DefaultListenerAggregate`, using -the "module\_listener\_options" retrieved. Checks if a service with the name `ServiceListener` -exists, otherwise it sets a factory with that name mapping to -`Zend\Mvc\Service\ServiceListenerFactory`. A bunch of service listeners will be added to the -`ServiceListener`, like listeners for the `getServiceConfig`, `getControllerConfig`, -`getControllerPluginConfig`, `getViewHelperConfig` module methods. -Next, it retrieves the `EventManager` service, and attaches the above listeners. -It instantiates a `Zend\ModuleManager\ModuleEvent` instance, setting the "ServiceManager" -parameter to the service manager object. -Finally, it instantiates a `Zend\ModuleManager\ModuleManager` instance, and injects the -`EventManager` and `ModuleEvent`. - * `MvcTranslator`, mapping to `Zend\Mvc\Service\TranslatorServiceFactory`, and returning an instance -of `Zend\Mvc\I18n\Translator`, which extends `Zend\I18n\Translator\Translator` and implements -`Zend\Validator\Translator\TranslatorInterface`, allowing the instance to be used anywhere a -translator may be required in the framework. - * `PaginatorPluginManager`, mapping to `Zend\Mvc\Service\PaginatorPluginManagerFactory`. This -instantiates the `Zend\Paginator\AdapterPluginManager` instance, passing it the service manager -instance -- this is used to manage paginator adapters - <zend.paginator.usage.paginating.adapters>. It also uses the -`DiAbstractServiceFactory` service -- effectively allowing you to fall back to DI in order to -retrieve paginator adapters. - * `Request`, mapping to `Zend\Mvc\Service\RequestFactory`. The factory is used to create and return -a request instance, according to the current environment. If the current environment is `cli`, it -will create a `Zend\Console\Request`, or a `Zend\Http\PhpEnvironment\Request` if the current -environment is HTTP. - * `Response`, mapping to `Zend\Mvc\Service\ResponseFactory`. The factory is used to create and -return a response instance, according to the current environment. If the current environment is -`cli`, it will create a `Zend\Console\Response`, or a `Zend\Http\PhpEnvironment\Response` if the -current environment is HTTP. - * `Router`, mapping to `Zend\Mvc\Service\RouterFactory`. If in a console enviroment, this will -behave the same way as the `ConsoleRouter` service, if not, it will behave the same way as -`HttpRouter` service. - * `RoutePluginManager`, mapping to `Zend\Mvc\Service\RoutePluginManagerFactory`. This instantiates -the `Zend\Mvc\Router\RoutePluginManager` instance, passing it the service manager instance -- this -is used to manage \[route types\](zend.mvc.routing.http-route-types). It also uses the -`DiAbstractServiceFactory` service -- effectively allowing you to fall back to DI in order to -retrieve route types. - * `SerializerAdapterManager`, mapping to `Zend\Mvc\Service\SerializerAdapterPluginManagerFactory`, -which returns an instance of `Zend\Serializer\AdapterPluginManager`. This is a plugin manager for -managing serializer adapter instances. - * `ServiceListener`, mapping to `Zend\Mvc\Service\ServiceListenerFactory`. The factory is used to -instantiate the `ServiceListener`, while allowing easy extending. It checks if a service with the -name `ServiceListenerInterface` exists, which must implement -`Zend\ModuleManager\Listener\ServiceListenerInterface`, before instantiating the default -`ServiceListener`. -In addition to this, it retrieves the `ApplicationConfig` and looks for the -`service_listener_options` key. This allows you to register own listeners for module methods and -configuration keys to create an own service manager; see the application configuration -options <zend.mvc.services.app-config> for samples. - * `ValidatorManager`, mapping to `Zend\Mvc\Service\ValidatorManagerFactory`. This instantiates the -`Zend\Validator\ValidatorPluginManager` instance, passing it the service manager instance -- this is -used to manage \[validators\](zend.validator.set). It also uses the `DiAbstractServiceFactory` -service -- effectively allowing you to fall back to DI in order to retrieve validators. - * `ViewFeedRenderer`, mapping to `Zend\Mvc\Service\ViewFeedRendererFactory`, which returns an -instance of `Zend\View\Renderer\FeedRenderer`, used to render feeds. - * `ViewFeedStrategy`, mapping to `Zend\Mvc\Service\ViewFeedStrategyFactory`, which returns an -instance of `Zend\View\Strategy\FeedStrategy`, used to select the `ViewFeedRenderer` given the -appropriate criteria. - * `ViewHelperManager`, mapping to `Zend\Mvc\Service\ViewHelperManagerFactory`, which returns an -instance of `Zend\View\HelperManager`. This is a plugin manager for managing view helper instances. - * `ViewJsonRenderer`, mapping to `Zend\Mvc\Service\ViewJsonRendererFactory`, which returns an -instance of `Zend\View\Renderer\JsonRenderer`, used to render JSON structures. - * `ViewJsonStrategy`, mapping to `Zend\Mvc\Service\ViewJsonStrategyFactory`, which returns an -instance of `Zend\View\Strategy\JsonStrategy`, used to select the `ViewJsonRenderer` given the -appropriate criteria. - * `ViewManager`, mapping to `Zend\Mvc\Service\ViewManagerFactory`. The factory is used to create and -return a view manager, according to the current environment. If the current environment is `cli`, it -will create a `Zend\Mvc\View\Console\ViewManager`, or a `Zend\Mvc\View\Http\ViewManager` if the -current environment is HTTP. - * `ViewResolver`, mapping to `Zend\Mvc\Service\ViewResolverFactory`, which creates and returns the -aggregate view resolver. It also attaches the `ViewTemplateMapResolver` and `ViewTemplatePathStack` -services to it. - * `ViewTemplateMapResolver`, mapping to `Zend\Mvc\Service\ViewTemplateMapResolverFactory` which -creates, configures and returns the `Zend\View\Resolver\TemplateMapResolver`. - * `ViewTemplatePathStack`, mapping to `Zend\Mvc\Service\ViewTemplatePathStackFactory` which creates, -configures and returns the `Zend\View\Resolver\TemplatePathStack`. -* **Abstract factories** - * `Zend\Cache\Service\StorageCacheAbstractServiceFactory` (opt-in; registered by default in the -skeleton application). - * `Zend\Db\Adapter\AdapterAbstractServiceFactory` (opt-in). - * `Zend\Form\FormAbstractServiceFactory` is registered by default. - * `Zend\Log\LoggerAbstractServiceFactory` (opt-in; registered by default in the skeleton -application). -* **Aliases** - * `Configuration`, mapping to the `Config` service. - * `Console`, mapping to the `ConsoleAdapter` service. - * `Di`, mapping to the `DependencyInjector` service. - * `Zend\Di\LocatorInterface`, mapping to the `DependencyInjector` service. - * `Zend\EventManager\EventManagerInterface`, mapping to the `EventManager` service. This is mainly -to ensure that when falling through to DI, classes are still injected via the `ServiceManager`. - * `Zend\Mvc\Controller\PluginManager`, mapping to the `ControllerPluginManager` service. This is -mainly to ensure that when falling through to DI, classes are still injected via the -`ServiceManager`. - * `Zend\View\Resolver\TemplateMapResolver`, mapping to the `ViewTemplateMapResolver` service. - * `Zend\View\Resolver\TemplatePathStack`, mapping to the `ViewTemplatePathStack` service. - * `Zend\View\Resolver\AggregateResolver`, mapping to the `ViewResolver` service. - * `Zend\View\Resolver\ResolverInterface`, mapping to the `ViewResolver` service. -* **Initializers** - * For objects that implement `Zend\EventManager\EventManagerAwareInterface`, the `EventManager` -service will be retrieved and injected. This service is **not** shared, though each instance it -creates is injected with a shared instance of `SharedEventManager`. - * For objects that implement `Zend\ServiceManager\ServiceLocatorAwareInterface`, the -`ServiceManager` will inject itself into the object. - * The `ServiceManager` registers itself as the `ServiceManager` service, and aliases itself to the -class names `Zend\ServiceManager\ServiceLocatorInterface` and `Zend\ServiceManager\ServiceManager`. - -## Abstract Factories - -As noted in the previous section, Zend Framework provides a number of abstract service factories by -default. Each is noted below, along with sample configuration. - -In each instance, the abstract factory looks for a top-level configuration key, consisting of -key/value pairs where the key is the service name, and the value is the configuration to use to -create the given service. - -### Zend\\Cache\\Service\\StorageCacheAbstractServiceFactory - -This abstract factory is opt-in, but registered by default in the skeleton application. It uses the -top-level configuration key "caches". - -```php -return array( - 'caches' => array( - 'Cache\Transient' => array( - 'adapter' => 'redis', - 'ttl' => 60, - 'plugins' => array( - 'exception_handler' => array( - 'throw_exceptions' => false, - ), - ), - ), - 'Cache\Persistence' => array( - 'adapter' => 'filesystem', - 'ttl' => 86400, - ), - ), -); -``` - -See the \[cache documentation\](zend.cache.storage.adapter) for more configuration options. - -### Zend\\Db\\Adapter\\AdapterAbstractServiceFactory - -This abstract factory is opt-in. It uses the top-level configuration key "db", with a subkey -"adapters". - -```php -return array( - 'db' => array('adapters' => array( - 'Db\ReadOnly' => array( - 'driver' => 'Pdo_Sqlite', - 'database' => 'data/db/users.db', - ), - 'Db\Writeable' => array( - 'driver' => 'Mysqli', - 'database' => 'users', - 'username' => 'developer', - 'password' => 'developer_password', - ), - )), -); -``` - -See the \[DB adapter documentation\](zend.db.adapter) for more configuration options. - -### Zend\\Form\\FormAbstractServiceFactory - -This abstract factory is registered by default. It uses the top-level configuration key "forms". It -makes use of the `FilterManager`, `FormElementManager`, `HydratorManager`, `InputFilterManager`, and -`ValidatorManager` plugin managers in order to allow instantiation and creation of form objects and -all related objects in the form hierarchy. - -```php -return array( - 'forms' => array( - 'Form\Foo' => array( - 'hydrator' => 'ObjectProperty', - 'type' => 'Zend\Form\Form', - 'elements' => array( - array( - 'spec' => array( - 'type' => 'Zend\Form\Element\Email', - 'name' => 'email', - 'options' => array( - 'label' => 'Your email address', - ), - ), - ), - ), - ), - ), -); -``` - -Form configuration follows the same configuration you would use with a form factory; the primary -difference is that all plugin managers have already been injected for you, allowing you the -possibility of custom objects or substitutions. - -See the \[form factory documentation\](zend.form.quick-start.factory) for more configuration -options. - -### Zend\\Log\\LoggerAbstractServiceFactory - -This abstract factory is opt-in, but registered by default in the skeleton application. It uses the -top-level configuration key "log". - -```php -return array( - 'log' => array( - 'Log\App' => array( - 'writers' => array( - array( - 'name' => 'stream', - 'priority' => 1000, - 'options' => array( - 'stream' => 'data/logs/app.log', - ), - ), - ), - ), - ), -); -``` - -See the \[log documentation\](zend.log.overview) for more configuration options. - -## Plugin Managers - -The following plugin managers are configured by default: - -* **ControllerManager**, corresponding to `Zend\Mvc\Controller\ControllerManager`, and used to -manage controller instances. -* **ControllerPluginManager**, corresponding to `Zend\Mvc\Controller\PluginManager`, and used to -manage controller plugin instances. -* **FilterManager**, corresponding to `Zend\Filter\FilterPluginManager`, and used to manage filter -instances. -* **FormElementManager**, corresponding to `Zend\Form\FormElementManager`, and used to manage -instances of form elements and fieldsets. -* **HydratorManager**, corresponding to `Zend\Stdlib\Hydrator\HydratorPluginManager`, and used to -manage hydrator instances. -* **InputFilterManager**, corresponding to `Zend\InputFilter\InputFilterPluginManager`, and used to -manage input filter instances. -* **RoutePluginManager**, corresponding to `Zend\Mvc\Router\RoutePluginManager`, and used to manage -route instances. -* **SerializerAdapterManager**, corresponding to `Zend\Serializer\AdapterPluginManager`, and used to -manage serializer instances. -* **ValidatorManager**, corresponding to `Zend\Validator\ValidatorPluginManager`, and used to manage -validator instances. -* **ViewHelperManager**, corresponding to `Zend\View\HelperPluginManager`, and used to manage view -helper instances. - -As noted in the previous section, all plugin managers share the same configuration and service types -as the standard service manager; they are simply scoped, and only allow instances of certain types -to be created or registered. Default types available are listed in the documentation for each -component. - -## ViewManager - -The View layer within `Zend\Mvc` consists of a large number of collaborators and event listeners. As -such, `Zend\Mvc\View\ViewManager` was created to handle creation of the various objects, as well as -wiring them together and establishing event listeners. - -The `ViewManager` itself is an event listener on the `bootstrap` event. It retrieves the -`ServiceManager` from the `Application` object, as well as its composed `EventManager`. - -Configuration for all members of the `ViewManager` fall under the `view_manager` configuration key, -and expect values as noted below. The following services are created and managed by the -`ViewManager`: - -* `ViewHelperManager`, representing and aliased to `Zend\View\HelperPluginManager`. It is seeded -with the `ServiceManager`. Created via the `Zend\Mvc\Service\ViewHelperManagerFactory`. - * The `Router` service is retrieved, and injected into the `Url` helper. - * If the `base_path` key is present, it is used to inject the `BasePath` view helper; otherwise, the -`Request` service is retrieved, and the value of its `getBasePath()` method is used. - * If the `base_path_console` key is present, it is used to inject the `BasePath` view helper for -console requests; otherwise, the `Request` service is retrieved, and the value of its -`getBasePath()` method is used. This can be useful for sending urls in emails via a cronjob. - * If the `doctype` key is present, it will be used to set the value of the `Doctype` view helper. -* `ViewTemplateMapResolver`, representing and aliased to `Zend\View\Resolver\TemplateMapResolver`. -If a `template_map` key is present, it will be used to seed the template map. -* `ViewTemplatePathStack`, representing and aliased to `Zend\View\Resolver\TemplatePathStack`. - * If a `template_path_stack` key is present, it will be used to seed the stack. - * If a `default_template_suffix` key is present, it will be used as the default suffix for template -scripts resolving. -* `ViewResolver`, representing and aliased to `Zend\View\Resolver\AggregateResolver` and -`Zend\View\Resolver\ResolverInterface`. It is seeded with the `ViewTemplateMapResolver` and -`ViewTemplatePathStack` services as resolvers. -* `ViewRenderer`, representing and aliased to `Zend\View\Renderer\PhpRenderer` and -`Zend\View\Renderer\RendererInterface`. It is seeded with the `ViewResolver` and `ViewHelperManager` -services. Additionally, the `ViewModel` helper gets seeded with the `ViewModel` as its root (layout) -model. -* `ViewPhpRendererStrategy`, representing and aliased to `Zend\View\Strategy\PhpRendererStrategy`. -It gets seeded with the `ViewRenderer` service. -* `View`, representing and aliased to `Zend\View\View`. It gets seeded with the `EventManager` -service, and attaches the `ViewPhpRendererStrategy` as an aggregate listener. -*`DefaultRenderingStrategy`, representing and aliased to `Zend\Mvc\View\DefaultRenderingStrategy`. -If the `layout` key is present, it is used to seed the strategy's layout template. It is seeded with -the `View` service. -* `ExceptionStrategy`, representing and aliased to `Zend\Mvc\View\ExceptionStrategy`. If the -`display_exceptions` or `exception_template` keys are present, they are used to configure the -strategy. -* `RouteNotFoundStrategy`, representing and aliased to `Zend\Mvc\View\RouteNotFoundStrategy` and -`404Strategy`. If the `display_not_found_reason` or `not_found_template` keys are present, they are -used to configure the strategy. -* `ViewModel`. In this case, no service is registered; the `ViewModel` is simply retrieved from the -`MvcEvent` and injected with the layout template name. - -The `ViewManager` also creates several other listeners, but does not expose them as services; these -include `Zend\Mvc\View\CreateViewModelListener`, `Zend\Mvc\View\InjectTemplateListener`, and -`Zend\Mvc\View\InjectViewModelListener`. These, along with `RouteNotFoundStrategy`, -`ExceptionStrategy`, and `DefaultRenderingStrategy` are attached as listeners either to the -application `EventManager` instance or the `SharedEventManager` instance. - -Finally, if you have a `strategies` key in your configuration, the `ViewManager` will loop over -these and attach them in order to the `View` service as listeners, at a priority of 100 (allowing -them to execute before the `DefaultRenderingStrategy`). - -## Application Configuration Options - -The following options may be used to provide initial configuration for the `ServiceManager`, -`ModuleManager`, and `Application` instances, allowing them to then find and aggregate the -configuration used for the `Config` service, which is intended for configuring all other objects in -the system. These configuration directives go to the `config/application.config.php` file. - -```php - array( - ), - - // These are various options for the listeners attached to the ModuleManager - 'module_listener_options' => array( - // This should be an array of paths in which modules reside. - // If a string key is provided, the listener will consider that a module - // namespace, the value of that key the specific path to that module's - // Module class. - 'module_paths' => array( - ), - - // An array of paths from which to glob configuration files after - // modules are loaded. These effectively override configuration - // provided by modules themselves. Paths may use GLOB_BRACE notation. - 'config_glob_paths' => array( - ), - - // Whether or not to enable a configuration cache. - // If enabled, the merged configuration will be cached and used in - // subsequent requests. - 'config_cache_enabled' => $booleanValue, - - // The key used to create the configuration cache file name. - 'config_cache_key' => $stringKey, - - // Whether or not to enable a module class map cache. - // If enabled, creates a module class map cache which will be used - // by in future requests, to reduce the autoloading process. - 'module_map_cache_enabled' => $booleanValue, - - // The key used to create the class map cache file name. - 'module_map_cache_key' => $stringKey, - - // The path in which to cache merged configuration. - 'cache_dir' => $stringPath, - - // Whether or not to enable modules dependency checking. - // Enabled by default, prevents usage of modules that depend on other modules - // that weren't loaded. - 'check_dependencies' => $booleanValue, - ), - - // Used to create an own service manager. May contain one or more child arrays. - 'service_listener_options' => array( - array( - 'service_manager' => $stringServiceManagerName, - 'config_key' => $stringConfigKey, - 'interface' => $stringOptionalInterface, - 'method' => $stringRequiredMethodName, - ), - ) - - // Initial configuration with which to seed the ServiceManager. - // Should be compatible with Zend\ServiceManager\Config. - 'service_manager' => array( - ), -); -``` - -For an example, see the [ZendSkeletonApplication configuration -file](https://github.com/zendframework/ZendSkeletonApplication/blob/master/config/application.config.php). - -## Default Configuration Options - -The following options are available when using the default services configured by the -`ServiceManagerConfig` and `ViewManager`. - -These configuration directives can go to the `config/autoload/{{,*.}global,{,*.}local}.php` files, -or in the `module//config/module.config.php` configuration files. The merging of these -configuration files is done by the `ModuleManager`. It first merges each module's -`module.config.php` file, and then the files in `config/autoload` (first the `*.global.php` and then -the `*.local.php` files). The order of the merge is relevant so you can override a module's -configuration with your application configuration. If you have both a -`config/autoload/my.global.config.php` and `config/autoload/my.local.config.php`, the local -configuration file overrides the global configuration. - -> ### Warning -Local configuration files are intended to keep sensitive information, such as database credentials, -and as such, it is highly recommended to keep these local configuration files out of your VCS. The -`ZendSkeletonApplication`'s `config/autoload/.gitignore` file ignores `*.local.php` files by -default. - -```php - array( - // Map of controller "name" to class - // This should be used if you do not need to inject any dependencies - // in your controller - 'invokables' => array( - ), - - // Map of controller "name" to factory for creating controller instance - // You may provide either the class name of a factory, or a PHP callback. - 'factories' => array( - ), - ), - - // The following are used to configure controller plugin loader - // Should be compatible with Zend\ServiceManager\Config. - 'controller_plugins' => array( - ), - - // The following are used to configure view helper manager - // Should be compatible with Zend\ServiceManager\Config. - 'view_helpers' => array( - ), - - // The following is used to configure a Zend\Di\Di instance. - // The array should be in a format that Zend\Di\Config can understand. - 'di' => array( - ), - - // Configuration for the Router service - // Can contain any router configuration, but typically will always define - // the routes for the application. See the router documentation for details - // on route configuration. - 'router' => array( - 'routes' => array( - ), - ), - - // ViewManager configuration - 'view_manager' => array( - // Base URL path to the application - 'base_path' => $stringBasePath, - - // Doctype with which to seed the Doctype helper - 'doctype' => $doctypeHelperConstantString, // e.g. HTML5, XHTML1 - - // TemplateMapResolver configuration - // template/path pairs - 'template_map' => array( - ), - - // TemplatePathStack configuration - // module/view script path pairs - 'template_path_stack' => array( - ), - // Default suffix to use when resolving template scripts, if none, 'phtml' is used - 'default_template_suffix' => $templateSuffix, // e.g. 'php' - - // Controller namespace to template map - // or whitelisting for controller FQCN to template mapping - 'controller_map' => array( - ), - - // Layout template name - 'layout' => $layoutTemplateName, // e.g. 'layout/layout' - - // ExceptionStrategy configuration - 'display_exceptions' => $bool, // display exceptions in template - 'exception_template' => $stringTemplateName, // e.g. 'error' - - // RouteNotFoundStrategy configuration - 'display_not_found_reason' => $bool, // display 404 reason in template - 'not_found_template' => $stringTemplateName, // e.g. '404' - - // Additional strategies to attach - // These should be class names or service names of View strategy classes - // that act as ListenerAggregates. They will be attached at priority 100, - // in the order registered. - 'strategies' => array( - 'ViewJsonStrategy', // register JSON renderer strategy - 'ViewFeedStrategy', // register Feed renderer strategy - ), - ), -); -``` - -For an example, see the [Application module configuration -file](https://github.com/zendframework/ZendSkeletonApplication/blob/master/module/Application/config/module.config.php) -in the ZendSkeletonApplication. diff --git a/doc/bookdown.json b/doc/bookdown.json deleted file mode 100644 index 7ea866c20..000000000 --- a/doc/bookdown.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "title": "zend-mvc: MVC application provider", - "target": "html/", - "content": [ - "book/zend.mvc.intro.md", - "book/zend.mvc.quick-start.md", - "book/zend.mvc.services.md", - "book/zend.mvc.routing.md", - "book/zend.mvc.mvc-event.md", - "book/zend.mvc.send-response-event.md", - "book/zend.mvc.controllers.md", - "book/zend.mvc.plugins.md", - "book/zend.mvc.examples.md", - {"Migration Guide": "book/migration.md"} - ] -} diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..02036c4dc --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,19 @@ +docs_dir: doc/book +site_dir: doc/html +pages: + - index.md + - Introduction: intro.md + - 'Quick Start': quick-start.md + - Reference: + - 'Default Services': services.md + - Routing: routing.md + - 'The MVC Event': mvc-event.md + - 'The SendResponse Event': send-response-event.md + - 'Available Controllers': controllers.md + - 'Controller Plugins': plugins.md + - Examples: examples.md + - 'Migration Guide': migration.md +site_name: zend-mvc +site_description: 'zend-mvc: MVC application provider' +repo_url: 'https://github.com/zendframework/zend-mvc' +copyright: 'Copyright (c) 2016 Zend Technologies USA Inc.'