mindplay/middleman
Dead simple PSR-15 / PSR-7 middleware dispatcher., (*1)
Provides (optional) integration with a variety
of dependency injection containers compliant with container-interop., (*2)
To upgrade from 1.x to 2.x, please see UPGRADING.md., (*3)
, (*4)
Let's stop trying to make this complicated:, (*5)
use Psr\Http\Message\RequestInterface as Request;
use Zend\Diactoros\Response;
$dispatcher = new Dispatcher([
function (Request $request, callable $next) {
return $next($request); // delegate control to next middleware
},
function (Request $request) {
return (new Response())->withBody(...); // abort middleware stack and return the response
},
// ...
]);
$response = $dispatcher->dispatch($request);
For simplicity, the middleware stack itself is immutable - if you need a stack you can manipulate, array
, ArrayObject
, SplStack
etc. are all fine choices., (*6)
If you prefer implementing middleware as a reusable class, just implement __invoke()
with the correct callback signature - or, optionally, implement MiddlewareInterface, like this:, (*7)
use Psr\Http\Message\RequestInterface as Request;
class MyMiddleware implements MiddlewareInteface
{
public function __invoke(Request $request, callable $next) {
// ...
}
}
Note that this works with or without implements MiddlewareInterface
, as long as you get the callback signature right., (*8)
If you want to wire it to a DI container
you can add a "resolver", which gets applied to every element in your middleware stack - for example:, (*9)
$dispatcher = new Dispatcher(
[
RouterMiddleware::class,
ErrorMiddleware::class,
],
new ContainerResolver($container)
);
Note that the "resolver" is any callable with a signature like function (string $name) : MiddlewareInterface
- if
you want the Dispatcher
to integrate deeply with your framework of choice, you can use a custom resolver closure., (*10)
If you want to understand precisely how this component works, the whole thing is just one class
with a few lines of code - if you're going to base your next
project on middleware, you can (and should) understand the whole mechanism., (*11)
, (*12)
Middleware?
Middleware is a powerful, yet simple control facility., (*13)
If you're new to the concept of middleware, the following section will provide a basic overview., (*14)
In a nutshell, a middleware component is a function (or MiddlewareInterface instance)
that takes an incoming (PSR-7) RequestInterface
object, and returns a ResponseInterface
object., (*15)
It does this in one of three ways: by assuming, delegating, or sharing responsibility
for the creation of a response object., (*16)
1. Assuming Responsibility
A middleware component assumes responsibility by creating and returning a response object,
rather than delegating to the next middleware on the stack:, (*17)
use Zend\Diactoros\Response;
function ($request, $next) {
return (new Response())->withBody(...); // next middleware won't be run
}
Middleware near the top of the stack has the power to completely bypass middleware
further down the stack., (*18)
2. Delegating Responsibility
By calling $next
, middleware near the top of the stack may choose to fully delegate the
responsibility for the creation of a response to other middleware components
further down the stack:, (*19)
function ($request, $next) {
if ($request->getMethod() !== 'POST') {
return $next($request); // run the next middleware
} else {
// ...
}
}
Note that exhausting the middleware stack will result in an exception - it's assumed that
the last middleware component on the stack always produces a response of some sort, typically
a "404 not found" error page., (*20)
3. Sharing Responsibility
Middleware near the top of the stack may choose to delegate responsibility for the creation of
the response to middleware further down the stack, and then make additional changes to
the returned response before returning it:, (*21)
function ($request, $next) {
$result = $next($request); // run the next middleware
return $result->withHeader(...); // then modify it's response
}
The middleware component at the top of the stack ultimately has the most control, as it may
override any properties of the response object before returning., (*22)