PHPackages                             waffle-commons/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. [Framework](/categories/framework)
4. /
5. waffle-commons/error-handler

ActiveLibrary[Framework](/categories/framework)

waffle-commons/error-handler
============================

Error-Handler component for Waffle framework.

0.1.0-beta4(2w ago)1321MITPHPPHP ^8.5CI passing

Since Jan 5Pushed 3w agoCompare

[ Source](https://github.com/waffle-commons/error-handler)[ Packagist](https://packagist.org/packages/waffle-commons/error-handler)[ RSS](/packages/waffle-commons-error-handler/feed)WikiDiscussions main Synced today

READMEChangelog (8)Dependencies (52)Versions (26)Used By (1)

[![Discord](https://camo.githubusercontent.com/b30f41baece56d71f7f496f7e39fd33a2a096221c66c648b350dd4fe14276c2e/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f3735353238383030313539323033333339313f6c6f676f3d646973636f7264)](https://discord.gg/eKgywnfXr2)[![PHP Version Require](https://camo.githubusercontent.com/e3cbdadc6d6b1092605c1cbd182b41dddd0d9defc287695bd031f9084f28186d/687474703a2f2f706f7365722e707567782e6f72672f776166666c652d636f6d6d6f6e732f6572726f722d68616e646c65722f726571756972652f706870)](https://packagist.org/packages/waffle-commons/error-handler)[![PHP CI](https://github.com/waffle-commons/error-handler/actions/workflows/main.yml/badge.svg)](https://github.com/waffle-commons/error-handler/actions/workflows/main.yml)[![codecov](https://camo.githubusercontent.com/93c5eeb935ff181dc0783e96446b84b53f5b60138f7de90236e7039d1d676744/68747470733a2f2f636f6465636f762e696f2f67682f776166666c652d636f6d6d6f6e732f6572726f722d68616e646c65722f67726170682f62616467652e7376673f746f6b656e3d64373461633632612d373837322d343033352d386238622d626363336166313939316530)](https://codecov.io/gh/waffle-commons/error-handler)[![Latest Stable Version](https://camo.githubusercontent.com/222a3034082b7d06262a201bfe063ab8fe3594f7e096c8fdada09051183ff6c2/687474703a2f2f706f7365722e707567782e6f72672f776166666c652d636f6d6d6f6e732f6572726f722d68616e646c65722f76)](https://packagist.org/packages/waffle-commons/error-handler)[![Latest Unstable Version](https://camo.githubusercontent.com/a89151b25902b58f67bd4a7e22b49354a5672c686bcadb6eda7e371adfb99290/687474703a2f2f706f7365722e707567782e6f72672f776166666c652d636f6d6d6f6e732f6572726f722d68616e646c65722f762f756e737461626c65)](https://packagist.org/packages/waffle-commons/error-handler)[![Total Downloads](https://camo.githubusercontent.com/20a7ff91a66d439445ea3926bd70c90acc88e02edf19984375a31a1ba4a75af6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f776166666c652d636f6d6d6f6e732f6572726f722d68616e646c65722e737667)](https://packagist.org/packages/waffle-commons/error-handler)[![Packagist License](https://camo.githubusercontent.com/802815694da6c61957461bf575f2c89a4ad5662ed36cce67d654b15c80ff3afe/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f776166666c652d636f6d6d6f6e732f6572726f722d68616e646c6572)](https://github.com/waffle-commons/error-handler/blob/main/LICENSE.md)

Waffle Error Handler Component
==============================

[](#waffle-error-handler-component)

> **Release:** `0.1.0-beta4` | [`CHANGELOG.md`](./CHANGELOG.md)**PSR Compliance:** PSR-15 (middleware), PSR-3 (logging), RFC 7807 (`application/problem+json`), RFC 7231 (`Allow` header on `405`)

The outermost middleware in every Waffle pipeline. Catches `Throwable` thrown deeper in the stack, logs it via the injected PSR-3 logger, and renders an RFC 7807 "Problem Details" JSON response.

📦 Installation
--------------

[](#-installation)

```
composer require waffle-commons/error-handler
```

🧱 Surface
---------

[](#-surface)

ClassRole`Waffle\Commons\ErrorHandler\Middleware\ErrorHandlerMiddleware`PSR-15 middleware. Wraps `$handler->handle()` in `try/catch(Throwable)`, logs, then delegates to the renderer.`Waffle\Commons\ErrorHandler\Renderer\JsonErrorRenderer``final readonly` renderer implementing `ErrorRendererInterface`. Produces RFC 7807 JSON.🚀 Wiring it up
--------------

[](#-wiring-it-up)

```
use Waffle\Commons\ErrorHandler\Middleware\ErrorHandlerMiddleware;
use Waffle\Commons\ErrorHandler\Renderer\JsonErrorRenderer;
use Waffle\Commons\Http\Factory\ResponseFactory;
use Waffle\Commons\Log\StreamLogger;

$renderer = new JsonErrorRenderer(
    responseFactory: new ResponseFactory(),
    debug: $appDebug, // false in production
);

$stack->prepend(new ErrorHandlerMiddleware($renderer, new StreamLogger()));
```

📦 RFC 7807 payload
------------------

[](#-rfc-7807-payload)

`JsonErrorRenderer::render(Throwable $e, ServerRequestInterface $request)` always emits the canonical RFC 7807 shape, encoded with `JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES`:

```
{
  "type":     "about:blank",
  "title":    "Bad Request",
  "status":   400,
  "detail":   "Untrusted Host \"evil.example\".",
  "instance": "/login"
}
```

Extensions added by Waffle:

- If the exception implements `Waffle\Commons\Contracts\Exception\Validation\ValidationExceptionInterface` and `getField()` returns non-null, a `field` key is added to the payload (RFC-011).
- If `$debug = true`, additional `trace`, `file`, `line` keys are added.
- In production (`$debug = false`), any 5xx `detail` is masked to `"An internal server error occurred."` to avoid leaking implementation details.

🧩 Status-code resolution
------------------------

[](#-status-code-resolution)

`JsonErrorRenderer::determineStatusCode(Throwable $e)` walks well-known exception interfaces (e.g. `ValidationExceptionInterface` → 422, `RouteNotFoundExceptionInterface` → 404, `MethodNotAllowedExceptionInterface` → 405, `\InvalidArgumentException` → 400) and falls back to `500` for unknown throwables. The matching is interface-based — your application exceptions can opt in by implementing the right contract interface. For a `MethodNotAllowedExceptionInterface`, the renderer also emits an RFC 7231 `Allow` header (e.g. `Allow: GET, HEAD, OPTIONS, POST`).

🐘 PHP 8.5 features used
-----------------------

[](#-php-85-features-used)

- `final readonly class JsonErrorRenderer` — the renderer holds an injected `ResponseFactoryInterface` and a `bool $debug` flag, both `readonly`.
- Strict-typed constructor + return types.
- `JSON_THROW_ON_ERROR` for fail-fast encoding.

🧭 Architectural boundary (`mago guard`)
---------------------------------------

[](#-architectural-boundary-mago-guard)

An active dependency **perimeter** is enforced on every CI run by `vendor/bin/mago guard` (bundled into `composer mago`; zero baselines). The rules live in [`mago.toml`](./mago.toml) under `[guard.perimeter]` — a forbidden `use` statement fails the build, not a reviewer.

Production code under `Waffle\Commons\ErrorHandler` may depend **only** on:

- `Waffle\Commons\ErrorHandler\**` — itself
- `Waffle\Commons\Contracts\**` — the shared contracts package, the **only** Waffle dependency permitted
- `Psr\**` — PSR interfaces (PSR-7 / PSR-15 / PSR-17)
- `@global` + `Psl\**` — PHP core and the PHP Standard Library

Test code under `WaffleTests\Commons\ErrorHandler` is unrestricted (`@all`). Structural rules are guarded too: interfaces must be named `*Interface`, `Exception\**` classes must end in `*Exception`, and any `Enum\**` namespace may hold only `enum` declarations.

Contract-first, component-agnostic by construction: components compose through `waffle-commons/contracts`, never directly through one another.

🧪 Testing
---------

[](#-testing)

```
docker exec -w /waffle-commons/error-handler waffle-dev composer tests
```

📄 License
---------

[](#-license)

MIT — see [LICENSE.md](./LICENSE.md).

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance95

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 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 ~22 days

Recently: every ~5 days

Total

8

Last Release

20d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/34a7557a3fb23aaf788ca3892b9b7efdf96e753264bafd0599153c9e8a921316?d=identicon)[LesliePetrimaux](/maintainers/LesliePetrimaux)

---

Top Contributors

[![supa-chayajin](https://avatars.githubusercontent.com/u/695448?v=4)](https://github.com/supa-chayajin "supa-chayajin (52 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/waffle-commons-error-handler/health.svg)

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

###  Alternatives

[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k15](/packages/tempest-framework)[symfony/symfony

The Symfony PHP framework

31.4k87.2M2.2k](/packages/symfony-symfony)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

85036.3k](/packages/flow-php-flow)[cakephp/cakephp

The CakePHP framework

8.9k19.5M1.8k](/packages/cakephp-cakephp)[guzzlehttp/psr7

PSR-7 message implementation that also provides common utility methods

8.0k1.1B4.0k](/packages/guzzlehttp-psr7)[shopware/platform

The Shopware e-commerce core

3.4k1.5M3](/packages/shopware-platform)

PHPackages © 2026

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