PSR-15 compatibility issues and proposal

A number of repositories have pull requests in place to use webimpress/http-middleware-compatibility in order to allow usage with the various iterations of the PSR-15 specification; this package provides polyfill support for the various versions.

I think we need to put a halt to this for now.

While I would really love to support all versions of the specification, it’s simply non-trivial. For those who are not aware of the history of the specification, the following is a summary of the various versions:

  • The 0.2.0 version of the http-interop/http-middleware package, which we followed for initial releases of Expressive, defined the namespace Interop\Http\Middleware with the interfaces ServerMiddlewareInterface and DelegateInterface, each defining the method process().
  • The 0.4.1 version of the http-interop/http-middleware package, supported by the 2.0 releases of Expressive, defined the Interop\Http\ServerMiddleware namespace, and renamed the ServerMiddlewareInterface to MiddlewareInterface.
  • The 0.5.0 version of the http-interop/http-middleware package renamed the namespace to Interop\Http\Server, renamed DelegateInterface to RequestHandlerInterface, and, futher, renamed the delegate’s process() method to handle().
  • That package was then split into two, http-interop/http-server-handler and http-interop/http-server-middleware; the two combined provide the RequestHandlerInterface and MiddlewareInterface, respectively.
  • Assuming no more changes occur and PSR-15 is accepted with the current architecture, we will have two new packages, psr/http-server-handler and psr/http-server-middleware, using the namespace Psr\Http\Server.

We run into a few problems trying to support the various versions.

First, the new http-interop packages (http-server-handler and http-server-middleware) declare themselves as conflicting with http-interop/http-middleware versions >= 0.5.0. This is okay , as the two packages combined declare the same namespace and interfaces. You require one or the other. For our purposes, we’d likely include http-interop/http-server-middleware, as that package has a requirement on http-server-handler as well.

Second, we cannot support each of the previous two versions of http-interop/http-middleware (0.4.1 and 0.2.0) simultaneously, as they are the same package; we can support one or the other UNLESS we define alternative interface definitions for the version not installed.

Third, the change in interface name from DelegateInterface to RequestHandlerInterface makes defining middleware within Stratigility and Expressive that supports multiple versions impossible, as it’s impossible to create a process() method that will comply with any version; it must comply with a single version at a time.

Fourth, middleware authors not only need to select the interface version they plan to target, but potentially also update any calls to the delegate/request handler. If they were targeting the DelegateInterface previously, and now target the RequestHandlerInterface, they also must update calls to process() within their own code to handle().

What webimpress/http-middleware-compatibility tried to do was:

  • pin a project to the http-interop/http-middleware version already in use, or, if on a new install, prompt the user for the version to use.
  • adapt the various interfaces by providing polyfills and providing alternative interfaces that extend those from multiple versions
  • provide a constant that references the appropriate delegate/request handler method name for use when calls to the delegate are needed

This worked fine… until the interfaces were split into two packages. At that point, the logic for choosing the package broke, as previously, you could just indicate the version to use, and now it would need to prompt for the package as well.

At this point, I wish we hadn’t updated Stratigility to use the compat package, as it’s causing more headaches than it should (in particular, developers are not aware what the various versions mean, nor that Expressive does not support the most recent versions at all); we should have just pinned to the 0.4.1 series of http-middleware and left it at that.

My inclination going forwards is that we create alpha or beta MAJOR releases that reference the new packages across any repository that currently implements or typehints against the interfaces, to allow developers to start testing against the latest spec revision. Then, when PSR-15 is approved, we do a new alpha or beta pinning to the final release, and the next MAJOR version supports PSR-15 only. In the meantime, we also start writing tools to migrate existing code as follows:

  • Update middleware to implement the new interface(s)
  • Update middleware delegation to use the new RequestHandlerInterface::handle() method

This approach would also be used here in zend-mvc for the MiddlewareListener.

