Subscribe to PHP Freaks RSS

PSR-15

syndicated from planet-php.net on January 23, 2018

Yesterday, following a unanimous vote from its Core Committee, PHP-FIG formally accepted the proposed PSR-15, HTTP Server Handlers standard.

This new standard defines interfaces for request handlers and middleware. These have enormous potential impact on the PHP ecosystem, as they provide standard mechanisms for writing HTTP-facing, server-side applications. Essentially, they pave the way for developers to create re-usable web components that will work in any application that works with PSR-15 middleware or request handlers!

Caveat

I acted as sponsor on PSR-15, and as final arbiter of changes during the review period.

Background

PSR-15 was started by Woody Gilk, who has acted in the role of Editor for its duration. The original intent was to ratify a middleware standard, and it was initially thought that it would be a quick ratification of a pattern that was already in wide use:

function (
    ServerRequestInterface $request,
    ResponseInterface $response,
    callable $next
) : ResponseInterface

where $next should implement the following signature:

function (
    ServerRequestInterface $request,
    ResponseInterface $response
) : ResponseInterface

"Double Pass"

The above pattern has been dubbed "double pass" middleware, for the fact that it passes two instances to the collaborator to pass to the next layer.

However, a number of critiques of this existing practice started to arise almost immediately, with one from Anthony Ferrara holding particular weight. The primary problems noted were:

  • Passing the response from layer to layer can lead to issues where an outer layer makes a change to the response it passes to an inner layer, expecting it to propagate back out, but an inner layer returns a different response entirely. Essentially, the pattern promotes problematic practices. If middleware needs to operate on a response, it should operate on the response returned by another layer.

  • Typehinting $next as callable means there's no way to ensure that the callable is actually capable of accepting the arguments passed to it. In other words, it's not type safe.

After debate within the working group, the next iteration proposed the following (some details differ, but basic interactions are the same):

interface DelegateInterface
{
    public function process(ServerRequestInterface $request) : ResponseInterface;
}

interface MiddlewareInterface { public function process( ServerRequestInterface $request, DelegateInterface $delegate ) : ResponseInterface; }

This largely solved the problems highlighted above. However, a few more details came up as different teams developed implementations.

First, many noted that they felt defining the same method name prevented polymorphism. A common use case was to define a "request handler" that could be called and which would in turn process itself. So, we updated the interfaces as follows:

interface RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request) : ResponseInterface;
}

interface MiddlewareInterface { public function process( ServerRequestInterface $request, RequestHandlerInterface $handler ) : ResponseInterface; }

Second, once that change was done, a number of others noted that the request handler could be useful in and of itself. For example, when creating a simple site, you could marshal a server request, pass it to a handler, and emit the response returned; middleware might not be necessary in this case. Another use case is for the final, internal end points of a middleware application: instead of implementing these as middleware, one could implement them as request handlers instead, as they do not operate on the results of the handler.

As a result, we made the change to ship the two interfaces as separate packages, with the package containing the MiddlewareInterface depending on the package defining the RequestHandlerInterface.

Finally, over the close to two years that this specification was being developed, PHP 7 gained in maturity, with 7.1 and 7.2 releases. We decided to pin the specification to PHP 7 or greater, and formally adopted return type hints within it.

While work on the specification

Truncated by Planet PHP, read more at the original (another 11583 bytes)