How the PHP Middleware Pattern works and can easily be applied
Tags: php, designpatterns, beginners, webdev
Published: 2022-05-04 05:24:58

In this post we'll be looking at Middleware in PHP. This pattern is most common in the handling of requests and responses. But the Middleware Pattern can also be applied in various other places. We'll look into what middleware is, how middleware works, when middleware can be useful and what an alternative to middleware might be.

_**Note:** The middleware pattern is not part of the patterns introduced by the 
[Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns), but I personally still view it as a pattern, as it can be applied in various situations._ 

## What is Middleware?

Middleware in PHP is a layer of actions / callables that are wrapped around a piece of core logic in an application. These middlewares provide the ability to change either the input or the output of this logic. So they "live" between the input and the output, right in the *middle*.

While these short explanations are obviously very easy to follow and *click* instantly, let me illustrate this with a small example:

```php
$input = [];
$output = $this->action($input);
```

In this example, a Middleware layer is able to:

1. change `$input` *before* it is injected into `$this->action()`
2. change `$output` *after* it was returned by `$this->action()`

Let's look at how it does this.

### How middleware works

The Middleware layer consists of a stack of middleware callables. These can be either a simple `Closure`, an invokable class or a method on a class.

Each one of the middleware callables is wrapped around the core action. Alright, I'll say it; it's like an *onion*, or a present with multiple layers of wrapping paper. The input gets injected into the first middleware (or most outer layer). The middleware can do something with the input, and then pass that (changed) input along to the next middleware, and the next, and the next, until it reaches the final action (the core). By this time, the input maybe changed a little or a lot.

Then the core action is executed and returns its output to the last middleware, which returns its output back to the previous middleware, all the way back to the first middleware. On its way back, every middleware can now change the output before it gets returned.

Before we look at some examples, let's first explore the different types of middleware.

## Types of middleware

While the concept of middleware is pretty much the same between them, there are actually *two* common types of middleware: *Single Pass Middleware* & *Double Pass Middleware*.

### Single Pass middleware

The most common type of middleware is *Single Pass Middleware*. With this type every middleware callable receives two parameters:

1. the input (e.g. a request, message, DTO or scalar value)
2. A `callable` to invoke the next middleware in the chain, that also receives a *single* parameter: the input.

> **Note:** In some conventions the callable parameter is called `$next` to indicate the invocation of the next middleware.

This type of middleware can change the input and forward that modified version to the next middleware. To affect the output; it will first call the next middleware and retrieve its output. It can then modify that output and return this instead.

Here is a small example middleware to illustrate the full behavior.

```php
function ($input, $next) {
  // Receive the input and change it.
  $input = $this->changeInput($input);
  
  // Call the next middleware or final action with the changed input, and receive the output.
  $output = $next($input); 
  
  // Change the output, and return it to the previous middleware or final consumer.
  return $this->changeOutput($output);
}
```

### Double Pass Middleware

The second type of middleware is *Double Pass Middleware*. With this type every middleware also receives a default *output* object as a parameter. So the "double" part refers to the middleware passing along both the input and an output to the next middleware / final action.

Why is this useful? In the *Single Pass* type a middleware must either:

1. Create the required output object when short-circuiting (not calling `$next` but returning a different result).
2. Call the `$next` middleware(s) to retrieve an output object, and change that.

Depending on the type of output, it can be cumbersome or even undesirable to make the middleware depend on a service or factory to create that output type. And it can be just as undesirable to call (and possibly instantiate) every other middleware and final action, only to discard everything they do; and change the output object completely.

So with the *Double Pass* a default output object is created beforehand and passed around. This way the middleware already has an output object of the correct type it can use when it wants to short-circuit.

Here is another small example to illustrate the full behavior.

```php
function ($input, $output, $next) {
  // Quickly return the output object, instead of passing it along.
  if ($this->hasSomeReason($input)) {
    return $output;
  }
  
  // Call the next middleware and return that output instead of the default output.
	return $next($input, $output); 
}
```