So, in sum, my proposal for each affected repository is:

  • Either create a new branch for the new major version, or mark the develop branch as targeting the next major version (update the branch-alias config in composer.json).
  • Have that branch pin to http-interop/http-server-middleware ^1.0.1.
  • Update the minimum PHP version to 7.1 (per our next major release milestone announcement)
  • Update affected code:
    • Update any MiddlewareInterface implementations:
      • Ensure MiddlewareInterface is imported from the correct namespace.
      • Import RequestHandlerInterface instead of DelegateInterface.
      • Ensure any middleware process() methods reference RequestHandlerInterface instead of DelegateInterface.
      • Ensure any calls to the delegate call handle() instead of process().
    • Update any DelegateInterface implementations (should only affect zend-stratigility and zend-expressive):
      • Implement RequestHandlerInterface instead, and ensure that class is correctly imported.
      • Rename the process() method to handle().
  • Create tooling for updating existing MiddlewareInterface implementations targeting http-interop 0.4.1 to the latest specification version.
1 Like

@matthew I’ve already created issue in zend-expressive to track progress on implementing http-interop/http-server-middleware in expressive modules:


I’ve created also tool to migrate middlewares and delegators in zend-expressive-tooling:

Almost all PR are ready, expressive needs documentation (I’m going to work on it tomorrow).

I think you missed one thing - we should also bump PHP version to 7.0 or 7.1 (in my PR I pinned it to 7.1) because new interop interfaces requires PHP 7.0.

I’ve added a bullet point for that; good catch!

1 Like

I think we need to put a halt to this for now.

I completely agree. To me it’s an ugly patch work and shouldn’t have been accepted. I understand people want to move forward and use the latest version. This is ok if it’s close to its final stage and ready for voting. But until then don’t rely on alpha packages. Usually a PSR package evolves and improves over time. But PSR-15 had a non compatible overhaul 5 times or even more. I stopped myself after the release of PSR-15 0.4.1.

New major expressive packages should depend on the final PSR-15. The whole logic in stratigility and expressive is becoming to complicated while trying to support all PSR-15 versions.

Uhm, does that mean PSR-15 is about to be final now?

Yes. We will likely put it to a working group vote in the next week or so, at which point it enters review status. At the end of that, if we have no changes to make, we can put it to a core committee vote for acceptance.

The most recent changes (http-middleware 0.5.0, bumping the PHP requirement to v7, followed by the package split) were a round of final changes we wanted to make before going to the review stage.

Totally agree with the approach @matthew . Invokable -> process() wasn’t too painful, but I’ve got more irons in the fire now. Tooling would be good. Where is that happening? https://github.com/zendframework/zend-expressive-tooling/pull/39?

Oops, just caught up with #contributors. Have a good holiday @matthew

In that case I would takes this as an opportunity to clean up the code in Expressive and Stratiglity. Removing support for non final PSR interfaces simplifies the logic in stratigility and expressive a lot. Since Middleware is pulled from containers, maybe we can supply adapters which users can use inside the middleware factories for non final PSR-15 middleware packages.

  • Use the develop branches from each expressive package as alpha releases for the next major version.
  • Pin to http-interop/http-server-middleware ^1.0.1
  • Bump to PHP 7.1
  • Code cleanup: Remove all previous http-interop, container-interop and all other beta PSR interface support.
  • Supply adapters for older http-interop/http-server-middleware versions which users can use until 3rd party packages are upgraded.
  • As soon as PSR-15 is accepted and the PSR namespace is available, release packages with a beta / RC version.

@xtreamwayz I absolutely agree; any new major versions need to:

  • Bump the minimum supported PHP version to 7.1.
  • Remove support for draft specs.

With regards to supplying adapters, this may be feasible, but I have my doubts; the rename of DelegateInterface to HandlerInterface and its related rename of its defined method from process to handle makes adapting very difficult. I think the approach that @michalbundyra suggests — using tooling to auto-update your middleware — will be a better approach, and one we can easily document in a migration guide.

