Reference
Dispatching PSR-7 Middleware
PSR-7 defines interfaces for HTTP messages, and is now being adopted by many frameworks; Zend Framework itself offers a parallel microframework targeting PSR-7 with Expressive. What if you want to dispatch PSR-7 middleware from zend-mvc?
zend-mvc currently uses zend-http for its HTTP transport layer, and the objects it defines are not compatible with PSR-7, meaning the basic MVC layer does not and cannot make use of PSR-7 currently.
However, starting with version 2.7.0, zend-mvc offers
Zend\Mvc\MiddlewareListener
. This dispatch
listener listens prior to the default DispatchListener
, and executes if the
route matches contain a "middleware" parameter, and the service that resolves to
is callable. When those conditions are met, it uses the PSR-7 bridge
to convert the zend-http request and response objects into PSR-7 instances, and
then invokes the middleware.
Mapping routes to middleware
The first step is to map a route to PSR-7 middleware. This looks like any other routing configuration, with one small change: instead of providing a "controller" in the routing defaults, you provide "middleware":
// Via configuration:
return [
'router' =>
'routes' => [
'home' => [
'type' => 'literal',
'options' => [
'route' => '/',
'defaults' => [
'middleware' => 'Application\Middleware\IndexMiddleware',
],
],
],
],
],
];
// Manually:
$route = Literal::factory([
'route' => '/',
'defaults' => [
'middleware' => 'Application\Middleware\IndexMiddleware',
],
]);
Middleware may be provided as PHP callables, or as service names.
As of 3.1.0 you may also specify an array
of middleware, and middleware
may be http-interop/http-middleware
compatible. Each item in the array must be a PHP callable, service name, or
http-middleware instance. These will then be piped into a
Zend\Stratigility\MiddlewarePipe
instance in the order in which they are
present in the array.
No action required
Unlike action controllers, middleware typically is single purpose, and, as such, does not require a default
action
parameter.
Middleware services
In a normal zend-mvc dispatch cycle, controllers are pulled from a dedicated
ControllerManager
. Middleware, however, are pulled from the application
service manager.
Middleware retrieved must be PHP callables. The MiddlewareListener
will
create an error response if non-callable middleware is indicated.
Writing middleware
Prior to 3.1.0, when dispatching middleware, the MiddlewareListener
calls it
with two arguments, the PSR-7 request and response, respectively. As such, your
middleware signature should look like the following:
namespace Application\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
class IndexMiddleware
{
public function __invoke(ServerRequestInterface $request, ResponseInterface $response)
{
// do some work
}
}
Starting in 3.1.0, the MiddlewareListener
always adds middleware to a
Zend\Stratigility\MiddlewarePipe
instance, and invokes it as
http-interop/http-middleware,
passing it a PSR-7 ServerRequestInterface
and an http-interop
DelegateInterface
.
As such, ideally your middleware should implement the MiddlewareInterface
from
http-interop/http-middleware:
namespace Application\Middleware;
use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
class IndexMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, DelegateInterface $delegate)
{
// do some work
}
}
Alternately, you may still write callable
middleware using the following
signature:
function (ServerREquestInterface $request, ResponseInterface $response, callable $next)
{
// do some work
}
In the above case, the DelegateInterface
is decorated as a callable.
In all versions, within your middleware, you can pull information from the composed request, and return a response.
Routing parameters
At the time of the 2.7.0 release, route match parameters were not yet injected into the PSR-7
ServerRequest
instance, and thus not available as request attributes.With the 3.0 release, they are pushed into the PSR-7
ServerRequest
as attributes, and may thus be fetched using$request->getAttribute($attributeName)
.
Middleware return values
Ideally, your middleware should return a PSR-7 response. When it does, it is
converted back to a zend-http response and returned by the MiddlewareListener
,
causing the application to short-circuit and return the response immediately.
You can, however, return arbitrary values. If you do, the result is pushed into
the MvcEvent
as the event result, allowing later dispatch listeners to
manipulate the results.
Found a mistake or want to contribute to the documentation? Edit this page on GitHub!