Containers
The Middleware Factory
With version 3, we made a conscious choice to use strong type-hinting wherever possible. However, we also recognize that doing so can sometimes be an inconvenience to the user and lead to an explosion in code verbosity.
One area in particular that concerned us was the Application
instance itself,
and the various methods it exposes for piping and routing middleware. If we made
each of these strictly typed, users would be forced to write code that looks
like the following:
use Psr\Container\ContainerInterface;
use Zend\Expressive\Application;
use Zend\Expressive\Middleware\LazyLoadingMiddleware;
use Zend\Stratigility\MiddlewarePipe;
use function Zend\Stratigility\middleware;
use function Zend\Stratigility\path;
return function (Application $app, ContainerInterface $container) : void {
$app->pipe(path(
'/foo',
new LazyLoadingMiddleware(App\FooMiddleware::class, $container)
));
$app->pipe(middleware(function ($request, $handler) {
// ...
}));
$booksPipeline = new MiddlewarePipe();
$booksPipeline->pipe(new LazyLoadingMiddleware(
Zend\ProblemDetails\ProblemDetailsMiddleware::class,
$container
));
$booksPipeline->pipe(new LazyLoadingMiddleware(
App\SessionMiddleware::class,
$container
));
$booksPipeline->pipe(new LazyLoadingMiddleware(
App\AuthenticationMiddleware::class,
$container
));
$booksPipeline->pipe(new LazyLoadingMiddleware(
App\AuthorizationMiddleware::class,
$container
));
$booksPipeline->pipe(new LazyLoadingMiddleware(
Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware::class
$container
));
$booksPipeline->pipe(new LazyLoadingMiddleware(
App\ValidationMiddleware::class
$container
));
$booksPipeline->pipe(new LazyLoadingMiddleware(
App\Handler\CreateBookHandler::class
$container
));
$app->post('/books/{id:\d+}', $booksPipeline);
};
Additionally, this would pose an enormous burden when migrating to version 3.
For these reasons, we developed the class Zend\Expressive\MiddlewareFactory
.
It composes a MiddlewareContainer in order to back
the following operations.
callable
$middleware = $factory->callable(function ($request, $handler) {
});
This method takes a callable middleware, and decorates it as a
Zend\Stratigility\Middleware\CallableMiddlewareDecorator
instance.
handler
$middleware = $factory->handler($requestHandler);
This method takes a PSR-15 request handler instance and decorates it as a
Zend\Stratigility\Middleware\RequestHandlerMiddleware
instance.
lazy
$middleware = $factory->lazy(App\Middleware\FooMiddleware::class);
This method decorates the service name using
Zend\Expressive\Middlware\LazyLoadingMiddleware
, passing the composed
MiddlewareContainer
to the instance during instantiation.
pipeline
$pipeline = $factory->pipeline(
$middlewareInstance,
'MiddlewareServiceName',
function ($request, $handler) {
},
$requestHandlerInstance
);
Creates and returns a Zend\Stratigility\MiddlewarePipe
, after passing each
argument to prepare()
first.
(You may pass an array of values instead of individual arguments as well.)
prepare
$middleware = $factory->prepare($middleware);
Inspects the provided middleware argument, with the following behavior:
MiddlewareInterface
instances are returned verbatim.RequestHandlerInterface
instances are decorated usinghandler()
.callable
arguments are decorated usingcallable()
.string
arguments are decorated usinglazy()
.array
arguments are decorated usingpipeline()
.
Usage in bootstrapping
The skeleton defines two files config/pipeline.php
and config/routes.php
.
These are expected to return a callable with the following signature:
use Psr\Container\ContainerInterface;
use Zend\Expressive\Application;
use Zend\Expressive\MiddlewareFactory;
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void {
};
Note that the MiddlewareFactory
is passed to these callables; this gives you
the ability to use it for more complex piping and routing needs, including
creating nested pipelines.
As an example, we'll rewrite our initial example to use the MiddlewareFactory
:
use Psr\Container\ContainerInterface;
use Zend\Expressive\Application;
use Zend\Expressive\MiddlewareFactory;
use function Zend\Stratigility\path;
return function (Application $app, MiddlewareFactory $factory, ContainerInterface $container) : void {
$app->pipe(path('/foo', $factory->prepare(App\FooMiddleware::class)));
$app->pipe($factory->prepare(function ($request, $handler) {
// ...
}));
$app->post('/books/{id:\d+}', $factory->pipeline(
ProblemDetailsMiddleware::class,
App\SessionMiddleware::class,
App\AuthenticationMiddleware::class,
App\AuthorizationMiddleware::class,
Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware::class,
App\ValidationMiddleware::class,
App\Handler\CreateBookHandler::class
));
};
Further simplifications
Internally,
Application
'spipe()
and various routing methods make use of theMiddlewareFactory
already;pipe()
also already makes use ofpath()
as well. As such, usage of theMiddlewareFactory
is not strictly necessary in the above example; it is used for illustrative purposes only.
Found a mistake or want to contribute to the documentation? Edit this page on GitHub!