I’ve gone through the initial checklist that webimpress posted on the Expressive issue tracker, and have created a master list showing a representative order in which items should be updated. This includes the suggested branch name, and target release (which will be an alpha release in each case).

A number of components have requirements on components that require changes to work with PSR-15, but will themselves remain backwards compatible. I have noted those, and indicated the new dependency constraints that will make them compliant. These are primarily adapters (router, template renderers, authentication and authorization adapters, etc.).

Releases without additional dependencies

  • zend-stratigility: 3.0.0alpha1 (branch release-3.0.0)
  • zend-expressive-router: 3.0.0alpha1 (branch release-3.0.0)
  • zend-expressive-helpers: 5.0.0alpha1 (branch release-5.0.0)
  • zend-problem-details: 1.0.0alpha1 (branch release-1.0.0)
  • zend-expressive-session: 1.0.0alpha1 (branch release-1.0.0)
  • zend-expressive-authentication: 1.0.0alpha1 (branch release-1.0.0)

Releases with dependencies

  • zend-expressive: 3.0.0alpha1 (branch release-3.0.0)
    • requires zend-stratigility ^3.0.0alpha1 || ^3.0
    • requires zend-expressive-router ^3.0.0alpha1 || ^3.0
  • zend-expressive-authorization: 1.0.0alpha1 (branch release-1.0.0)
    • requires zend-expressive-router ^3.0.0alpha1 || ^3.0
  • zend-expressive-tooling: 1.0.0alpha1 (branch release-1.0.0)
    • requires stratigility ^3.0.0alpha1 || ^3.0
    • requires zend-expressive ^3.0.0alpha1 || ^3.0
    • requires zend-expressive-router ^3.0.0alpha1 || ^3.0

Backwards compatible components with dependencies

The following are not directly affected by the changes; we can add releases that
support the new alpha releases, and, likely, the new major releases, without any
breaks. This support can happen in either bugfix or minor releases; as such, no
projected release numbers are provided.

  • zend-expressive-authentication-oauth2
    • requires zend-expressive-authentication: ^0.2 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-authentication-basic
    • requires zend-expressive-authentication: ^0.1 || ^0.2 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-authentication-session
    • requires zend-expressive-session ^0.1.0 || ^1.0.0alpha1 || ^1.0
    • requires zend-expressive-authentication: ^0.2 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-authentication-zendauthentication
    • requires zend-expressive-authentication: ^0.2 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-authorization-acl
    • requires zend-expressive-authorization: ^0.1 || ^0.2 || ^0.3 || ^0.4 || 1.0.0alpha1 || ^1.0
    • requires zend-expressive-router ^2.1 || ^3.0.0alpha1 || ^3.0
  • zend-expressive-authorization-rbac
    • requires zend-expressive-authorization: ^0.1 || ^0.2 || ^0.3 || ^0.4 || ^1.0.0alpha1 || ^1.0
    • requires zend-expressive-router ^2.1 || ^3.0.0alpha1 || ^3.0
  • zend-expressive-aurarouter
    • requires zend-expressive-router ^2.1 || ^3.0.0alpha1 || ^3.0
  • zend-expressive-fastroute
    • requires zend-expressive-router ^2.1 || ^3.0.0alpha1 || ^3.0
  • zend-expressive-zendrouter
    • requires zend-expressive-router ^2.1 || ^3.0.0alpha1 || ^3.0
  • zend-expressive-platesrenderer
    • requires zend-expressive-router ^1.3.2 || ^2.1 || ^3.0.0alpha1 || ^3.0
    • requires zend-expressive-helpers ^2.2 || ^3.0.1 || ^4.0 || ^5.0.0alpha1 || ^5.0
  • zend-expressive-twigrenderer
    • requires zend-expressive-router ^1.3.2 || ^2.1 || ^3.0.0alpha1 || ^3.0
    • requires zend-expressive-helpers ^1.4 || ^2.2 || ^3.0.1 || ^4.0 || ^5.0.0alpha1 || ^5.0
  • zend-expressive-zendviewrenderer
    • requires zend-expressive-router ^1.3.2 || ^2.1 || ^3.0.0alpha1 || ^3.0
    • requires zend-expressive-helpers ^1.4 || ^2.2 || ^3.0.1 || ^4.0 || ^5.0.0alpha1 || ^5.0
  • zend-expressive-flash
    • requires zend-expressive-session ^0.1 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-csrf
    • requires zend-expressive-session ^0.1.0 || ^1.0.0alpha1 || ^1.0
    • requires zend-expressive-flash ^0.1.0 || ^1.0 (+ any new minor version)
  • zend-expressive-session-ext
    • requires zend-expressive-session ^0.1.0 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-hal
    • requires (dev) zend-expressive-helpers ^4.2 || 5.0.0alpha1 || ^5.0

