Problem Details Middleware

While returning a problem details response from your own middleware is powerful and flexible, sometimes you may want a fail-safe way to return problem details for any exception or PHP error that occurs without needing to catch and handle them yourself.

For this purpose, this library provides ProblemDetailsMiddleware.

This middleware does the following:

As such, you can register this in middleware stacks in order to automate generation of problem details for exceptions and PHP errors.

As an example, using Expressive, you could compose it as an error handler within your application pipeline, having it handle all errors and exceptions from your application:

$app->pipe(ProblemDetailsMiddleware::class);

Or for a subpath of your application:

$app->pipe('/api', ProblemDetailsMiddleware::class);

Alternately, you could pipe it within a routed-middleware pipeline:

$app->get('/api/books', [
    ProblemDetailsMiddleware::class,
    BooksList::class,
], 'books');

This latter approach ensures that you are only providing problem details for specific API endpoints, which can be useful when you have a mix of APIs and traditional web content in your application.

Listeners

The ProblemDetailsMiddleware allows you to register listeners to trigger when it handles a Throwable. Listeners are PHP callables, and the middleware triggers them with the following arguments, in the following order:

Note that each of these arguments are immutable; you cannot change the state in a way that that state will propagate meaningfully. As such, you should use listeners for reporting purposes only (e.g., logging).

As an example:

// Where $logger is a PSR-3 logger implementation
$listener = function (
    Throwable $error,
    ServerRequestInterface $request,
    ResponseInterface $response
) use ($logger) {
    $logger->error('[{status}] {method} {uri}: {message}', [
        'status'  => $response->getStatusCode(),
        'method'  => $request->getMethod(),
        'uri'     => (string) $request->getUri(),
        'message' => $error->getMessage(),
    ]);
};

Attach listeners to the ProblemDetailsMiddleware instance using its attachListener() method:

$middleware->attachListener($listener);

Factory

The ProblemDetailsMiddleware ships with a corresponding PSR-11 compatible factory, ProblemDetailsMiddlewareFactory. This factory looks for a service named Zend\ProblemDetails\ProblemDetailsResponseFactory; if present, that value is used to instantiate the middleware.

For Expressive 2 users, this middleware should be registered automatically with your application on install, assuming you have the zend-component-installer plugin in place (it's shipped by default with the Expressive skeleton).

Registering listeners

In order to register listeners, we recommend using a delegator factory on the Zend\ProblemDetails\ProblemDetailsMiddleware service.

As an example:

class LoggerProblemDetailsListenerDelegator
{
    public function __construct(ContainerInterface $container, $serviceName, callable $callback)
    {
        $middleware = $callback();
        $middleware->attachListener($container->get(LoggerProblemDetailsListener::class));
        return $middleware;
    }
}

You would then register this as a delegator factory in your configuration:

'delegators' => [
    ProblemDetailsMiddleware::class => [
        LoggerProblemDetailsListenerDelegator::class,
    ],
],