Now we know what a middleware callable looks like, but how can we wrap all these middlewares on top of an action?

## A simple middleware implementation

Let's dive into some code, and create a very basic *Single Pass* middleware implementation. In this example we will prepend & append some value to a string (re-)using a single middleware class.

> **Note:** There are many ways a middleware implementation can be built. This example isn't very optimized, and there is lots of room for improvement. But this example is really to demonstrate the inner workings of middleware as clear as possible.

As we've seen, we first need a core action (or function) to wrap our middleware around. Our action will simply receive
and return a string.

```php
$action = fn(string $input): string => $input;
```

Now we'll create an invokable middleware class that we can use multiple times with a different value to prepend and append.

```php
class ValueMiddleware
{
    public function __construct(string $value) {}

    public function __invoke(string $input, callable $next): string
    {
        // Prepend value to the input.
        $output = $next($this->value . $input);

        // Append value to the output.
        return $output . $this->value;
    }
}
```

The middleware class is instantiated with a `$value`. This instance will be our middleware callable, since it is an invokable class. Let's create 3 of them:

```php
$middlewares = [
    new ValueMiddleware('1'),
    new ValueMiddleware('2'),
    new ValueMiddleware('3'),
];
```

Now we'll add all of these middlewares as a layer around our `$action`, and then we'll examine the code.

```php
foreach ($middlewares as $middleware) {
    $action = fn(string $input): string => $middleware($input, $action);
}
```

Let's look at what is happening in this loop.

- At first the `$action ` closure is our simple callback which returns the `$input`.
- In the first loop `$action` is overwritten with a new closure that also receives the `$input` (just like the initial action). When this closure is executed, it will call the first middleware, and provide that input, as well as our initial `$action`. So inside that middleware `$next` is now the original action.
- In the second and third loop `$action` is overwritten again, but in this middleware the `$next` callable is not the original action, but the closure we set at the previous iteration.

> **Note:** As we can see, `$next` never directly refers to a middleware. It can't, because the middleware method signature expects 2 parameters, while `$next` only receives one; the input. `$next` is always a callable which is responsible for calling the next middleware. So this can also be a method on a middleware handler that contains and keeps track of a list of (applied) middlewares.

And that's it. Our middleware implementation is complete. All we need to do now is run the `$action` with a value, and check out the response.

```php
echo $action('value');
```

The result of this will of course be: `123value123`... wait what? Did you expect `123value321`? That would make the whole onion thing make more sense, right? But it is actually correct.

The middleware that prepends and appends `1` is applied first, but is then wrapped with `2`, which in turn is wrapped by `3`. So middleware 3 is the first to prepend `3` on the input, but it is also the *last* middleware to append that value. It's a bit of a mind-bender, but when we check out the `$input` and `return` for every middleware we end up with this list:

| Middleware   | $input   | return      |
|--------------|----------|-------------|
| Middleware 3 | value    | 123value123 |
| Middleware 2 | 3value   | 123value12  |
| Middleware 1 | 23value  | 123value1   |
| Core-action  | 123value | 123value    |

Working our way down the `$input` list, and back up the `return` list, we can see what stages the value passes through in this entire middleware flow.

> **Tip:** If you want the order of middlewares to be outside-in, where the top of the list is the first one to be called, you should `array_reverse` the array, or use a Last-In-First-Out (LIFO) iterator like a `Stack`.

## Request and Response middleware

One of the most common places you'll find middleware being used is within a framework that transforms a `Request` object into a `Response` object. [PSR-15: HTTP Server Request Handlers](https://www.php-fig.org/psr/psr-15/) in the PHP Standard Recommendation (PSR), is a recommendation on how a ([PSR-7](https://www.php-fig.org/psr/psr-7/)) `Request` object should be handled and turned into a `Response` object. This recommendation also contains the `Psr\Http\Server\MiddlewareInterface`. This interface favors the use of a `process` method on a middleware class, but the principle is the same. It receives an input (the `Request` object), modifies it, and passes it along to `RequestHandler` which will trigger the next middleware or final action.