We’ve begun work on these changes, and, in doing so, discovered that if we update libraries defining interfaces to require PHP 7.1, any dependent packages typically need to be updated as well, due to additions of things such as:

  • scalar type hints (requires PHP 7.0)
  • Return type hints (of any type; requires PHP 7.0)
  • void return type hint (requires PHP 7.1)
  • nullable type hints (requires PHP 7.1)

This specifically came up when updating zend-expressive-router; proposed changes would update the RouterInterface to add return type hints (including void) as well as one or more scalar type hints on method arguments. If we do this, then dependent packages, such as zend-expressive-fastroute, can not update to the newer version, as the implementation it creates would not have compatible signatures.

Essentially, this means new major versions for every Expressive-based package, due to either PSR-15 adoption or PHP 7.1 adoption.

As such, I’m breaking the list into different sections:

  • Releases due to PSR-15 adoption (these bump to 7.1 as a side-effect).
  • Releases that depend on packages with PSR-15 changes.
  • Releases due to PHP 7.1 adoption only.
  • Releases that depend on packages with PHP 7.1 changes (which could be packages adopting PSR-15).
  • Releases that are completely backwards compatible.

Releases adopting PSR-15

  • zend-stratigility: 3.0.0alpha1 (branch release-3.0.0)
  • zend-expressive-router: 3.0.0alpha1 (branch release-3.0.0)
  • zend-expressive-helpers: 5.0.0alpha1 (branch release-5.0.0)
  • zend-problem-details: 1.0.0alpha1 (branch release-1.0.0)
  • zend-expressive-session: 1.0.0alpha1 (branch release-1.0.0)
  • zend-expressive-authentication: 1.0.0alpha1 (branch release-1.0.0)

Releases depending on PSR-15 packages

  • zend-expressive: 3.0.0alpha1 (branch release-3.0.0)
    • requires zend-stratigility ^3.0.0alpha1 || ^3.0
    • requires zend-expressive-router ^3.0.0alpha1 || ^3.0
    • zend-expressive-aurarouter (branch release-3.0.0)
    • zend-expressive-fastroute (branch release-3.0.0)
    • zend-expressive-zendrouter (branch release-3.0.0)
  • zend-expressive-authorization: 1.0.0alpha1 (branch release-1.0.0)
    • requires zend-expressive-router ^3.0.0alpha1 || ^3.0
    • requires zend-expressive-authentication: ^1.0.0@dev || ^1.0
  • zend-expressive-authentication-oauth2
    • requires zend-expressive-authentication: ^0.2 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-flash: 1.0.0alpha1 (branch release-1.0.0)
    • requires zend-expressive-session ^0.1 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-csrf: 1.0.0alpha1 (branch release-1.0.0)
    • requires zend-expressive-session ^0.1.0 || ^1.0.0alpha1 || ^1.0
    • requires zend-expressive-flash ^0.1.0 || ^1.0 (+ any new minor version)
  • zend-expressive-tooling: 1.0.0alpha1 (branch release-1.0.0)
    • requires stratigility ^3.0.0alpha1 || ^3.0
    • requires zend-expressive ^3.0.0alpha1 || ^3.0
    • requires zend-expressive-router ^3.0.0alpha1 || ^3.0

