juststeveking/http-status-code
PHP package that turns HTTP status codes into a typed enum, with helpers for messages, code categories and Laravel validation. Replace magic numbers with descriptive cases for clearer, safer API and controller responses—no more guessing what 418 means.
Architecture fit. The package is a single backed enum — no service provider, no facade, no config file, no container binding, no middleware. Architecturally it is not a component you integrate; it is a vocabulary you adopt. That makes it a near-zero-risk fit for Laravel on PHP 8.1+, but it also means it occupies no architectural slot. The honest framing for a TPM: Laravel and Symfony already ship Response::HTTP_* constants via symfony/http-foundation, so the architectural question is not "does this fit?" but "which status-code convention wins project-wide?" This package is a credible forcing function for that decision, not a load-bearing dependency. Mixing it with framework constants is worse than either choice alone, so the deliverable is a convention, and the package is the artifact that makes the convention greppable and enforceable.
Integration feasibility. Extremely high. composer require juststeveking/http-status-code, constrained to ^3.0 (the enum line — ignore older write-ups recommending ^1.0). The entire public API fits on one screen. Three patterns carry the value: enum cases at the response boundary in controllers; a statusMap array in the exception handler mapping domain exceptions to cases; and a status(): HttpStatusCode method on domain exceptions so they are self-describing and unit-testable without booting the framework. Estimated effort for a pilot service is days, not weeks, and the whole decision is reversible in an afternoon.
Technical risk. Runtime risk is negligible — HTTP status codes are a frozen spec and the surface area is ~10 lines. The real risks are organizational and ergonomic:
response()->json($data, HttpStatusCode::OK) type-errors; HttpStatusCode::OK === 200 is silently false. tryFrom() returns ?self and must be branched explicitly; from() throws ValueError and turns bad input into a 500. Mitigate with PHPStan/Larastan at a meaningful level, not with process.Retry-After companions) require wrapping — at which point you own a class anyway.lang files and treat ->name as developer-facing only.Key questions to resolve before committing.
Stack fit. Laravel 9/10/11 on PHP 8.1+, Lumen, or plain PHP. Zero wiring: no publish step, no service provider registration, no config. In Laravel 10 the exception mapping lives in app/Exceptions/Handler.php; in Laravel 11 it moves into bootstrap/app.php via withExceptions. Both are compatible; the sequencing below accounts for the split.
Migration path. Treat it as a convention migration, not a dependency install:
Response::HTTP_* explicitly, with the reversal cost documented. A PM outcome of "no action — we already use framework constants consistently" is valid and should be recorded.->value and case-vs-int comparisons. Without this, the migration generates a new class of review comments.response()->json($user, HttpStatusCode::CREATED->value)), since that is the highest-visibility, lowest-coupling change.ifs with a statusMap array; add status(): HttpStatusCode to domain exceptions so mappings are testable without the framework.assertHttpStatus(HttpStatusCode $code) on TestResponse keeps call sites readable at suite scale; at minimum assert with HttpStatusCode::CREATED->value so failures print meaningfully.Compatibility. Watch three edges: the PHP floor (8.1+ for backed enums; match the tag to the runtime, not the reverse); name drift across ecosystems (this package follows IANA wording — UNPROCESSABLE_ENTITY, IM_A_TEAPOT — while Symfony uses HTTP_UNPROCESSABLE_ENTITY and Guzzle has its own set, so let IDE completion pick names rather than typing from memory); and JSON encoding, where backed enums serialize to their value in PHP 8.1+, usually desirable but occasionally a surprise when an enum leaks into a log payload or fixture.
Sequencing across the portfolio. Do the pilot → measure → expand. Do not attempt a fleet-wide sweep before the convention is enforced by static analysis and encoded in review. If more than one team will touch status codes, sequence the ADR and the static-analysis rule ahead of any code change; otherwise you get partial adoption, which is strictly worse than either uniform choice.
Maintenance. Low and predictable. The enum tracks a frozen spec; treat only major bumps (1.x → 2.x → 3.x) as upgrade events worth reviewing. The 3.0.1 → 3.0.2 patch required no code or test changes. Budget approximately nothing for routine upkeep, and pre-agree the exit ramp: if the dependency becomes inconvenient, inline the enum under MIT — roughly 50 lines — in an afternoon. That pre-agreed exit is what makes the low-activity signal tolerable.
Support. Community-only. Single maintainer, no vendor SLA, no security response commitment. For a constants list this is acceptable, but it must be stated plainly in any architecture review rather than glossed. If your org runs meaningful dependency attestation, the governance overhead likely exceeds the value delivered, and inlining is the rational choice.
Scaling. Cost is flat regardless of team or service count — that is the appeal. The scaling risk is social, not technical: convention drift. Partial adoption where some controllers use the enum and others use Response::HTTP_* produces the worst of both worlds — two vocabularies, neither authoritative, and reviewers unable to reason about consistency. Guard against this with a single enforced convention and a lint rule, not with documentation alone.
Failure modes to instrument.
->value — hard type error at the response constructor; caught by PHPStan, not by tests that never hit the path.from() on untrusted input — ValueError becomes a 500 where a 400 or a rejected payload was intended. Standardize on tryFrom() and decide explicitly whether null falls back to INTERNAL_SERVER_ERROR or rejects.tryFrom() null fall-through — a webhook sending "status": "abc" or 999 silently yields null; if nothing branches on it, the response is undefined.=== comparison — HttpStatusCode::OK === 200 is always false; test assertions quietly pass or fail for the wrong reason.Ramp-up. Effectively zero. One screen of README is the whole API; a developer is productive in under an hour. The measurable benefit is review time no longer spent decoding integers (UNPROCESSABLE_ENTITY vs 422) and static analysis having something to check. Treat that as a real but small productivity signal — sufficient to justify convention work inside an API-standardization or SDK epic, insufficient to justify headcount or a roadmap slot.
Roadmap placement. Enabler, not a line item. With 49 stars, 0 dependents, an opportunity score around 34.5, and a 3.0.2 patch that is maintenance activity rather than differentiation, this should not move a roadmap. Delegate to engineering as a developer-convenience choice within an API-conventions effort — no vendor review, no budget line — precisely because the decision is reversible in an afternoon and therefore should not consume planning cycles.
How can I help you explore Laravel packages today?