Executing and composing middleware

The easiest way to execute middleware is to write closures and attach them to a Laminas\Stratigility\MiddlewarePipe instance. You can nest MiddlewarePipe instances to create groups of related middleware, and attach them using a base path so they only execute if that path is matched.

$api = new MiddlewarePipe();  // API middleware collection
$api->pipe(/* ... */);        // repeat as necessary

$app = new MiddlewarePipe();  // Middleware representing the application
$app->pipe(new PathMiddlewareDecorator('/api', $api)); // API middleware attached to the path "/api"

Request path changes when path matched

When you use the PathMiddlewareDecorator using a path (other than '' or '/'), the middleware it decorates is dispatched with a request that strips the matched segment(s) from the start of the path. Using the previous example, if the path /api/users/foo is matched, the $api middleware will receive a request with the path /users/foo. This allows middleware segregated by path to be re-used without changes to its own internal routing.

Handling errors

While the above will give you a basic application, it has no error handling whatsoever. We recommend adding an initial middleware layer using the Laminas\Stratigility\Middleware\ErrorHandler class:

use Laminas\Diactoros\Response;
use Laminas\Stratigility\Middleware\ErrorHandler;

$app->pipe(new ErrorHandler(new Response());
// Add more middleware...

You can learn how to customize the error handler to your needs in the chapter on error handlers.

Decorating the MiddlewarePipe

Another approach is to compose a Laminas\Stratigility\MiddlewarePipe instance within your own Psr\Http\Server\MiddlewareInterface implementation, and optionally implementing the RequestHandlerInterface and/or pipe() method.

In such a case, you might define the process() method to perform any additional logic you have, and then call on the decorated MiddlewarePipe instance in order to iterate through your stack of middleware:

class CustomMiddleware implements MiddlewareInterface
{
    private $pipeline;

    public function __construct(MiddlewarePipe $pipeline)
    {
        $this->pipeline = $pipeline;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
    {
        // perform some work...

        // delegate to parent
        $this->pipeline->process($request, $handler);

        // maybe do more work?
    }
}

Another approach using this method would be to override the constructor to add in specific middleware, perhaps using configuration provided.

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Laminas\Stratigility\MiddlewarePipe;

class CustomMiddleware implements MiddlewareInterface
{
    private $pipeline;

    public function __construct(array $configuration, MiddlewarePipe $pipeline)
    {
        // do something with configuration ...

        // attach some middleware ...
        $pipeline->pipe(/* some middleware */);

        $this->pipeline = $pipeline;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
    {
        /* ... */
    }
}

These approaches are particularly suited for cases where you may want to implement a specific workflow for an application segment using existing middleware, but do not necessarily want that middleware applied to all requests in the application.