> **Tip:** [Laminas' Mezzio](https://docs.mezzio.dev/) is a minimal PSR-7 middleware Framework which provides a set of PSR-15 middlewares. And while most frameworks that work with PSR-7 and PSR-15 use *Single Pass Middleware* (since that
is what PSR-15 recommends) it also provides a [*Double Pass Middleware* Decorator](https://docs.mezzio.dev/mezzio/v3/cookbook/double-pass-middleware/#using-double-pass-middleware), so those will also work with these PSRs.

> [Learn more about decorators and proxies](https://doeken.org/blog/decorator-vs-proxy-pattern)

### Examples of Request and Response middlewares

Middlewares can be used to perform all kinds of actions and checks during a request, so the main action (controller) can focus on the task at hand. Let's look at a few examples of middleware that can be useful.

#### Cross-site request forgery (CSRF) validation middleware

To prevent a [CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) attack a framework can choose to add a certain validation to a request. This validation need to happen before the main action is triggered. In this case a middleware can examine the request object. If it deems the request valid, it can pass it along to the next handler. But if the validation fails, the middleware can immediately short-circuit the request, and return a `403 Forbidden` response.

#### Multi-tenant middleware

Some applications allow for [multi-tenancy](https://en.wikipedia.org/wiki/Multitenancy), which basically means the same source code is used for different customers; e.g. by hooking up a separate database / source per customer. A middleware can inspect the request and figure out (for instance by checking the request URL) what database should be selected, or which customer should be loaded. It can even attach that `Customer` entity (if one exists) to the `Request` object, so the core action can read the proper information off the request object, instead of having to figure out the correct customer itself.

#### Exception handling middleware

Another great type of middleware is the handling of exceptions. If this middleware is applied as the outermost middleware it can wrap the entire request to response flow in a try-catch block. If an exception is thrown, this middleware can log the exception and then return a properly configured response object which contains information about the exception.

## Other real world examples of middleware

While middleware is most common in the handling of requests and responses, there are other use cases. Here are a some examples:

### Symfony Messenger middleware

The [Queue handler of Symfony](https://symfony.com/doc/current/messenger.html) `symfony/messenger` uses middleware to manipulate the `Message` when it is sent to the queue, and when it is read from the queue and handled by the `MessageHandler`.

One example of these is the `router_context` middleware. Because messages are (mostly) handled asynchronous, the original `Request` is not available. This middleware stores the original `Request` state (things like the host and HTTP port) when it is sent to the queue and restores this when the `Message` is handled, so the handler can use this context to build up things like absolute URLs.

### Guzzle middleware

While also somewhat in the realm of Request and Response; the Guzzle HTTP Client also supports the use of [middleware](https://docs.guzzlephp.org/en/stable/handlers-and-middleware.html#middleware). These again allow you to change the request and response to and from the HTTP request. This implementation works a bit different, but still revolves around a callback that triggers the next middleware / action.

## Alternative to middleware

Like we've seen, middleware essentially allows us to change input and output respectively *before* and *after* a core action, within a single function / callable. So a great alternative to middleware would be to provide event hooks *before* and *after* the core action. With these events you can change the input before it hits the core action, and maybe even return early. And afterwards you can change the output.

Both of these options work quite well. For instance [Laravel uses middleware](https://laravel.com/docs/9.x/middleware#introduction) around their Request and Response flow, while [Symfony uses the Events](https://symfony.com/doc/current/reference/events.html) approach.

> **Note:** If you want to learn more about events, you can check out my (quite lengthy) blog post on [Event Dispatching](https://doeken.org/blog/event-dispatching-exploration).

## Thanks for reading

When using middleware you can easily create a separation of concerns within your code. Every middleware has a tiny job to do. It can easily be tested; and the core action can focus on what it does best, without worrying about any possible edge cases or side effects. And while it currently mostly revolves around (PSR-7) Request and Response objects, the pattern can be used widely. Do you have other ideas where this pattern can be useful? Let me know!