On this page
Dispatching Middleware
Apart from PSR-15 Psr\Http\Server\MiddlewareInterface
or Psr\Http\Server\RequestHandlerInterface
middleware or
service name strings which resolve to such instances, the middleware
key in the route definition takes also an
instance of the PipeSpec
class for the middleware pipe comprising the former.
Technically, middleware or middleware pipe defined in each individual mvc route is a complete request handler: it must produce a response and cannot delegate to a controller or middleware defined in another route.
Here are some examples how middleware can be dispatched:
MiddlewareInterface
or RequestHandlerInterface
Service Strings
If you only dispatch one class, the preferred method is passing a container service class-string or alias which
resolves to a PSR-15 Psr\Http\Server\MiddlewareInterface
or Psr\Http\Server\RequestHandlerInterface
.
Laminas\Stratigility\MiddlewarePipe
implements both interfaces and could be provided by the application container
as a configured instance. It is what is used internally for the PipeSpec
definitions.
use Application\Handler\AlbumDetailHandler;
use Application\Middleware\AlbumDetailMiddlewarePipe;
use Laminas\Mvc\Middleware\PipeSpec;
use Laminas\Router\Http\Literal;
return [
'router' => [
'routes' => [
'container-handler-class-literal' => [
'type' => Literal::class,
'options' => [
'route' => '/container-handler-class-literal',
'defaults' => [
'controller' => PipeSpec::class,
'middleware' => AlbumDetailHandler::class,
],
],
],
'container-handler' => [
'type' => Literal::class,
'options' => [
'route' => '/container-handler',
'defaults' => [
'controller' => PipeSpec::class,
'middleware' => 'container_id_for_album_detail_handler',
],
],
],
'container-pipe' => [
'type' => Literal::class,
'options' => [
'route' => '/container-pipe',
'defaults' => [
'controller' => PipeSpec::class,
'middleware' => AlbumDetailMiddlewarePipe::class,
],
],
],
'container-middleware' => [
'type' => Literal::class,
'options' => [
'route' => '/container-middleware',
'defaults' => [
'controller' => PipeSpec::class,
'middleware' => SomeMiddleware::class,
],
],
],
],
],
];
PipeSpec
with PSR-15 Service Names
Middleware can be passed as a PipeSpec
object with container service class-string literals. This is the preferred
method, if you need to dispatch multiple middleware instances.
Assuming you have two middleware, Application\Middleware\SelectLanguageMiddleware
which determines the language of
the visitor and Application\Middleware\AlbumFromRouteMiddleware
which fetches an Album
object by the ID in the route
param. Both of them add this information to the ServerRequestInterface
attributes which can be used by the
Application\Handler\AlbumDetailHandler
at the end of the pipe:
use Application\Handler\AlbumDetailHandler;
use Application\Middleware\AlbumFromRouteMiddlware;
use Application\Middleware\SelectLanguageMiddleware;
use Laminas\Mvc\Middleware\PipeSpec;
use Laminas\Router\Http\Segment;
return [
'router' => [
'routes' => [
'album-details' => [
'type' => Segment::class,
'options' => [
'route' => '/album/:album_id',
'defaults' => [
'controller' => PipeSpec::class,
'middleware' => new PipeSpec(
SelectLanguageMiddleware::class,
AlbumFromRouteMiddelware::class,
AlbumDetailHandler::class
),
],
],
],
],
],
];
Anonymous Interface Implementations
You can also pass an anonymous class which implements MiddlewareInterface
or RequestHandlerInterface
directly.
This is intended primarily for development because it can cause issues with configuration caching.
use Laminas\Diactoros\Response\TextResponse;
use Laminas\Mvc\Middleware\PipeSpec;
use Laminas\Router\Http\Literal;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
return [
'router' => [
'routes' => [
'instance-middleware' => [
'type' => Literal::class,
'options' => [
'route' => '/instance-middleware',
'defaults' => [
'controller' => PipeSpec::class,
'middleware' => new class implements MiddlewareInterface {
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
// You have to return a response directly because attempting to use $handler here will
// always result in an empty pipeline exception.
// This middleware pipe does not delegate to controllers or middleware from other routes.
return new TextResponse('Hello!');
}
},
],
],
],
'instance-handler' => [
'type' => Literal::class,
'options' => [
'route' => '/instance-handler',
'defaults' => [
'controller' => PipeSpec::class,
'middleware' => new class implements RequestHandlerInterface {
public function handle(
ServerRequestInterface $request
): ResponseInterface {
return new TextResponse('Hello!');
}
},
],
],
],
],
],
];
Closures
A Closure
can also be passed directly. This should also be used only for development.
use Laminas\Mvc\Middleware\PipeSpec;
use Laminas\Router\Http\Literal;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
return [
'router' => [
'routes' => [
'closure' => [
'type' => Literal::class,
'options' => [
'route' => '/closure',
'defaults' => [
'controller' => PipeSpec::class,
'middleware' => static function (
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
return $handler->handle($request);
},
],
],
],
],
],
];
Mixed PipeSpec
Arguments
In this example multiple different possible parameters are passed to PipeSpec
:
use Application\Handler\AlbumDetailHandler;
use Laminas\Mvc\Middleware\PipeSpec;
use Laminas\Router\Http\Literal;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
return [
'router' => [
'routes' => [
'instance-pipespec-dev-env' => [
'type' => Literal::class,
'options' => [
'route' => '/instance-pipespec-dev-env',
'defaults' => [
'controller' => PipeSpec::class,
'middleware' => new PipeSpec(
// id/alias of service in container
'container_id_for_album_middleware',
// anonymous class implementing a MiddlewareInterface
new class implements MiddlewareInterface {
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
return $handler->handle($request);
}
},
// Closure
static function (
ServerRequestInterface $request,
RequestHandlerInterface $handler
): ResponseInterface {
return $handler->handle($request);
},
// class-string of a handler as a convenient way to specify id/alias
// which must be resolved by the container
AlbumDetailHandler::class,
),
],
],
],
],
],
];
Note
Please note that if you pass instances of middleware or handlers directly, it might not be compatible with config caching. You should use that for development only. The recommended variant is passing container managed middleware like
SomeRequestHandler::class
.
Defining :middleware
Placeholder in Route Definition
Although you can define a :middleware
placeholder in your middleware route definition, we are strongly advising
against doing so. This can be utilized maliciously to invoke middleware that was never intended to be called directly.
Since middleware lookup uses the main application container, it can also be abused to cause instantiation of expensive
services leading to a DoS vulnerability.