New Year's Eve 2019 marked a new era for Zend Framework.

That day, the community migrated the Zend Framework code base to its new homes on GitHub, marking its shift to becoming the Laminas Project.

Up until that day, the Zend Framework was single-handedly sponsored and led by Zend Technologies, and later Rogue Wave Software. With the transition, it is now led by an independent Technical Steering Committee, and will soon be governed by a charter with the Linux Foundation.

Over the years, Zend Framework has seen wide adoption across the PHP ecosystem, with an emphasis on the Enterprise market. It has formed the basis of numerous business applications and services including eCommerce platforms, content management, healthcare systems, entertainment platforms and portals, messaging services, APIs, and many others. The Laminas Project will continue to serve PHP users building these applications.


To prevent branding issues, the project has a new name, as does its subprojects. At a glance:

Original Project Original GitHub Organization Original PHP Namespace New Project Name New GitHub Organization New PHP Namespace
Zend Framework (components) zendframework Zend Laminas (components) laminas Laminas
Zend Framework (MVC) zendframework Zend Laminas (MVC) laminas Laminas
Apigility zfcampus ZF / ZF\Apigility Laminas API Tools laminas-api-tools Laminas\ApiTools
Expressive zendframework Zend\Expressive Mezzio mezzio Mezzio

There are a few outliers (e.g., both zf-development-mode and zf-composer-autoloading lived under zfcampus, and thus Apigility, previously, but are now distributed as Laminas components), but those are the general changes.

In all cases, the original repository was marked as "Archived" on GitHub, which makes the repository read-only, and prevents opening new issues or pull requests, or commenting on existing ones. Additionally, the related packages were marked "Abandoned" on Packagist, with a recommendation of using the equivalent Laminas package.

Users will still be able to install all existing Zend Framework, Apigility, and Expressive packages, now and into the foreseeable future; they just will not get any more updates. As such, we strongly urge you to migrate your code to Laminas.

How to Migrate

The Laminas Project includes a full migration guide which can be used both to migrate applications as well as libraries that consume any Zend Framework, Apigility, and/or Expressive components.

What's next?

For Zend Framework? Nothing. It's not going away, but there will be no further development on it or its subprojects.

For Laminas? There are many ideas and projects that the community has been waiting until the migration to start! Keep an eye on the Laminas Project blog for new posts detailing these!


Since its inception, Zend Technologies, and later Rogue Wave Software, has been single-handedly leading and sponsoring the Zend Framework project. Over the years, Zend Framework has seen wide adoption across the PHP ecosystem, with an emphasis on the Enterprise market. It has formed the basis of numerous business application and services including eCommerce platforms, content management, healthcare systems, entertainment platforms and portals, messaging services, APIs, and many others.

To take it to the next step of adoption and innovation, we are happy to announce that we are transitioning Zend Framework and all its subprojects to an open source project hosted at the Linux Foundation.

The Linux Foundation is host to a range of widely successful open source projects, and has led many similar transitions in the past. We believe that by moving Zend Framework to this proven governance model, it will enjoy both growth in adoption and contributors, and it will continue to focus on delivering best of breed innovative code, using the highest standards of security, transparency, and quality.

Please welcome The Laminas Project!

The transition is already underway, but that we are working to finalize the list of founding members. Contact us to learn more about membership benefits and how your company can help guide the project roadmap.


ZF2019-01: Information disclosure in zend-developer-tools

The package zendframework/zend-developer-tools provides a web-based toolbar for introspecting an application. When updating the package to support PHP 7.3, a change was made that could potentially prevent toolbar entries that are enabled by default from being disabled.

Affected versions

  • zendframework/zend-developer-tools 1.2.2

Action Taken

A test was added to the package to verify that only whitelisted entries should be aggregated when configuring the toolbar, and the code updated to comply.

The patch resolving the vulnerability is available in zend-developer-tools 1.2.3.

We highly recommend all users of the package to update immediately.


The Zend Framework team thanks the following for identifying the issues and working with us to help protect its users:


ZF2018-01: URL Rewrite vulnerability

zend-diactoros (and, by extension, Expressive), zend-http (and, by extension, Zend Framework MVC projects), and zend-feed (specifically, its PubSubHubbub sub-component) each contain a potential URL rewrite exploit. In each case, marshaling a request URI includes logic that introspects HTTP request headers that are specific to a given server-side URL rewrite mechanism.

When these headers are present on systems not running the specific URL rewriting mechanism, the logic would still trigger, allowing a malicious client or proxy to emulate the headers to request arbitrary content.

Action Taken

In each of the affected components, we have removed support for the specific request headers. Users can provide support within their applications to re-instate the logic if they are using the specific URL rewrite mechanism; users are encouraged to filter these headers in their web server prior to any rewrites to ensure their validity.

The patch resolving the vulnerability is available in:

  • zend-diactoros, 1.8.4
  • zend-http, 2.8.1
  • zend-feed, 2.10.3

Zend Framework MVC, Apigility, and Expressive users will receive relevant updated components via composer update.

We highly recommend all users of affected projects update immediately.


The Zend Framework team thanks the following for identifying the issues and working with us to help protect its users:


Last year, we wrote about using Laravel Homestead with ZF projects. Today, we contributed some changes to Homestead to simplify setting it up to serve Apigility, Expressive, and Zend Framework projects.

