franzl/whoops-middleware
PSR-15 middleware that drops the Whoops error handler into any middleware pipeline, turning uncaught exceptions into rich, stack-trace-friendly debug pages during development while staying out of the way in production.
What changed in 2.0.0 (and what did not)
2.0.0 is a maintenance release, not a capability release. Three changes matter: PHP 8 support added, PHP <7.2 dropped, and the hard dependency on a Laminas PSR-7 implementation replaced with middleware/utils runtime discovery. No new features, no API changes to the middleware itself, no change to the security posture. The two structural objections from the prior assessment — this is not a Laravel package and it is redundant with first-party error renderers — are untouched. The prior score (0.44 / opportunity 47.86) was not re-measured; treat it as a stale reference point, not a current signal.
Architecture fit
Unchanged. Still a thin PSR-15 MiddlewareInterface adapter over filp/whoops, still framework-agnostic, still requiring a bridge to participate in Laravel's handle($request, Closure $next) kernel pipeline. The PSR-7 decoupling modestly improves fit in mixed-dependency environments — previously the package imposed Laminas on adopters, which was a real conflict vector in a Slim/Mezzio app already using Nyholm or Diactoros. That frictions less now. It does not change the fundamental ratio: for the Laravel stack in scope, this is meaningful glue in service of a feature spatie/laravel-ignition already provides.
The redundancy argument is now the only load-bearing objection, which is worth stating plainly. If the target runtime is PSR-15-native and lacks a debug renderer, 2.0.0 is a cleaner drop-in than 1.x was.
Integration feasibility
Improved but with a new wrinkle. Removing the forced Laminas dependency shrinks the install footprint and eliminates a class of version-constraint collisions. The cost is a new transitive dependency (middleware/utils) whose own maintenance posture must be vetted independently — we have traded a known-heavy dependency for an unknown-light one.
The wrinkle: runtime PSR-7 discovery introduces non-determinism. "Pick an available PSR-7 implementation" is benign in a single-implementation app and ambiguous in one that ships several (common in Laravel via Symfony's PSR-7 bridge plus a standalone implementation). If discovery selects an unexpected factory, failure surfaces as odd request/response behavior or a type error inside the error path — exactly where you least want a diagnostic puzzle. Verify which implementation is selected in the actual target app before piloting, and pin the resulting behavior.
Breaking change profile is benign: dropping PHP <7.2 is a non-event for any supported runtime, and 2.0.0 is a major so the pin is explicit. Nothing here requires code changes in an existing 1.x integration unless it depended on Laminas being transitively present.
Technical risk
Key questions
middleware/utils? We are inheriting it.middleware/utils stops tracking our PHP minor?Stack fit
Unchanged verdict, slightly better mechanics. Good in Slim 4 / Mezzio / Laminas / custom PSR-15 pipelines — and now less intrusive there thanks to the PSR-7 decoupling. Poor-to-neutral in Laravel, where it remains a bolt-on competing with a first-party solution. Scope adoption to non-Laravel PSR-15 services, or declare the Laravel case out of scope.
Migration path
For greenfield PSR-15 adoption, additive and unchanged: register as a near-outermost layer behind a strict debug === true && environment !== 'production' gate. For teams on 1.x, upgrading to 2.0.0 is low-risk but should be paired with an explicit check that the Laminas implementation is still resolvable where expected — if anything in the app depended on it being present transitively, that dependency is now implicit and unsatisfied.
For Laravel, the path is unchanged and still not worth it: PSR-15 bridge → kernel adaptation → verify exception fidelity. Laravel's Handler renders before the middleware sees many exceptions, which can render the middleware inert. Validate that ordering assumption first; it remains the most likely silent failure.
Compatibility
Pin exact versions — 2.0.0 exactly, not ^2.0. Verify PHP minor support empirically rather than from release notes. Verify the discovery library resolves the implementation you expect in each target app. If a bridge library is in play, check its maintenance status independently; bridging to an abandoned bridge compounds the risk and 2.0.0 does nothing to help that.
Sequencing
middleware/utils — maintenance status, advisory history, pinning strategy.display_errors off, log redaction.Maintenance
Still low effort, still non-zero, still vendored-code discipline from day one. The 2.0.0 release resets the abandonment clock but does not establish a cadence — one release after a long gap is not a maintenance commitment. Expect periodic Whoops advisories, PHP minor checks, and the same standing "is this still alive?" question each cycle. The prior 18–24 month fork-or-vendor expectation is unchanged; if anything, a maintainer resurfacing once makes the next dormancy harder to predict.
Support
No vendor, no SLA, single maintainer, no dependents to learn from. Internal ownership sits with the pipeline team. On-call unfamiliarity is unchanged. The one-page runbook still applies — add a line on PSR-7 discovery so the next person knows which implementation should be selected and what to check when it isn't.
Scaling
Runtime cost remains negligible (error paths only). The organizational scaling risk is unchanged: N services × one lightly-maintained dependency each, each needing its own gate audit and its own discovery-behavior verification after this release.
Failure modes
middleware/utils both have to track PHP majors. Vendoring plan remains the mitigation.Ramp-up
Under an hour for a PSR-15-fluent PHP developer; the learning is pipeline ordering and security gating discipline, not the package. The prior conclusion for Laravel-only teams still holds and is the recommendation: do not adopt — ramp-up is effectively the cost of learning a bridge library for a feature Ignition already provides. For PSR-15-native services with no first-party renderer, 2.0.0 makes a scoped pilot marginally more attractive than it was: smaller dependency footprint, current PHP support claimed. Run the compatibility spike before committing either way.
How can I help you explore Laravel packages today?