Repository navigation
Add a predicate-based throwable filtering adapter #5
Description
Delivered status
This opt-in 2.x feature is complete. Owner coisa merged PR68 at a709ec85b679e46bc422e7acd63654764dcfa633; that PR natively closed this issue. Handler\FilteringThrowableHandler shipped in v2.1.0 and remains present in current master/v2.4.1.
The implementation preserves PHP 7.4, the typed constructor, legacy integration and default discovery. The explicit boolean predicate controls delivery to its delegate; predicate/delegate failures propagate. Focused unit tests, full PHPDoc, the executable filtering recipe, guides/site and consumer skill were delivered together. PR86 now exposes the complete source-checked recipe.
The following investigation records the original alternatives and acceptance questions. Its proposal wording and unchecked historical checklist do not mean this closed feature awaits another implementation.
Historical investigation
Summary & intent
Investigate a small predicate decorator that forwards selected throwables to a reporter. It should make reusable routing explicit while preserving the existing PHP suppression, HTTP rendering and lifecycle contracts.
Status: potential opt-in 2.x minor after compatibility-preserving tooling/CI. Keep PHP 7.4 syntax/resolution and the legacy container/ConfigProvider/defaults; no filtering default is introduced. See the staged roadmap.
Findings & constraints
| Observed fact | Design consequence |
|---|---|
| CallableThrowableHandler already supports application-defined filtering | Keep one-off cases simple; justify a reusable decorator through composition |
PHP-error conversion already honors error_reporting() and @ |
Filter already-created throwables; never rewrite global error masks |
| The proposed major reports before pure rendering; 2.x derives a response body from output capture | Do not assume that future separation exists: filter private diagnostic branches and verify the legacy HTTP composition explicitly |
ErrorException::getSeverity() carries PHP severity |
Do not treat arbitrary exception codes as severity or HTTP status |
Alternatives & trade-offs
| Alternative | Complexity | Breaking risk | Maintenance cost | Assessment |
|---|---|---|---|---|
| Application callable only | Low | None | Repeated local predicates | Valid status quo |
| A small predicate decorator | Low | Additive | One focused adapter | Recommended investigation |
| Routing through existing event listeners | Medium | None | Requires event composition | Useful when routing is already event-based |
| General rules engine/configuration DSL | High | Broad policy surface | Substantial | Outside this issue; no demonstrated need |
Proposed strategy
Evaluate FilteringThrowableHandler with a ThrowableHandlerInterface delegate and callable(Throwable): bool predicate. A match delivers the same object once; a nonmatch returns normally. Predicate and delegate failures propagate; neither causes implicit retry.
Keep the decorator outside the default aggregate and preserve handleThrowable(): void. Applications supply the predicate explicitly; autowiring must not invent one. Constructor order and final naming should follow the library's composition conventions.
This is intentional diagnostic filtering, not exception recovery. A nonmatch does not mean the application operation succeeded, and HTTP rendering still handles the original downstream failure.
Isolated investigation & validation
In a disposable branch/worktree, compose the decorator with a recording reporter and the existing middleware. Use synthetic ordinary exceptions and ErrorException severities. Check that the predicate runs once, matches preserve identity/order, nonmatches skip only the delegate, and existing callback restoration remains unchanged. Preserve the known 2.x lifecycle behavior; this decorator does not add the major's guard.
No process-wide masks, real production errors or global suppression configuration should be changed. If the decorator does not simplify composition, retain a tested callable example and discard the isolated implementation.
Guiding questions
- Is
truealways “forward”, and is that obvious in the chosen name? - Should the predicate accept only Throwable, with richer routing kept application-owned?
- Which examples make intentional suppression clear without encouraging broad loss of diagnostics?
Acceptance criteria
- Freeze predicate semantics, constructor order and naming with concise PHPDoc.
- Cover match/nonmatch, predicate failure, delegate failure, original-object identity, one invocation and relevant lifecycle/recursion boundaries.
- Prove diagnostic filtering leaves HTTP response selection and PHP suppression semantics intact.
- Add a runnable example and update English README, API/provider/cookbook/static-site docs, relevant migration guidance and
coisa-error-handlerskill in the same PR. - Include a Changeset; pass the retained 2.x runtime range including the PHP 7.4 minimum, supported dependency bounds,
composer --no-plugins check, source parity and production autoload/archive validation. - Keep configuration and registration explicit; introduce no rule engine or automatic filtering policy.
Related proposals: direct PSR-3, best effort.
Activity