As of version 7.6.0, Homestead users can now define sites with any of the following "type" values:

  • apigility
  • expressive
  • zf

When one of these values is used, Homestead will setup the nginx instance used by Homestead to properly to work with the project.

Getting started

Much of what we detailed last year is still true:

  • You will need to add the laravel/homestead box to Vagrant: vagrant box add laravel/homestead.

  • You will likely want to use the vagrant-hostsupdater plugin to Vagrant to facilitate mapping the VM IP address and server name to your system hosts file: vagrant plugin install vagrant-hostsupdater.

  • You will need to temporarily add the laravel/homestead package as a development dependency to your application: composer require --dev laravel/homestead.

  • You will need to use the Homestead tooling to create a Vagrantfile and Homestead.yaml configuration file: ./vendor/bin/homestead make.

Configuring Homestead

Once you have your Homestead.yaml file created, you can edit it. The two things we need specifically are:

  • A folder mapping the application root directory to a directory in the vagrant image.
  • A site definition.

Generally, the folder mapping is already present, and will look something like the following:

  - map: /home/username/dev/application
    to: /home/vagrant/code

If you want the Homestead.yaml to be portable, however, you can tell it to map the current directory, and not a fully qualified path:

  - map: .
    to: /home/vagrant/code

Next, we'll look at the site definition. After you first run homestead make, you should have the following:

  - map: homestead.test
    to: /home/vagrant/code/public