Releases adopting PHP 7.1

  • zend-expressive-template (branch release-2.0.0)

Releases depending on PHP 7.1 packages

  • zend-expressive-aurarouter (branch release-3.0.0)
    • requires zend-expressive-router ^3.0.0alpha1 || ^3.0
  • zend-expressive-fastroute (branch release-3.0.0)
    • requires zend-expressive-router ^3.0.0alpha1 || ^3.0
  • zend-expressive-zendrouter (branch release-3.0.0)
    • requires zend-expressive-router ^3.0.0alpha1 || ^3.0
  • zend-expressive-platesrenderer
    • requires zend-expressive-template ^2.0.0-dev || ^2.0
    • requires zend-expressive-router ^3.0.0dev || ^3.0
    • requires zend-expressive-helpers ^5.0.0-dev || ^5.0
  • zend-expressive-twigrenderer
    • requires zend-expressive-template ^2.0.0-dev || ^2.0
    • requires zend-expressive-router ^3.0.0dev || ^3.0
    • requires zend-expressive-helpers ^5.0.0-dev || ^5.0
  • zend-expressive-zendviewrenderer
    • requires zend-expressive-template ^2.0.0-dev || ^2.0
    • requires zend-expressive-router ^3.0.0dev || ^3.0
    • requires zend-expressive-helpers ^5.0.0-dev || ^5.0

Backwards compatible components with dependencies

The following are not directly affected by the changes; we can add releases that support the new alpha releases, and, likely, the new major releases, without any breaks. This support can happen in either bugfix or minor releases; as such, no projected release numbers are provided.

  • zend-expressive-authentication-basic 0.1.2
    • requires zend-expressive-authentication: ^0.1 || ^0.2 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-authentication-session 0.2.1
    • requires zend-expressive-session ^0.1.0 || ^1.0.0alpha1 || ^1.0
    • requires zend-expressive-authentication: ^0.2 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-authentication-zendauthentication 0.2.1
    • requires zend-expressive-authentication: ^0.2 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-authorization-acl 0.1.2
    • requires zend-expressive-authorization: ^0.1 || ^0.2 || ^0.3 || ^0.4 || 1.0.0alpha1 || ^1.0
    • requires zend-expressive-router ^2.1 || ^3.0.0alpha1 || ^3.0
  • zend-expressive-authorization-rbac 0.1.3
    • requires zend-expressive-authorization: ^0.1 || ^0.2 || ^0.3 || ^0.4 || ^1.0.0alpha1 || ^1.0
    • requires zend-expressive-router ^2.1 || ^3.0.0alpha1 || ^3.0
  • zend-expressive-session-ext 0.1.2
    • requires zend-expressive-session ^0.1.0 || ^1.0.0alpha1 || ^1.0
  • zend-expressive-hal 0.6.1
    • requires (dev) zend-expressive-helpers ^4.2 || 5.0.0alpha1 || ^5.0

Are these releases intended to be 1-1 ports from zend-mvc or something different entirely? For example zend-expressive-flash is now different, does not have namespaces that zend-mvc-flash-pluginmessenger has. Is feature parity important, or these will be treated as separate independent projects with own unrelated features and usage, and if so, will there still be maintenance accepted in mvc repos or should bug fixing effort in their old repos be forwared here instead?

@Alexander_Romanenko These packages all exist already and target Expressive specifically; the work above describes changes to make the compatible with the final version of the PSR-15 specification only.

New packages you point out (zend-expressive-flash, etc.) had RFCs elsewhere in the forums, and are not always intended to have feature parity with the zend-mvc equivalents.

zend-mvc is still actively maintained, and will continue to be for the foreseeable future; in fact, @xerkus is currently working on changes for a v4 release!

1 Like