PHPackages                             snicco/psr7-error-handler - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Debugging &amp; Profiling](/categories/debugging)
4. /
5. snicco/psr7-error-handler

ActiveLibrary[Debugging &amp; Profiling](/categories/debugging)

snicco/psr7-error-handler
=========================

A powerful and customizable error handler for psr7 apps.

v1.10.1(1y ago)019.7k11LGPL-3.0-onlyPHPPHP ^7.4|^8.0

Since Apr 17Pushed 1y ago1 watchersCompare

[ Source](https://github.com/snicco/psr7-error-handler)[ Packagist](https://packagist.org/packages/snicco/psr7-error-handler)[ RSS](/packages/snicco-psr7-error-handler/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (5)Versions (35)Used By (11)

A powerful and customizable error handler for PSR-7/PSR-15 applications
=======================================================================

[](#a-powerful-and-customizable-error-handler-for-psr-7psr-15-applications)

[![codecov](https://camo.githubusercontent.com/a99e6ec528fffd1664e95534f9a09a4a09d2afe62799ff0d8774dc22d8453f6c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f436f7665726167652d3130302532352d73756363657373)](https://codecov.io/gh/snicco/snicco)[![Psalm Type-Coverage](https://camo.githubusercontent.com/c12cfed65c7da16501f7a84e7861b8c4757fc30e9dc00bb2983783dbb3f3f84c/68747470733a2f2f73686570686572642e6465762f6769746875622f736e6963636f2f736e6963636f2f636f7665726167652e7376673f)](https://shepherd.dev/github/snicco/snicco)[![Psalm level](https://camo.githubusercontent.com/c5e90ffcf3a5aa1f78f93bddde5db7627b114329393aa87697df8cedc7f5391a/68747470733a2f2f73686570686572642e6465762f6769746875622f736e6963636f2f736e6963636f2f6c6576656c2e7376673f)](https://psalm.dev/)[![PhpMetrics - Static Analysis](https://camo.githubusercontent.com/364ffb28ea219affd0fed2e99cc046bac0bf41da3f1d3814e0cbe4a4bb54c994/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5068704d6574726963732d5374617469635f416e616c797369732d326561343466)](https://snicco.github.io/snicco/phpmetrics/Psr7ErrorHandler/index.html)[![PHP-Versions](https://camo.githubusercontent.com/241a10d25aa09d5e8a82ebd2b55780a63dd43736d958d4004c3166e650874aca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d253545372e34253743253545382e30253743253545382e312d626c7565)](https://camo.githubusercontent.com/241a10d25aa09d5e8a82ebd2b55780a63dd43736d958d4004c3166e650874aca/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d253545372e34253743253545382e30253743253545382e312d626c7565)

The **ErrorHandler** component of the [**Snicco** project](https://github.com/snicco/snicco) is a standalone error handler for **PHP** applications that use **PSR-7** requests.

Table of contents
-----------------

[](#table-of-contents)

1. [Installation](#installation)
2. [Needed collaborators](#usage)
    1. [RequestAwareLogger](#the-requestawarelogger)
    2. [ExceptionInformationProvider](#the-exceptioninformationprovider)
    3. [ExceptionDisplayer](#the-exceptiondisplayer)
3. [Full example](#full-working-example)
4. [Exception utilities](#exception-utilities)
5. [Contributing](#contributing)
6. [Issues and PR's](#reporting-issues-and-sending-pull-requests)
7. [Security](#security)

Installation
------------

[](#installation)

```
composer require snicco/psr7-error-handler
```

Collaborators for the HTTP error handler
----------------------------------------

[](#collaborators-for-the-http-error-handler)

On a high level, the [`HttpErrorHandler` interface](src/HttpErrorHandler.php) is responsible for transforming instances of `Throwable` into instances of `Psr\Http\Message\ResponseInterface`.

This package provides a [`TestErrorHandler`](src/TestErrorHandler.php), which just re-throws exceptions and a [`ProductionErrorHandler`](src/ProductionErrorHandler.php), which is the main focus of this documentation.

To instantiate the [`ProductionErrorHandler`](src/ProductionErrorHandler.php), **we need the following collaborators:**

---

### The `RequestAwareLogger`

[](#the-requestawarelogger)

The [`RequestAwareLogger`](src/Log/RequestAwareLogger.php) is a simple wrapper class around a **PSR-3 logger**.

It allows you to add [log context](https://www.php-fig.org/psr/psr-3/#13-context) to each log entry depending on the caught exception, the current request, etc.

The last argument passed to `RequestAwareLogger::__construct()` is variadic and accepts instances of [`RequestLogContext`](src/Log/RequestLogContext.php).

This is how you use it:

(Monolog is just an example, you can use any PSR-3 logger.)

```
use Psr\Log\LogLevel;
use Snicco\Component\Psr7ErrorHandler\Information\ExceptionInformation;
use Snicco\Component\Psr7ErrorHandler\Log\RequestAwareLogger;
use Snicco\Component\Psr7ErrorHandler\Log\RequestLogContext;

$monolog = new Monolog\Logger();

$request_aware_logger = new RequestAwareLogger($monolog);

// With custom exception levels.
// By default, any status code > 500 will be LogLevel::CRITICAL
// Anything below will be LogLevel::ERROR
$request_aware_logger = new RequestAwareLogger($monolog, [
    Throwable::class => LogLevel::ALERT,
    MyCustomException::class => LogLevel::WARNING
])

// With custom log context:
class AddIPAddressFor403Exception implements RequestLogContext {

    public function add(array $context, ExceptionInformation $information) : array{

        if(403 === $information->statusCode()) {
            $context['ip'] = $information->serverRequest()->getServerParams()['REMOTE_ADDR'];
        }
        return $context;
    }
}

// The last argument is variadic.
$request_aware_logger = new RequestAwareLogger($monolog, [], new AddIPAddressFor403Exception());
```

---

### The `ExceptionInformationProvider`

[](#the-exceptioninformationprovider)

The [`ExceptionInformationProvider`](src/Information/ExceptionInformationProvider.php) is responsible for converting instances of `Throwable` to an instance of [`ExceptionInformation`](src/Information/ExceptionInformation.php).

[`ExceptionInformation`](src/Information/ExceptionInformation.php) is a **value object** that consist of:

- a unique identifier for the exception that will be passed as [log context](https://www.php-fig.org/psr/psr-3/#13-context) and can be displayed to users.
- an HTTP status code that should be used when displaying the exception.
- a safe title for displaying the exception (safe meaning "does not contain sensitive information").
- a safe message for displaying the exception (safe meaning "does not contain sensitive information").
- the original `Throwable`
- a transformed `Throwable`
- the original instance of `Psr\Http\Message\ServerRequestInterface`

This package comes with a [`InformationProviderWithTransformation`](src/Information/InformationProviderWithTransformation.php) implementation of that interface.

You can instantiate this class like so:

```
use Snicco\Component\Psr7ErrorHandler\Identifier\SplHashIdentifier;
use Snicco\Component\Psr7ErrorHandler\Information\InformationProviderWithTransformation;

// uses spl_object_hash to uniquely identify exceptions.
$identifier = new SplHashIdentifier();

// This will use the error messages in /resources/en_US.error.json
$information_provider = InformationProviderWithTransformation::fromDefaultData($identifier);

// Or with custom error messages
$error_messages = [
    // The 500 status code is mandatory. All other HTTP status codes are optional.
    500 => [
        'title' => 'Whoops, this did not work...',
        'message' => 'An error has occurred... We are sorry.'
    ];
]
$information_provider = new InformationProviderWithTransformation($error_messages, $identifier);
```

As its class name suggests, the [`InformationProviderWithTransformation`](src/Information/InformationProviderWithTransformation.php)allows you to transform exceptions into other kinds of exceptions.

This is done by using [`ExceptionTransformers`](src/Information/ExceptionTransformer.php).

An example on how to transform custom exception classes to an instance of [`HttpException`](src/HttpException.php).

```
use Snicco\Component\Psr7ErrorHandler\Information\ExceptionTransformer;

class CustomAuthenticationTo404Transformer implements ExceptionTransformer {

    public function transform(Throwable $e) : Throwable{

        if(!$e instanceof MyCustomAuthenticationException) {
            return $e;
        }

        // Key value pairs of headers that will later be added to the PSR-7 response.
        $response_headers = [
            'WWW-Authenticate' => '/login'
        ];

        // The status code that should be used for the PSR-7 response.
        $status_code = 401;

        return \Snicco\Component\Psr7ErrorHandler\HttpException::fromPrevious($e, $status_code, $response_headers);
    }
}

$identifier = new SplHashIdentifier();

$information_provider = InformationProviderWithTransformation::fromDefaultData(
    $identifier,
    new CustomAuthenticationTo404Transformer() // Last argument is variadic
);
```

If you provide no [`ExceptionTransformers`](src/Information/ExceptionTransformer.php) every exception will be converted to a [`HttpException`](src/HttpException.php) with status code `500`. (unless it's already an instance of [`HttpException`](src/HttpException.php))

---

### The `ExceptionDisplayer`

[](#the-exceptiondisplayer)

An [`ExceptionDisplayer`](src/Displayer/ExceptionDisplayer.php) is responsible for displaying [`ExceptionInformation`](src/Information/ExceptionInformation.php).

An [`ExceptionDisplayer`](src/Displayer/ExceptionDisplayer.php) has one content-type that it supports.

The [`ProductionExceptionHandler`](src/ProductionErrorHandler.php) accepts one or more [`ExceptionDisplayers`](src/Displayer/ExceptionDisplayer.php)and will determine the best displayer for the current request.

This package comes with two default displayers that will be used as a fallback:

- [`FallbackHtmlDisplayer`](src/Displayer/FallbackHtmlDisplayer.php), for requests where the `Accept` header is `text/html`.
- [`FallbackJsonDisplayer`](src/Displayer/FallbackJsonDisplayer.php), for requests where the `Accept` header is `application/json`.

The best displayer for the combination of exception/request is determined by using [`DisplayerFilters`](src/DisplayerFilter/DisplayerFilter.php).

Out of the box this package comes with the following filters:

- [`Delegating`](src/DisplayerFilter/Delegating.php), delegates to other filters.
- [`CanDisplay`](src/DisplayerFilter/CanDisplay.php), filters based on the return value of `ExceptionDisplayer::canDisplay()`
- [`Verbosity`](src/DisplayerFilter/Verbosity.php), filters based on the return value of `ExceptionDisplayer::isVerbose()` and the verbosity level during the current request.
- [`ContentType`](src/DisplayerFilter/ContentType.php), filters based on the return value of `ExceptionDisplayer::supportedContentType()` and the `Accept` header of the current request. **! Important: This filter only performs very basic content negotiation.** Content-negotiation is out of scope for this package and should be performed in a middleware.

Full working example
--------------------

[](#full-working-example)

This is a working example of how you would instantiate the [`ProductionErrorHandler`](src/ProductionErrorHandler.php), preferably in your dependency-injection container.

```
use Psr\Log\LogLevel;
use Snicco\Component\Psr7ErrorHandler\DisplayerFilter\CanDisplay;
use Snicco\Component\Psr7ErrorHandler\DisplayerFilter\ContentType;
use Snicco\Component\Psr7ErrorHandler\DisplayerFilter\Delegating;
use Snicco\Component\Psr7ErrorHandler\DisplayerFilter\Verbosity;
use Snicco\Component\Psr7ErrorHandler\Identifier\SplHashIdentifier;
use Snicco\Component\Psr7ErrorHandler\Information\InformationProviderWithTransformation;
use Snicco\Component\Psr7ErrorHandler\Log\RequestAwareLogger;
use Snicco\Component\Psr7ErrorHandler\ProductionErrorHandler;

// Use any PSR-7 response factory
$psr_7_response_factory = new Nyholm\Psr7\Factory\Psr17Factory();

$request_aware_logger = new RequestAwareLogger(
    new Monolog\Logger(), // Use any PSR-3 logger
);

$information_provider = InformationProviderWithTransformation::fromDefaultData(
    new SplHashIdentifier()
);

$prefer_verbose = (bool) getenv('APP_DEBUG');

$displayer_filter = new Delegating(
    new ContentType(),
    new Verbosity($prefer_verbose),
    new CanDisplay(),
);

$error_handler = new ProductionErrorHandler(
    $psr_7_response_factory,
    $request_aware_logger,
    $information_provider,
    $displayer_filter,
    // Custom exception displayers go here (variadic)
)
```

Then use the instantiated error handler in a middleware like so:

```
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Server\MiddlewareInterface;
use Snicco\Component\Psr7ErrorHandler\HttpErrorHandler;

class ErrorHandlerMiddleware implements MiddlewareInterface {

    private HttpErrorHandler $error_handler;

    public function __construct(HttpErrorHandler $error_handler) {
        $this->error_handler = $error_handler;
    }

    public function process(ServerRequestInterface $request,RequestHandlerInterface $handler) : ResponseInterface{

        try {
            return $handler->handle($request);
        }catch (Throwable $e) {
            return $this->error_handler->handle($e, $request);
        }
    }

}
```

Exception utilities
-------------------

[](#exception-utilities)

### User-facing exceptions

[](#user-facing-exceptions)

This package comes with a [`UserFacing`](src/UserFacing.php) interface, which your custom exceptions can implement.

If an exception that implements [`UserFacing`](src/UserFacing.php) is thrown, the return values of `UserFacing::safeTitle()` and `UserFacing::safeMessage()` will be used to create the [`ExceptionInformation`](src/Information/ExceptionInformation.php), instead of the default HTTP error messages that might not make sense to your users.

**The original exception message will be logged** while your users get to see something they can relate (a little more) to.

### HTTP exceptions

[](#http-exceptions)

This packages comes with a generic [`HTTPException`](src/HttpException.php) class that you can throw in your HTTP related code (mostly middleware).

This allows you to dictate the HTTP response code and optionally additional response headers.

The difference between using the [`HTTPException`](src/HttpException.php) class and using an [`ExceptionTransformer`](src/Information/ExceptionTransformer.php) is that the latter is intended for your **domain exceptions** while [`HTTPExceptions`](src/HttpException.php) should be thrown only in HTTP related code (like middleware and Controllers).

Contributing
------------

[](#contributing)

This repository is a read-only split of the development repo of the [**Snicco** project](https://github.com/snicco/snicco).

[This is how you can contribute](https://github.com/snicco/snicco/blob/master/CONTRIBUTING.md).

Reporting issues and sending pull requests
------------------------------------------

[](#reporting-issues-and-sending-pull-requests)

Please report issues in the [**Snicco** monorepo](https://github.com/snicco/snicco/blob/master/CONTRIBUTING.md##using-the-issue-tracker).

Security
--------

[](#security)

If you discover a security vulnerability, please follow our [disclosure procedure](https://github.com/snicco/snicco/blob/master/SECURITY.md).

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance35

Infrequent updates — may be unmaintained

Popularity20

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity65

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~27 days

Recently: every ~1 days

Total

33

Last Release

609d ago

Major Versions

v1.10.0 → v2.0.0-beta.12024-09-01

v1.10.1 → v2.0.0-beta.72024-09-04

### Community

Maintainers

![](https://www.gravatar.com/avatar/d659a021c9077ceb60e6ab1ea4287b9f209edb97e93bceceb7fb539d8302e894?d=identicon)[calvinalkan](/maintainers/calvinalkan)

---

Top Contributors

[![snicco-bot](https://avatars.githubusercontent.com/u/101470239?v=4)](https://github.com/snicco-bot "snicco-bot (25 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/snicco-psr7-error-handler/health.svg)

```
[![Health](https://phpackages.com/badges/snicco-psr7-error-handler/health.svg)](https://phpackages.com/packages/snicco-psr7-error-handler)
```

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.8k18.5M1.6k](/packages/cakephp-cakephp)[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[aporat/store-receipt-validator

PHP receipt validator for Apple App Store and Amazon Appstore

6503.9M9](/packages/aporat-store-receipt-validator)[opensearch-project/opensearch-php

PHP Client for OpenSearch

15024.3M64](/packages/opensearch-project-opensearch-php)[phpro/http-tools

HTTP tools for developing more consistent HTTP implementations.

28137.8k](/packages/phpro-http-tools)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

595.2M386](/packages/shopware-core)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