Let's change this a bit. First, we'll give a new site name, then a site type (I'll use "expressive" here, but you can change this to "apigility" or "zf" based on your application), and we'll enable Z-Ray.

  - map: expressive.test
    to: /home/vagrant/code/public
    type: expressive
    zray: "true"

Yes, the correct value for the zray setting is "true"; see this issue for details.

From here, we can finally get running:

$ vagrant up

If you are not using the vagrant-hostsupdater plugin, you'll need to add an entry to your system hosts file: expressive.test


We're hoping having this support in place will allow Zend Framework zend-mvc, Apigility, and Expressive developers to create and share development environments easily between their teams. If you have additional features you would like enabled by default (e.g., auto-detection of ZF, Apigility, and Expressive applications by homestead make, additional default nginx configuration, etc.), be sure to swing by the Slack or forums and ask!

I want to extend a hearty thank you to Joe Ferguson for helping me provide the integration, and guiding me through the contribution process for Homestead.


With Expressive 3 complete, we were able to turn our sights on another important initiative: PHP 7.2 support across all components and Apigilty modules.

The short story is: as of today, that initiative is complete! If you are using the Zend Framework MVC framework, Expressive, or Apigility, or any of the ZF components standalone, you should be able to perform a composer update to get versions that support PHP 7.2.

The full story is much longer.

How we got there

The PHP project does a pretty stellar job of preserving backwards compatibility. Some might say they do it to a fault, being averse to any changes that might cause breakage for users, even if the change fixes bad behavior on the part of the language.

Interestingly, there have been a ton of initiatives to tighten up the language and have it behave more predictably. Unfortunately, we, and a number of projects on which we depend, were bit by some of these efforts that went into PHP 7.1 and 7.2.

One in particular was problematic.

Let's say you have a class such as the following:

class SomeContainer
    public function get($name, array $options = null)

Next, we'll have an extension to that class that overrides that method and changes the default value:

class AContainerExtension extends SomeContainer
    public function get($name, array $options = [])

These should be fine, right? Wrong.

Starting in 7.1.0, the above emits an E_WARNING due to incompatible signatures. This is because PHP 7.1 adds nullable types, and considers the first signature equivalent to a nullable array.

The problem is that PHPUnit, on seeing an E_WARNING, creates an error status for the test in which it is raised.

There were a number of other minor changes such as deprecated APIs that also affected our code, often leading to unexpected test errors. Technically, the code likely could run, but not without emitting deprecation notices and/or warnings — and our goal is to run cleanly, so that users can see only the warnings pertinent to their own application code.

On top of this, a number of PHPUnit classes exhibited similar behaviors, which meant that under PHP 7.2, with the versions of PHP we were using, we could not verify that our code could work under that version.

The upshot for us is that testing against 7.2 wasn't as easy as just adding another PHP version to the test matrix. We also had to find a set of different PHP versions that we could test against (for instance, PHPUnit 6 and 7 require PHP 7 versions, but we also still support PHP 5.6 in many of our components and modules), figure out how to get Travis-CI to install a PHPUnit version appropriate to the PHP version we were testing in, and ensure that the PHPUnit features we were using worked across all PHPUnit versions against which we might test.

Thankfully, we had a secret weapon: Michał Bundyra (@MichalBundyra on twitter). Michał spent a fair bit of time this past year developing increasingly effective approaches to this sort of problem, and, once the 7.2 initiative was announced, jumped in and created patches for almost every single component and module we ship.

Seriously, this was work above and beyond anything I can reasonably expect of a volunteer. Go thank him, already!

Our approach

The approach Michał developed involves two things. First, a range of known-good PHPUnit dependencies, and, second, a set of configuration for Travis-CI that will allow installing the appropriate dependencies.

First, we use the following constraints for PHPUnit in our composer.json files when both PHP 5.6 and PHP 7 versions are required:

"phpunit/phpunit": "^5.7.21 || ^6.3 || ^7.1",

These allow us to use the 5.7 series for PHP 5.6, and either the 6.3 or 7.1 series when under other PHP versions.

We also commit our composer.lock file. I'll show why in a moment.

Next, we use configuration similar to the following with Travis-CI:

sudo: false

language: php

    - $HOME/.composer/cache

    - COMPOSER_ARGS="--no-interaction"

    - php: 5.6
        - DEPS=lowest
    - php: 5.6
        - DEPS=locked
        - LEGACY_DEPS="phpunit/phpunit zendframework/zend-code"
    - php: 5.6
        - DEPS=latest
    - php: 7
        - DEPS=lowest
    - php: 7
        - DEPS=locked
        - CS_CHECK=true
        - LEGACY_DEPS="phpunit/phpunit-mock-objects phpspec/prophecy zendframework/zend-code"
    - php: 7
        - DEPS=latest
    - php: 7.1
        - DEPS=lowest
    - php: 7.1
        - DEPS=locked
        - TEST_COVERAGE=true
    - php: 7.1
        - DEPS=latest
    - php: 7.2
        - DEPS=lowest
    - php: 7.2
        - DEPS=locked
    - php: 7.2
        - DEPS=latest

  - if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi

  - travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs
  - if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi
  - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi
  - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi
  - stty cols 120 && composer show

  - vendor/bin/phpunit
  - if [[ $CS_CHECK == 'true' ]]; then vendor/bin/phpcs ; fi

Let's break that down.

We set up a few things up front for all builds: we're using dockerized php jobs (sudo: false, language: php), we're caching composer metadata between builds (which greatly speeds up the installation process!), and defining our default composer arguments.

From there, we define our test matrix. Each job in the matrix includes:

  • The PHP version we are testing against.
  • Environment variables for that specific build.

You'll notice we have three jobs for each PHP version, corresponding to the following environment variables:

  • DEPS=lowest
  • DEPS=locked
  • DEPS=latest

These variables are indicating how we want to install dependencies:

  • locked indicates we want to use those specified in the composer.lock file.
  • lowest means we want to test against the lowest stable dependencies we allow.
  • latest indicates we want to test against the latest available dependences we allow.

This approach allows us to determine:

  • When we start using features from a library that are not present in the earliest version we have indicated we support. If the lowest tests fail, we likely either need to change what part of a third-party API we are consuming, or bump the minimum supported version of that dependency.

  • When a library has introduced a BC break in a more recent release than we tested against previously. In such cases, we can try and find a way to make our own usage of that library forwards-compatible with the new version; create an issue notifying the developer(s) of that library of the BC break; or change our dependencies to not allow the newer version.

Additionally, some jobs have more variables they define:

  • CS_CHECK will tell the job whether or not to run CS checks. (We also often define an env variable for running coverage reports.)

  • LEGACY_DEPS allows us to specify dependencies we need to update after initial installation. More on that in the coming paragraphs.

We also disable xdebug unless we're running coverage reports. This speeds up Composer operations as well as running unit tests. I have the before_install script detailed above, but do not define any environments with the TEST_COVERAGE variable set, nor demonstrate how we use it to run reports.

When we hit the install section is when the "magic" happens. The first thing we do is an install from the lockfile. When we do so, we pass the --ignore-platform-reqs option, as we cannot guarantee that the dependencies in the lockfile will work for the current PHP version being used.

We then check to see if LEGACY_DEPS is non-empty. If so, we do a composer update --with-dependencies, passing the value of LEGACY_DEPS as the packages to update. This allows us to use the lockfile on locked versions, but then get platform-specific dependencies for libraries where we know that what's in the lockfile may not work on all platforms.

Next, we check for DEPS=latest, running composer update, and DEPS=lowest, running composer update --prefer-lowest --prefer-stable.

Finally, we display what dependencies were installed, along with their versions. We use the construct stty cols 120 to set the display columns, as otherwise composer will not detect a TTY, and spit out only the dependencies, with no associated version.

The beauty of this approach is that we are able to use it almost verbatim across our repositories, with only minor changes to which LEGACY_DEPS we need, and which versions need them. Having multiple tests per version, spanning a range of dependencies, has allowed us to identify and solve problems arising from libraries we consume quickly.

This approach allowed us to run tests under PHP 7.2, fix any issues identified, and finally release new versions that fully support PHP 7.2.

What's next?

We have a number of initiatives we're working on in the coming months:

  • Frank Brückner is working on a site refresh to both make the documentation and main sites more consistent in look-and-feel, as well as better support mobile browsers.

  • We continue to work on the Apigility on Expressive initiative. While many features were released with stable versions for Expressive 3, there's still work to be done, including tooling support.

  • Aleksei Khudiakov has been working on a set of proposals for a PSR-7-based zend-mvc v4.

  • We want to work on tutorials and guides to help users make the most of Expressive, as well as migrate to Expressive from zend-mvc.

If you want to help out with any of these initiatives:


When we were finalizing features for Expressive 3, we had a number of users testing using asynchronous PHP web servers. As a result, we made a number of changes in the last few iterations to ensure that Expressive will work well under these paradigms.

Specifically, we made changes to how response prototypes are injected into services.

Response prototypes?

What's the problem?

In an async system, one advantage is that you can bootstrap the application once, and then respond to requests until the server is shutdown.

However, this can become problematic with services that compose a response prototype in order to produce a response (e.g., authentication middleware that may need to produce an "unauthenticated" response; middleware that will produce a "not found" response; middleware that will produce a "method not allowed" response; etc.). We have standardized on providing response prototypes via dependency injection, using a service named after the interface they implement: Psr\Http\Message\ResponseInterface.

If a particular service accepts a response instance that's injected during initial service creation, that same instance will be used for any subsequent requests that require it. And that's where the issue comes in.

When running PHP under traditional conditions — php-fpm, the Apache SAPI, etc. — all requests are isolated; the environment is both created and torn down for each and every request. As such, passing an instance is perfectly safe; there's very little chance, if any, that any other service will be working with the same instance.

With an async server, however, the same instance will be used on each and every request. Generally, manipulations of PSR-7 message instances will create new instances, as the interfaces they implement are specified as immutable. Unfortunately, due to technical limitations of the PHP language, we were unable to make the body of response messages immutable. This means that if one process writes to that body, then a subsequent process — or even those executing in parallel! — will receive the same changes. This can lead to, in the best case scenario, duplicated content, and, in the worst, provide incorrect content or perform information leaking!

To combat these situations, we modified the Psr\Http\Message\ResponseInterface service we register with the dependency injection container: it now returns not an instance of the interface, but a factory capable of producing an instance. Services should compose this factory, and then call on it each time they need to produce a response. This fixes the async problem, as it ensures a new instance is used each time, instead of the same instance.

(Additionally, this change helps us prepare for the upcoming PSR-17, which describes factories for PSR-7 artifacts; this solution will be compatible with that specification once complete.)

Why async?

If asynchronous systems operate so differently, why bother?

There's many reasons, but the one that generally gets the attention of developers is performance.

We performed benchmarks of Expressive 2 and Expressive 3 under both Apache and nginx, and found version 3 received around a 10% improvement.

We then tested using Swoole. Swoole is a PHP extension that provides built-in async, multi-threaded input/output (I/O) modules; it's essentially the I/O aspects of node.js — which allow you to create network servers and perform database and filesystem operations — but for PHP.

A contributor, Westin Shafer, has written a module for Expressive 3 that provides an application wrapper for Swoole that is exposed via a CLI command. We ran our same benchmarks against this, and the results were astonishing: applications ran consistently 4 times faster under this asynchronous framework, and used fewer resources!

While performance is a great reason to explore async, there are other reasons as well. For instance, if you do not need the return value of an I/O call (e.g., a database transaction or cache operation), you can fire it off asynchronously, and finish out the response without waiting for it. This can lead to reduced waiting times for clients, further improving your performance.

We have had fun testing Swoole, and think it has tremendous possibilities when it comes to creating microservices in PHP. The combination of Expressive and Swoole is remarkably simple to setup and run, making it a killer combination!

Notes on setting up Swoole

The wshafer/swoole-expressive package requires a version 2 release of the Swoole extension.

However, there's a slight bug in the PECL installer whereby it picks up the most recent release as the "latest", even if a version with greater stability exists. As of the time of writing, version 1.10.2 of Swoole was released after version 2.1.1, causing it to be installed instead of the more 2.X version.

You can force installation of a version by appending the version you want when invoking the pecl command:

$ pecl install swoole-2.1.1

The version must be fully qualified for it to install correctly; no partials (such as swoole-2 or swoole-2.1 will work.


Yesterday, we tagged and released Expressive 3!

Expressive 3 provides a middleware microframework.

Create a new Expressive application using Composer:

$ composer create-project zendframework/zend-expressive-skeleton

The installer will prompt you for your choice of:

  • Initial application architecture (minimal, flat, modular)
  • Which dependency injection container you would like to use.
  • Which routing library you would like to use.
  • Which templating library you would like to use, if any.
  • Which error handling library you would like to use, if any.

From there, it creates a new project for you, and allows you to get started developing immediately.

You can read more in our quick start, and may want to check out our command line tooling to see what we provide to make development even faster for you!

What are the features?

Expressive 3 embraces modern PHP, and requires PHP 7.1 or higher. Strong type-hinting, including return type hints, make both our job and your job easier and more predictable. The ability to use all modern PHP features helps us deliver a solid base for your application.

Expressive 3 provides full support for the PSR-15 (Middleware and Request Handlers) standard. We believe strongly in supporting standards, to the extent that this release also drops direct support for the "double-pass" middleware style we have supported since version 1.0

Expressive 3 massively refactors its internals as well. In fact, the majority of the code in the zend-expressive package was removed, moved to other existing packages where it had a better semantic affiliation1, or extracted to new packages2. This base package now mainly handles coordinating collaborators and providing a user-friendly interface to creating your application pipeline and routes.3

Expressive 3 provides more command line tooling and tooling improvements in order to make developing your application easier. We added a command for creating factories for existing classes (factory:create).4 The middleware:create command now creates a factory for the middleware generated. We added support for creating request handlers5, complete with factory generation and registration, as well as template support.6

Finally, we recognize that Expressive has changed massively between versions 1 and 3, while simultaneously keeping its primary API stable and unchanged. However, to help users find the information they need for the version they run, we have rolled out versioned documentation, with each version providing only information specific to its release cycle:

The most recent version will always be present in the primary navigation, with links to other versions present as well.

New components!

We have several new components that provide features for Expressive — or any PSR-15 framework you may be using! These include:

We have a number of other packages in the works around authentication, authorization, and data validation that we will be releasing in the coming weeks and months; stay tuned for announcements!

What about upgrading?

We have prepared a migration document that covers new features, removed features, and a list of all changes.

Additionally, we have provided [migration tooling]( to aid you in your migration from version 2 to version 3. The tool will not necessarily give you a fully running application, but it will take care of the majority of the changes necessary to bump your application to version 3, including setting up appropriate dependencies, and updating your bootstrapping files to conform to the new skeleton application structure.

If you need assistance, you can find community help:

What's next?

We have been working on a number of API-related modules for Expressive (and any PSR-15 applications) since last summer, with a number of components already completed, and others close to completion. We plan to finalize these in the next few months.

Thank You!

We extend a hearty thank you to everyone who tested the various pre-releases and provided feedback. Additionally, we are singling out the following individuals who provided significant contributions to the Expressive 3 project:

  • Enrico Zimuel provided a ton of feedback and critique during the design phase, and was a driving force behind many of the API usability decisions.

  • Rob Allen did a workshop at SunshinePHP, right as we dropped our initial alpha releases, and provided feedback and testing for much of our tooling additions.

  • Frank Brückner provided ongoing feedback and review of pull requests, primarily around documentation; he is also responsible for a forthcoming rewrite of our documentation theme to make it more responsive and mobile-friendly.

  • Daniel Gimenes provided feedback and ideas as we refactored zend-stratigility; he is the one behind package-level utility functions such as Zend\Stratigility\doublePassMiddleware(), Zend\Stratigility\path(), and more.

  • Witold Wasiczko provided the majority of the rewrite of zend-stratigility for version 3. He can be celebrated for removing over half the code from that repository!

In addition to these people, I want to extend a personal thank you to the following people:

  • Geert Eltink has helped maintain Expressive v2, and particularly the various routers and template engines, making them ready for v3 and testing continually. As a maintainer, I was able to rely on him to take care of merges as we finalized the releases, and was pleasantly surprised to wake up to new releases several times when he fixed critical issues in our alpha and RC releases.

  • Michał Bundyra provided a constant stream of pull requests related to quality assurance (including ongoing work on our phpcs extension!), as well as critical review of incoming patches. He spearheaded important work in the refactoring process, including changes to how we handle response prototypes, and critical fixes in our routers to address issues with how we detect allowed methods for path route matches. We synced each and every single day, often arguing, but always coming to consensus and plowing on.

If you get a chance, reach out to these contributors and thank them for the release!


  • 0: The Expressive ecosystem makes use of many other standards as well, including PSR-7 HTTP Messages, PSR-11 Container, and PSR-13 HTTP Links.

  • 1: As an example, the routing, dispatch, and "implicit methods" middleware were all moved to the zend-expressive-router package, as they each work with the router and route results.

  • 2: Request generation, application dispatch, and response emission were all moved to a new package, zend-httphandlerrunner.

  • 3: These refactors led to a net removal of code across the board, vastly simplifying the internals. This will lead to ease of maintenance, greater stability, and, based on benchmarks we've been performing, 10% better performance and less system resource usage.

  • 4: factory:create uses PHP's Reflection API in order to determine what dependencies are in place in order to generate a factory class; it also registers the class and factory with the container!

  • 5: In previous Expressive versions, we referred to "actions", which were any middleware that returned a response instead of delegating to another layer of the application. PSR-15 calls such classes request handlers. Our tooling provides an action:create command, however, for those who prefer the "action" verbiage.

  • 6: The command creates a template named after the handler created; it uses the root namespace of the class to determine where to put it in the filesystem. Additionally, it alters the generated request handler to render the template into a zend-diactoros HtmlResponse!


This week, we've worked on backports from Expressive 3 to Expressive 2, and, in the process, identified a few issues with how the routing package handles implicit HEAD and OPTIONS requests. As a result, we've just released 3.0.0rc2:

What are "implicit" HEAD and OPTIONS requests?

Implicit HEAD and OPTIONS requests are requests using those methods made to routes that do not explicitly define them; in other words, if no routes for a given path include the HEAD or OPTIONS methods.

We provide a way for router implementations to flag a routing failure as being due to requesting a method that is not explicitly allowed. We also provide middleware for providing responses to HEAD and OPTIONS requests under those conditions, as well as separate middleware for simply reporting that a method is not allowed.

Getting started with RC2

To start a new project based on 3.0.0rc2, use Composer to create a new project:

$ composer create-project "zendframework/zend-expressive-skeleton:3.0.0rc2"

If you want to install to a custom directory name, use the following instead:

$ composer create-project zendframework/zend-expressive-skeleton {your directory} 3.0.0rc2

Once installed, you can follow the same instructions as for RC1.

Updating from RC1

Updating from RC1 requires a few manual steps.

Prior to upgrading, you will need to do the following:

$ composer require "zendframework/zend-diactoros:^1.7.1"

Then run:

$ composer update

Once done, you will need to make one change to your config/pipeline.php.

Locate the following line:


Cut the line, and paste it following the line reading:


This change is necessary due to how each of these middleware inspect the routing result and act on it. If MethodNotAllowedMiddleware operates before the Implicit*Middleware, it will detect a 405 condition. Moving it after those middleware allow them to intercept for HEAD and OPTIONS requests.


We still have a number of tasks to accomplish before the stable 3.0.0 release. In particular:

  • We need to provide full documentation for the v3 release.

  • We will be issuing a 2.2 release with:

  • We need to document migration from v2.2 to v3, and potentially provide automated tooling.

  • We anticipate users may still find bugs in the RC, and will be actively incorporating bugfixes before the stable release.

Our target date is still 15 March 2018, but we need your help! Help by testing the RC2 skeleton and providing your feedback. As we prepare the v2.2 release, help by testing tooling and applying our migration documentation to your applications, and let us know what works and what doesn't. If you find features that are not documented, let us know by filing issues or asking questions in our Slack.

We look forward to the stable release, and the positive impact PSR-15 will have on the PHP ecosystem!


We've been working diligently the past three weeks to finalize API changes and new features for the Expressive 3.0 release, and are pleased to announce immediate availability of our first release candidate, 3.0.0rc1!

Why RC and not beta?

Why the jump from alpha to release candidate? Most of our planned features also contained API changes, whether these were namespace or class name changes, signature changes due to adopting type hints, or wholesale refactors. As a result, we held an extended alpha release cycle so that we could continue making API changes as we worked on new features. In the last three weeks, we've continued to release new alpha versions of components, which users were picking up as they updated; surprisingly, the majority of these continued to work due to efforts at backwards compatibility that we made along the way.

We feel at this point that we've identified and implemented all desired changes both in terms of API as well as features, and announced a feature freeze yesterday. This puts us in a status more analogous to release candidates than beta (where features could still be added).

In this post, we'll cover:

Getting started with RC1

To start a new project based on 3.0.0rc1, use Composer to create a new project:

$ composer create-project "zendframework/zend-expressive-skeleton:3.0.0rc1"

If you want to install to a custom directory name, use the following instead:

$ composer create-project zendframework/zend-expressive-skeleton {your directory} 3.0.0rc1

The installer will prompt you for a number of things:

  • What style install (minimal, flat, or modular).
  • Which DI container to use.
  • Which router to use.
  • Which template engine to use.
  • Which error handler to use.

We recommend the defaults, except in the case of a template engine; in that case, choose the one you're most familiar with.

Once the installer has gathered its information, it will begin installing dependencies, enable development mode, and then let you know it's done!

You can create your first request handler to handle an incoming request using our tooling:

$ composer expressive handler:create "App\Handler\HelloWorldHandler"


Run the following command to find out what other commands we expose:

$ composer expressive help

You can also get help on individual commands:

$ composer expressive help handler:create

This will create a new class and tell you where in the filesystem you'll find it; it will also create a factory and tell you about that. If you have enabled a template engine, it will also create a template file and tell you where it is.

To route to it, edit the file config/routes.php and add the following line within the callback it defines:

    $app->get('/hello/world', App\Handler\HelloWorldHandler::class);

Next, fire up the built-in web server:

$ composer serve

and navigate to http://localhost:8080/hello/world to see your handiwork!

You can also create middleware to add to your application pipeline, or within route-specific pipelines. From here, you have the basic building blocks and application structure to get started!

Migrating from alpha3 to rc1

While a ton has changed between the 3.0.0alpha3 release and today, most applications built on alpha3 should be able to continue working as they were before, due to a number of backwards compatibility efforts we put into place to aid migration both for alpha users, as well as v2 users.

However, we recommend making the following changes to your application as well, to ensure it follows the structure that will be used in the final stable release.


Whenever you see a constraint with the format ^X.Y.ZalphaW || ^X.Y, remove the || ^X.Y part of the constraint. This ensures that a previous alpha version cannot be installed if you use the --prefer-lowest flag when running composer update. More stable versions will still be installed when they become available.


Ensure the following configuration providers are present:


Most likely, you will have been prompted to install these already anyways, but double-check to be sure.


alpha3 refered to the following classes via import statements; they should be updated per the following table:

alpha3 reference rc1 reference
Zend\Expressive\Middleware\DispatchMiddleware Zend\Expressive\Router\Middleware\DispatchMiddleware
Zend\Expressive\Middleware\ImplicitHeadMiddleware Zend\Expressive\Router\Middleware\ImplicitHeadMiddleware
Zend\Expressive\Middleware\ImplicitOptionsMiddleware Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware
Zend\Expressive\Middleware\NotFoundMiddleware Zend\Expressive\Handler\NotFoundHandler
Zend\Expressive\Middleware\RouteMiddleware Zend\Expressive\Router\Middleware\PathBasedRoutingMiddleware

Also add the following import statement:

use Zend\Expressive\Router\Middleware\MethodNotAllowedMiddleware;

Change the line reading:




Change the line reading:




Changes between alpha3 and rc1

A number of substantial changes were released within the core Expressive packages. We'll detail them package-by-package, providing cumulative changes (vs. release-by-release changes), with general changes affecting all packages listed first.

General changes

Potentially the most far-reaching change was a decision to no longer compose response prototypes in classes that need to produce a response, but rather response factories. All classes that previously accepted a Psr\Http\Message\ResponseInterface instance (with the exception of Zend\Stratigility\Middleware\DoublePassMiddleware) now accept a PHP callable capable of producing a ResponseInterface instance. The simplest form, using zend-diactoros, would look like the following:

function () {
    return new Response();

We made this change for a variety of reasons.

First, not all containers allow marking an instance as non-shared. This meant that a ResponseInterface service would return the same instance each time. While this is generally fine, as the various with*() methods produce new instances, it falls apart when you write to the response body, as the body is the one part of a response that is mutable. (We often addressed this fact by also composing a stream factory, for producing an empty body stream to use with a response.) While we could solve this problem by indicating the ResponseInterface service was non-shared, this would not work on all containers, and led to convoluted solutions such as using "virtual services" to refer to discrete instances.

Second, an upcoming specification from PHP-FIG will largely address this. The proposed PSR-17 defines a number of interfaces describing factories for PSR-7 HTTP messages.

By changing our classes to compose a callable factory, we accomplish several things:

  • We can now share the ResponseInterface service safely, and re-use it in any service that needs to produce a response. Since the factory will always return a new response instance, sharing the factory for the ResponseInterface service is safe.

  • When PSR-17 is available, we will be able to decorate its response factory via a closure and continue to use it. Expressive applications will be immediately PSR-17 compatible.

Solving type-safety issues

The one problem with passing a PHP callable for the factory is that we have no guarantees that it actually returns a PSR-7 ResponseInterface!

To solve this, each class that composes a response factory re-assigns it as follows:

$this->responseFactory = function () use ($responseFactory) : ResponseInterface {
    return $responseFactory();

This approach ensures that a TypeError is raised if the factory returns any other type!


Stratigility received the following changes since the Expressive alpha3 release.

  • The Zend\Stratigility\Middleware\ErrorHandler and NotFoundHandler classes were updated to accept response factories instead of prototypes, as outlined in the previous section.

  • All middleware and handlers, as well as the Next implementation, were marked final.


zend-expressive-router underwent a massive rewrite. The rewrite can be characterized as two major changes: routes and route results are now middleware, and all middleware from zend-expressive other than the LazyLoadingMiddleware was moved to this package.

One general change was made: the package now ships a ConfigProvider class and exposes it to zend-component-installer. This allows it to ship factories for middleware it exposes and ensure that middleware can be used immediately within Expressive applications.

Routes and RouteResults

Zend\Expressive\Router\Route now implements the PSR-15 MiddlewareInterface. Its process() method proxies to the middleware passed to its constructor.

Zend\Expressive\Router\RouteResult also now implements MiddlewareInterface. In the case of a successful result, its process() method will proxy to the route matched. In the case of a failed match, the method instead acts as a no-op, proxying directly to the handler argument. Due to these changes, we felt we could remove the getMatchedMiddleware() method; the middleware is still accessible via the composed Route class, and, generally speaking, you should not need direct access to it.

These changes greatly simplify the DispatchMiddleware, as it no longer needs to check if a successful match occurred, and can instead process the route result directly.

Additionally, the RouteMiddleware::process() logic was simplified, as it no longer needs to conditionally inject a RouteResult as a request attribute; it does it regardless of the result of matching.


All previously provided middleware (RouteMiddleware, PathBasedRoutingMiddleware, and DispatchMiddleware) were moved to the Zend\Expressive\Router\Middleware namespace.

Additionally, we moved the following middleware from zend-expressive into this package:

zend-expressive middleware class zend-expressive-router middleware class
Zend\Expressive\Middleware\ImplicitHeadMiddleware Zend\Expressive\Router\Middleware\ImplicitHeadMiddleware
Zend\Expressive\Middleware\ImplicitOptionsMiddleware Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware

As detailed in the General changes section, each was modified to compose a response factory instead of a prototype.

These middleware were imported into zend-expressive-router as they are closely related to routing:

  • ImplicitHeadMiddleware introspects a RouteResult in order to return a response if a HEAD request was made and other conditions are met.

  • ImplicitOptionsMiddleware introspects a RouteResult in order to return a response if an OPTIONS request was made and other conditions are met.

We also refactored the RouteMiddleware (and, by extension, the PathBasedRoutingMiddleware) in order to extract the functionality for indicating a 405 Method Not Allowed response. This functionality is now shipped in a new class, Zend\Expressive\Router\Middleware\MethodNotAllowedMiddleware, which should be piped immediately following the RouteMiddleware or PathBasedRoutingMiddleware. This solves a problem several users have reported: we previously had no ability to modify how a 405 response is returned. You can now pipe alternative middleware for this feature if you desire using templates, Problem Details, or other formats.


The zend-expressive package also had a large number of changes. The majority were related to exporting functionality to more relevant packages; other changes were made to provide backwards compatibility with previous alpha releases, as well as v2 releases.

API/Behavior changes.

  • Zend\Expressive\Container\ApplicationConfigInjectionDelegator now raises an exception if the $callback argument produces an instance of anything other than a Zend\Expressive\Application instance. Previously, it would return it immediately. Now, it raises an exception in order to detail to the user what changes they need to make to their application.

  • The exceptions Zend\Expressive\Exception\InvalidMiddlewareException and MissingDependencyException were updated to implement the PSR-11 ContainerExceptionInterface.

New classes

  • Zend\Expressive\Response\ServerRequestErrorResponseGenerator is an invokable class capable of generating a response from a Throwable as provided by Zend\HttpHandlerRunner\RequestHandlerRunner. The class Zend\Expressive\Container\ServerRequestErrorResponseGeneratorFactory was updated to create and return an instance of this class.

  • Zend\Expressive\Response\ErrorResponseGeneratorTrait contains the bulk of the logic for generating an error response used both by the above class, as well as the existing Zend\Expressive\Middleware\ErrorResponseGenerator.

New factories

The package provides two new factories:

  • Zend\Expressive\Container\ResponseFactoryFactory will return a PHP callable capable of producing a PSR-7 ResponseInterface. By default, the class provides a closure around instantiation of a zend-diactoros Response instance. The package maps it to the service Psr\Http\Message\ResponseInterface.

  • Zend\Expressive\Container\StreamFactoryFactory will return a PHP callable capable of producing a PSR-7 StreamInterface. By default, the class provides a closure around instantiation of a zend-diactoros Stream instance backed by a writable php://temp resource. The package maps it to the service Psr\Http\Message\StreamInterface.

Middleware removals

The following middleware were removed; in all cases, they were added to the zend-expressive-router package:

  • Zend\Expressive\Middleware\ImplicitHeadMiddleware
  • Zend\Expressive\Middleware\ImplicitOptionsMiddleware

The class Zend\Expressive\Middleware\NotFoundMiddleware was renamed to Zend\Expressive\Handler\NotFoundHandler, and now implements the PSR-15 RequestHandlerInterface (instead of MiddlewareInterface). The class now also composes a response factory instead of a response prototype. (Its related factory does this for you automatically.)

Factory removals

The following factories were removed, as they are either unnecessary, or provided by other packages:

  • Zend\Expressive\Container\RouteMiddlewareFactory (now provided in zend-expressive-router)
  • Zend\Expressive\Container\DispatchMiddlewareFactory (now provided in zend-expressive-router)
  • Zend\Expressive\Container\ImplicitHeadMiddlewareFactory (now provided in zend-expressive-router)
  • Zend\Expressive\Container\ImplicitOptionsMiddlewareFactory (now provided in zend-expressive-router)


We spent a fair amount of time on zend-expressive-tooling to provide both migration tools as well as tools to make you more productive during development. In particular, we added the following:

  • migrate:interop-middleware will migrate middleware implementing http-interop interfaces to PSR-15. (This was in previous releases, but not highlighted before!)

  • migrate:middleware-to-request-handler will scan a directory (your src/ tree by default) for middleware. When it detects middleware that does not call on its handler argument, it converts it to a request handler.

  • action:create is an alias to the handler:create detailed in the previous post on alpha3, and exposes the same set of functionality for creating a PSR-15 request handler. Some developers prefer the "Action" verbiage, and requested this feature.

  • factory:create will generate a factory class file for the given class, using PHP's Reflection API. The generated file will be a sibling to the original class file. The functionality also auto-registers the class and factory in your configuration.

    The middleware:create, handler:create, and action:create commands now all use this functionality to create a factory for the class generated and wire it in your application configuration. (You may disable this capability via a CLI switch.)

Additionally, we added template awareness to the handler:create and action:create commands. By default, if they detect a Zend\Expressive\Template\TemplateRendererInterface service in your container, they will:

  • Generate a template named after the root namespace and newly generated class (minus any Handler, Action, or Middleware suffix).
  • Place a template file in the configured template path for the root namespace, and named after the generated class.
  • Modify the generated class to accept a TemplateRendererInterface instance to its constructor, and render the named template to a zend-diactoros HtmlResponse.

The commands allow you to disable template capabilities via a switch, as well as provide an alternate template namespace, template name, and template file extension.

Ecosystem updates

The skeleton releases cover the core functionality of Expressive: Stratigility, Expressive itself, routing, template engines, and the helpers. However, the Expressive ecosystem includes other functionality as well:

  • zend-expressive-session and its adapters
  • zend-expressive-authentication and its adapters
  • zend-expressive-authorization and its adapters
  • zend-problem-details
  • zend-expressive-hal

We have provided alpha releases of each of these packages to provide the following:

  • PSR-15 support
  • Response factory (vs response prototype) support
  • Compatibility with core alpha and RC releases

As such, if you are using the RC1 skeleton (or have updated your alpha3 skeleton), you will be able to use these packages without issue; installing them will grab these latest alpha versions, which will be compatible with the stable release (and for which their own stable releases will work with the Expressive v3 release).


So, we're done, right?


There's still work that remains. In particular:

  • We plan to version the existing documentation. This will allow us to provide version-specific docs, without confusing users about different usage and declarations.

  • We need to provide full documentation for the v3 release.

  • We will be issuing a 2.2 release with:

    • Deprecations, based on the v3 changes.
    • Backports of select v3 changes in order to aid migration.
  • We need to document migration from v2.2 to v3, and potentially provide automated tooling.

  • We anticipate users will find bugs in the RC, and will be actively incorporating bugfixes before the stable release.

Our target date is still 15 March 2018, but we need your help! Help by testing the RC1 skeleton and providing your feedback. As we prepare the v2.2 release, help by testing tooling and applying our migration documentation to your applications, and let us know what works and what doesn't. If you find features that are not documented, let us know by filing issues or asking questions in our Slack.

We look forward to the stable release, and the positive impact PSR-15 will have on the PHP ecosystem!


  • 2018-03-01: Fixes a syntax error in the instructions for creating a project. Additionally, fixes a grammatical issue in one of the headers.