Upgrading to 2.7

Middleware

zend-mvc now registers Zend\Mvc\MiddlewareListener as a dispatch listener at a priority higher than Zend\Mvc\DispatchListener, allowing dispatch of PSR-7 middleware. Read the middleware chapter for details on how to use this new feature.

Application

The constructor signature of Zend\Mvc\Application has changed. Previously, it was:

__construct($configuration, ServiceManager $serviceManager)

and internally, it pulled the services EventManager, Request, and Response from the provided $serviceManager during initialization.

The new constructor signature provides optional arguments for injecting the event manager, request, and response:

__construct(
    $configuration,
    ServiceManager $serviceManager,
    EventManager $events = null,
    RequestInterface $request = null,
    ResponseInterface $response = null
)

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.

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 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:

$events = $instance->getEventManager();
if ($events instanceof EventManagerInterface) {
    $events->setSharedManager($container->get('SharedEventManager'));
}

In zend-eventmanager v3, event managers are now injected with the shared manager at instantiation, and no setter exists for providing the shared manager. As such, the above logic changed to:

$events = $instance->getEventManager();
if (! $events || ! $events->getSharedManager()) {
    $instance->setEventManager($container->get('EventManager'));
}

In other words, it re-injects with a new event manager instance if the instance 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:

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.

To prepare your code, you will need to do the following within your controller:

As an example, consider the following code from a controller:

$db = $this->getServiceLcoator()->get('Db\ApplicationAdapter');

To update your controller, you will:

The controller then might look like the following:

use Zend\Db\Adapter\AdapterInterface;
use Zend\Mvc\Controller\AbstractActionController;

class YourController extends AbstractActionController
{
    private $db;

    public function __construct(AdapterInterface $db)
    {
        $this->db = $db;
    }

    public function someAction()
    {
        $results = $this->db->query(/* ... */);
        /* ... */
    }
}

A factory would look like the following:

use Interop\Container\ContainerInterface;

class YourControllerFactory
{
    public function __invoke(ContainerInterface $container)
    {
        return new YourController($container->get('Db\ApplicationAdapter'));
    }
}

You then also need to ensure the controller manager knows about the factory. It likely already does, as an invokable; you will redefine it as a factory in your module.config.php:

return [
    'controllers' => [
        'factories' => [
            YourController::class => YourControllerFactory::class,
            /* ... */
        ],
        /* ... */
    ],
    /* ... */
];

While this may seem like more steps, doing so ensures your code has no hidden dependencies, improves the testability of your code, and allows you to substitute alternatives for either the dependencies or the controller itself.

Optional dependencies

In some cases, you may have dependencies that are only required for some execution paths, such as forms, database adapters, etc. In these cases, you have two approaches you can use: