samuelgfeller/slim-error-renderer

Slim 4 error handling middleware and exception page renderer

Fund package maintenance!
Ko Fi

Installs: 178

Dependents: 3

Suggesters: 0

Security: 0

Stars: 0

Watchers: 1

Forks: 0

Open Issues: 0

Type:project

1.2.1 2024-04-23 13:17 UTC

This package is auto-updated.

Last update: 2024-11-16 12:17:47 UTC


README

Latest Version on Packagist Software License Build Status Total Downloads

This package provides an alternative to the default Slim error handler and renderer.
It renders a styled error details page with the stack trace and the error message or a generic error page for production.

Custom error page renderers can be created to change the design of the error pages by implementing the ErrorDetailsPageRendererInterface or GenericErrorPageRendererInterface.

It also provides a middleware to make the project "exception-heavy", which means that it will throw exceptions with a stack trace for notices and warnings during development and testing like other frameworks such as Laravel or Symfony.

Preview

Requirements

  • PHP 8.2+
  • Composer
  • A Slim 4 application

Installation

Install the package with composer

Open a terminal in your project's root directory and run the following command:

composer require samuelgfeller/slim-error-renderer

Configuration

The following configuration values are required in the settings. Modify accordingly in the development, production, and testing configuration files.

File: config/defaults.php

$settings['error'] = [
    // Must be set to false in production
    'display_error_details' => false,
    // Whether to log errors or not
    'log_errors' => true,
];

Add the error handling middleware to the Slim app

Container instantiation

If you're using Dependency Injection, add the error handling middleware to the container definitions (e.g. in the config/container.php) file. The ExceptionHandlingMiddleware constructor accepts the following parameters:

  1. Required: instance of a response factory object implementing the Psr\Http\Message\ResponseFactoryInterface (see here for a default implementation of a response factory)
  2. Optional: instance of a PSR 3 logger to log the error
  3. Optional: boolean to display error details (documentation: Error Handling)
  4. Optional: contact email for the "report error" button on the error page
  5. Optional: A custom generic error page renderer that implements SlimErrorRenderer\Interfaces\ProdErrorPageRendererInterface
  6. Optional: A custom error details page renderer that implements SlimErrorRenderer\Interfaces\ErrorDetailsPageRendererInterface
<?php

use SlimErrorRenderer\Middleware\ExceptionHandlingMiddleware;

return [
    // ...
    
    ExceptionHandlingMiddleware::class => function (ContainerInterface $container) {
        $settings = $container->get('settings');
        $app = $container->get(App::class);
        
        return new ExceptionHandlingMiddleware(
            $app->getResponseFactory(),
            $settings['error']['log_errors'] ? $container->get(LoggerInterface::class) : null,            
            $settings['error']['display_error_details'],
            $settings['public']['main_contact_email'] ?? null
        );
    },
    
    // ...
];

Middleware stack

The middleware can now be added to the middleware stack in the config/middleware.php file.
It should be the very last middleware in the stack to catch all exceptions (Slim middlewares are executed in the reverse order they are added).
This replaces the default slim error middleware.

<?php

use Slim\App;

return function (App $app) {
    // ...

    // Handle exceptions and display error page
    $app->add(ExceptionHandlingMiddleware::class);
}

"Exception-heavy" middleware

The NonFatalErrorHandlingMiddleware promotes warnings and notices to exceptions when the display_error_details setting is set to true in the configuration.
This means that the error details for notices and warnings will be displayed with the stack trace and error message.

Container instantiation

The NonFatalErrorHandlingMiddleware also needs to be instantiated in the container.

The constructor takes three parameters:

  1. Required: bool to display error details
  2. Required: bool to log the warning / notice
  3. Optional: instance of a PSR 3 logger to log the warning / notice
<?php

use SlimErrorRenderer\Middleware\NonFatalErrorHandlingMiddleware;

return [
    // ...
    
    NonFatalErrorHandlingMiddleware::class => function (ContainerInterface $container) {
        $settings = $container->get('settings');
        
        return new NonFatalErrorHandlingMiddleware(
            $settings['error']['display_error_details'],
            $settings['error']['log_errors'] ? $container->get(LoggerInterface::class) : null,            
        );
    },
    
    // ...
];

Add to middleware stack

The middleware should be added right above the ExceptionHandlingMiddleware in the stack.

File: config/middleware.php

use Slim\App;

return function (App $app) {
    // ...

    // Promote warnings and notices to exceptions
    $app->add(NonFatalErrorHandlingMiddleware::class); // <- Add here
    // Handle exceptions and display error page
    $app->add(ExceptionHandlingMiddleware::class);
}

Conclusion

Have a look a the slim-starter for a default implementation of this package and the slim-example-project for a custom prod error page with layout.

Why use this package?

A reason this small library exists instead of using the default Slim error handler and a custom error renderer, is to provide the "exception-heavy" feature and better-looking error pages.
But these things can be achieved with a custom error renderer and middleware located in the project as well.

The issue with the default Slim\Handlers\ErrorHandler is that while testing, the $contentType in the error handler is null and instead of using any custom error renderer its hardcoded to use the Slim\Error\Renderers\HtmlErrorRenderer. This has two consequences:

  1. The error is not thrown while integration testing, which means debugging is harder.
  2. Tests where an exception is expected, fail with the PHPUnit 11 warning Test code or tested code did not remove its own error handlers. A fix for this message is calling restore_error_handler() but this can't be done as the error handler doesn't allow for custom error renderers when testing.

So a custom handler is required anyway, and with the custom renderers and the handling of non-fatal errors, it made sense to put that in a separate small library.

License

This project is licensed under the MIT license — see the LICENSE file for details.