What is middleware?

In the realm of web application development Middleware refers to functions wrapped around business logic. They rely upon the decorator pattern and are composed in a sort of layered stack. You could think of them as onion layers, with your business logic residing at the innermost stratum (more on Wikipedia).

Several modern PHP frameworks (such as Zend Expressive and Slim) embrace such framework-agnostic, reuse-oriented paradigm, wholly supporting PSR-7 compliant middleware.

Goal for PHP Middleworld is collecting all available PSR-7 compliant middleware and provide a unique, consistent repository, encouraging middleware reuse and exchange among PHP developers.

How does it work?

Middleware layers take an Http request as input and return an Http response as output. They act as a sort of filter during the request/response lifecycle of a web application. As a request is received, the middleware stack is traversed. Each middleware layer is then executed before the request reaches the designated business logic snippet, associated to the invoked route. Also, the middleware stack is traversed backwards after business logic has completed its job and before content is sent back to the client.

A small example

Slide 50

To get a better grasp on how middleware works, let's take an airplane trip as an example. In this context, there's no doubt that our core action is the flight itself (= the core action of our business logic). Before and after the flight takes place, though, we usually need to have our documents checked, drop and collect our luggages, board and alight the plane. If this context was a web application, we'd perform all of these actions through middleware functions. We'd have middleware for each of the steps, before and after the flight itself.

The nice part is that all other flights could take advantage of existing procedures, and there'd be no need to re-invent the wheel everytime, creating such procedures for every other flight. In the realm of web applications, all of these cross cutting concerns would be taken care by middleware.

How does middleware look like?

Middleware exploits PSR-7 Http abstractions and allows for reuse across framworks. The PHP Framework interoperability Group defined an interface, commonly known as Single pass, as part of the PSR-15 recommendation. An example of such a middleware (performing the doSomethingOnRequest action on the request, and the doSomethingOnResponse action on response) is described below:

class Middleware implements MiddlewareInterface
{
    function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
    ): ResponseInterface {
        if (!$this->preconditionsExist($request)) {
            throw new RuntimeException();
        }

        $request = $this->doSomethingOnRequest($request);

        $response = $handler->handle($request);

        return $this->doSomethingOnResponse($response);
    }
}

The request parameter represents the HTTP request, whereas the handler parameter is an abstraction over the inner application which the middleware is wrapping.

The ServerRequestInterface interface provides just a handle method, which accepts an HTTP request and returns an HTTP response

As middleware started to become popular in PHP, another approach, known as Double pass was commonly used. The above middleware would look like this as a Double pass middleware:

class Middleware
{
    function __invoke(
        RequestInterface $request,
        ResponseInterface $response,
        callable $next
    ): ResponseInterface {
        if (!$this->preconditionsExist($request, $response)) {
            throw new RuntimeException();
        }

        $request = $this->doSomethingOnRequest($request);

        $response = $next($request, $response);

        return $this->doSomethingOnResponse($response);
    }
}

The main difference from the previous approach is that here middleware receives three parameters intead of two, having also a ResponseInterface at our disposal. The $next parameter is a callable which receives the request to dispatch next middleware in the pipeline.

It's worth noticing here that such next parameter does not represent a piece of middleware (see signature difference), but rather a way for the middleware to have the framework locating and providing the next appropriate piece of middleware in the stack.