zend mvc v3 refactor: reduce components

Matthew Weier O'Phinney edited this page Apr 12, 2016 · 53 revisions


This initiative is now complete! Many thanks to all who assisted!

This page tracks development tasks for getting zend-mvc ready for a v3 release. It is primarily targeted at reducing overall component dependencies to a small core; this will allow users to adopt only the components they need for their given application, and reduce initial overhead.

Most components in zend-mvc are already optional. However, a huge number of factories exist within the component that actually initialize features from other components. These should be moved to the relevant components instead.

Component installer

If factories are moved to the components, then we need some way of notifying the application about them. Zend Framework already has a mechanism for this via modules; these can both provide configuration, as well as hook into the application lifecycle via event listeners. However, currently, these need to be manually added to the application after installation.

The proposed zend-component-installer provides a potential solution to this, by providing Composer event hooks that would, on detection of a package that provides a component or module, inject configuration into the application.

  • Complete zend-component-installer:
    • Allow injection to any of several supported locations:
      • application.config.php (vanilla ZF2)
      • modules.config.php (Apigility)
      • development.config.php (zend-development-mode)
      • config.php (Expressive)
    • Provide option to "do not inject".
    • Allow re-use of a selection for remaining packages.

Split out console functionality

Surprisingly, console integration provides the most integration points, including:

  • zend-console
  • zend-text (for displaying console usage messages)
  • zend-filter and zend-validator (used in console routes)

In examination, zend-filter and zend-validator are no longer actually used in the console routes, and that usage could be removed without affecting functionality.

