Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Error handling

Something always goes wrong: a post is not found, access is denied, the database is down. In Slim every such situation is signalled by throwing an exception, and the error middleware attached in config/middleware.php turns it into a response with the right status code.

HTTP exceptions

Slim has ready-made exception classes for typical errors. One of them is already used in ShowPostAction:

// src/Action/ShowPostAction.php
use Slim\Exception\HttpNotFoundException;
 
$post = $this->posts->find((int) $args['id']);
 
if ($post === null) {
    throw new HttpNotFoundException($request, 'Post not found');
}

Open /posts/999 — the response has code 404. The exception takes the request as the first argument and an optional message as the second.

ExceptionCodeWhen to throw
HttpBadRequestException400The request is malformed
HttpUnauthorizedException401Login is required
HttpForbiddenException403Login is not enough, access is denied
HttpNotFoundException404The record or page does not exist
HttpMethodNotAllowedException405The method does not fit the route

They all live in the Slim\Exception namespace. Slim throws 404 for an unknown address and 405 for a wrong method by itself. Any other exception — a bug in the code or an unreachable database — becomes a response with code 500.

The default page

With APP_DEBUG=true the middleware shows details: the exception class, the file, the line and the call chain. With false it shows a short page with the error name. It works, but does not look like the rest of the site.

Your own error template

Create a separate template. It does not extend the layout: the error handler works outside the other layers, and functions such as url_for() may not be connected yet at that moment.

{# templates/error.twig #}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Error {{ status }}</title>
</head>
<body>
    <h1>{{ status == 404 ? 'Page not found' : 'Something went wrong' }}</h1>
    <p><a href="/">Back to the home page</a></p>
</body>
</html>

Your own handler

addErrorMiddleware() returns the middleware object. It is given a function that builds the response instead of the standard one:

// config/middleware.php
use Psr\Http\Message\ServerRequestInterface;
use Slim\Exception\HttpException;
use Slim\Views\Twig;
 
$errorMiddleware = $app->addErrorMiddleware($settings['debug'], true, true);
 
if (!$settings['debug']) {
    $errorMiddleware->setDefaultErrorHandler(
        function (ServerRequestInterface $request, Throwable $exception) use ($app) {
            $status = $exception instanceof HttpException ? $exception->getCode() : 500;
 
            $response = $app->getResponseFactory()->createResponse($status);
 
            return $app->getContainer()->get(Twig::class)
                ->render($response, 'error.twig', ['status' => $status]);
        }
    );
}

For HTTP exceptions getCode() returns the status code; for everything else 500 is used. The custom handler is enabled only outside debug mode — during development Slim's detailed page is more useful.

Set APP_DEBUG=false in .env and open /posts/999 — your "Page not found" page appears.

Common mistake

Debug mode on a production server shows the visitor file paths, SQL query text and code fragments. The first argument of addErrorMiddleware() must be false on the server, and errors should be learned from the log — the subject of the next lesson. And do not print $exception->getMessage() in your template for 500 errors: it may contain internal details.

Thanks for reading!

Donate to the site — we work hard for you.