$app->notFound() for Slim v3

In Slim v2, there is the option to invoke the error or not found handler with respectively $app->error() or $app->notFound() inside your controller/closure. In Slim v3, the feature has been removed* but there is a way to manage the same result with middleware.

To clarify from the start, Slim automatically calls the not found handler when a request does not match any of your routes. But there are other cases: you query a DB item and got zero results, for example. In these cases, you want to trigger the 404 page yourself.

The principle idea is to set a Response with a 404 status code and without any body. This way, you have the minimal required code to trigger a 404 reduced to this:

return $response->withStatus(404);

If you are in need of a 404 page with a non-standard body (i.e. where your page design of the not found page is not the default 404 design you implemented), you can write a response to this body too. The middleware will check if any body is set, and only without body the default error handler is triggered.

With Slim v3, PSR-7 and middleware, everything above can be accomplished in a few lines of code:

// $c is the DI container of Slim
$app->add(function ($request, $response, $next) use ($c) {
    // First execute anything else
    $response = $next($request, $response);

    // Check if the response should render a 404
    if (404 === $response->getStatusCode() &&
        0   === $response->getBody()->getSize()
    ) {
        // A 404 should be invoked
        $handler = $c['notFoundHandler'];
        return $handler($request, $response);
    }

    // Any other request, pass on current response
    return $response;
}

In a similar fashion, the error handler could be invoked when you return a 500 error, and so on. With middleware, the layered design really shines and although it is a very small example, this already shows the power of middleware.

*) The feature was not actively “removed” but during the major rewrite of Slim the existing feature simply hasn’t been implemented back. And actually I think above is a cleaner solution than it existed before.