Some of us consider mixing the MVC with console usage an anti-pattern, and we have recommended usage of zf-console as an alternative. However, we also do not want to break existing applications unnecessarily on upgrading to version 3. As such, the recommendation is:

  • Separate console functionality into a separate package, zend-mvc-console
    • move all console-specific factories to this package
    • move the console routes present in zend-router into this package
      • add a delegator factory for the RouterInterface service that returns the ConsoleRouter service if a console environment is detected
      • remove the requirements on zend-filter and zend-validator
    • move the AbstractConsoleController into this package
    • move the console view manager and related classes to this package
      • add a delegator factory for the ViewManager service that returns the ConsoleViewManager service if a console environment is detected
    • add a specialized InjectRoutematchParamsListener that injects ConsoleRequest params, and registers at the same priority as the one in zend-mvc
    • add a delegator factory for the Request and Response services that return console-specific instances if a console environment is detected
    • move the ConsoleResponseSender to this package
      • add a delegator factory for the Zend\Mvc\SendResponseListener to attach the `ConsoleResponseSender at the appropriate priority
  • Update zend-router
    • remove console-based routing and configuration
    • update RouterFactory to remove console awareness; have it simply proxy to the HttpRouter service
  • update zend-mvc
    • update ViewManagerFactory to only proxy to the HttpViewManager
    • update the InjectRoutematchParamsListener to remove ConsoleRequest awareness
    • update the RequestFactory and ResponseFactory to remove ConsoleRequest awareness
    • update the SendResponseListener to remove registration of the ConsoleResponseSender
    • add a suggestion for zend-mvc-console
    • merge #99

Split out (some) controller plugins

Controller plugins are one area where integration is the norm, making de-coupling difficult. The proposed option is to move each into its own repository, and suggest each via zend-mvc.

In particular, the following plugins have external dependencies outside those of the zend-mvc core:

Suggested approach:

  • Create a repository in the name format zend-mvc-plugin-<lowercase plugin name>.
  • Create a composer.json that lists zend-mvc as a dependency, as well as any other components required by the plugin, and lists itself as a zend-component-installer component.
  • Copy src and tests for the plugin to that repository. You will need to create a new namespace for the plugin, typically along the lines of Zend\Mvc\Plugin\<TitleCasePluginName>; this is necessary to ensure that you can expose the component as a module.
  • Add a Module class with a getConfig() method that returns controller_plugins configuration; this configuration should add entries for the plugin, including aliases from the previous fully qualified class name to the new class name.
  • Once complete, remove component from develop branch of zend-mvc (via a pull request). Pull request must include:
    • removal of source and tests for the component
    • removal of the plugin's entry in the controller PluginManager.
    • documentation in the migration guide indicating the removal, and which package to install to provide the functionality.

Split out MVC-specific i18n functionality

zend-mvc defines a number of things that are related to zend-i18n:

  • Translatable routes (now in zend-router)
  • MVC-specific translators
    • DummyTranslator for when ext/intl is missing
    • Translator, which decorates a zend-i18n translator, and implements the translator interfaces from zend-i18n and zend-validator

The proposal is to split this into its own zend-mvc-i18n package, with the following tasks:

  • create a separate package for the MVC integration (zend-mvc-i18n - [x] provides the DummyTranslator
    • provides the "integration" translator (the one implementing multiple translation interfaces)
    • provides the translation service factory mapped to MvcTranslator
    • add a config provider and a module class
  • update the zend-i18n package (PR #40)
    • define the translator loader plugin manager factory
    • add a config provider and a module class
      • register the i18n view helper configuration
  • zend-router
    • add optional dependency on zend-mvc-i18n
  • remove i18n functionality from zend-mvc (PR #110)


zend-json is used specifically with the AbstractRestfulController for auto-deserialization of JSON payloads. If this functionality is removed (and delegated to a dispatch listener), the dependency can be removed.

Alternately, we can keep it; however, it would be useful if it could split out the Zend\Json\Server subcomponent.

As such, tasks are:

  • split zend-json server subcomponent into separate component (zend-json-server, accomplished by @webit4me)
  • split Zend\Json\Json::fromXml() into separate component (zend-xml2json)
  • consider refactoring AbstractRestfulController to remove automated deserialization (modified to use json_decode() by default, fallback to Zend\Json\Json::decode() if present, and raise an exception if neither is available)

Proposed Dependencies

The following is a list of required and optional components for zend-mvc.

Dependencies to keep

Required components

  • zend-eventmanager (which provides the internal workflow engine)
  • zend-servicemanager and container-interop (from which all services are retrieved)
  • zend-view (used within the default workflow; however, if you return responses from your listeners and/or controllers, it can be omitted, but omission fails the default use cases as outlined by @akrabat above)
  • zend-http (not required for console-only applications, but required for the default use cases as outlined by @akrabat above)
  • zend-modulemanager (used by the Application during bootstrapping)
  • zend-stdlib (DispatchableInterface, RequestInterface, ResponseInterface, SplQueue, ArrayUtils, etc.)
  • zend-filter (used by the FilePostRedirectGet controller plugin, and the InjectTemplateListener; this latter is required for the default workflow. Inlining the logic would allow removal of zend-filter)
  • zend-json (used by AbstractRestfulController; could be removed if that class is refactored to remove automated JSON decoding)

Semi-required components

The following components are optional due to the fact that users may or may not opt into functionality that consumes them. As such, they could likely go in the suggest section of composer.json, and thus be required in development.

  • zend-psr7bridge (used by the MiddlewareListener, which is registered by default. However, if no routes map to middleware, no classes from this component are ever used)


  • Reduce dependency list to those listed above. (PR #115)
  • Remove factories and artifacts not related to the components listed above.
    • Predicated by the tasks in both the previous and next sections.

Dependencies to remove

Everything else can and should be removed. Removal includes:

  • Porting the factories and other artifacts to the respective component.
    • Potentially adding zend-servicemanager and/or zend-eventmanager as optional dependencies (if not present already).
  • Adding configuration providers to the component:
    • ConfigProvider class with general purpose configuration, typically for use with Expressive.
    • Module class for wiring with zend-mvc; this can and typically should use the ConfigProvider to ensure consistency; see https://github.com/zendframework/zend-router/pull/1 for an example.
  • Removing the dependency within the zend-mvc develop branch:
    • Remove from composer.json
    • Remove factories and other artifacts
  • Tag new minor release of component.

These include:

Interestingly, there are other components not currently in the composer.json that likely could stand the module treatment, too, due to their common appearance in applications:

  • zend-cache (already defines factories; add a config provider and Module class) (zend-cache PR #94)
  • zend-db (already defines factories; add config provider and Module class) (zend-db PR #96)
  • zend-mail (defines an SMTP protocol plugin manager) (zend-mail PR #80)
  • zend-paginator (defines AdapterPluginManager and specific adapter factories, as well as ScrollingStyle plugin manager; needs config provider and Module class) (zend-paginator PR #19)
  • zend-session (factories are already in component; just needs config provider/module) (zend-session PR #37)
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.