asispts/http-accept
A small PHP library for parsing HTTP Accept headers. It turns Accept, Accept-Language, Accept-Encoding and Accept-Charset values into usable objects, handles quality factors and wildcards, and helps you choose the best matching media type, language or encoding for a request.
asispts/http-accept is a small, dependency-free parser for the HTTP Accept* header family — Accept, Accept-Language, Accept-Encoding, Accept-Charset — returning preferences sorted by quality value. It is not tied to Laravel, so there is no service provider or facade to register; you use it as a plain library.
Install (note the rename). The v1.0.0 release renamed the Composer project to asispts/http-accept. If you installed the package under its pre-1.0 name, remove the old requirement and add the new one:
composer remove <old-package-name>
composer require asispts/http-accept
Where to look first. The src/ directory (look for the parser class and any value objects representing a media type plus its q value) and the tests/ directory — the test suite is the de-facto specification for how edge cases (wildcards, q=0, malformed input) are handled, which matters given the parser was refactored in this release.
First use case. Content negotiation for an API: read $request->header('Accept'), hand it to the parser, and pick the best supported representation. That is the shortest path from "installed" to "useful" and exercises the main code path.
Middleware-driven content negotiation. Parse the Accept header once in a middleware, attach the resolved negotiated type (and the parser result) to the request, and let controllers read it. This keeps negotiation logic out of every action and gives you a single place to define the supported-media-type map.
Language and encoding negotiation. The same parser shape applies to Accept-Language and Accept-Encoding. Use Accept-Language to pick a Locale for the request, and Accept-Encoding to decide whether to gzip a response. A common Laravel pattern is one NegotiateContent middleware that sets Content-Type, Content-Language, and Vary: Accept, Accept-Language.
Form requests / validation. When clients upload, inspect the Content-Type (a single media type, not a q-list). Do not confuse it with Accept — the parser handles the q-list form, so reach for it only on Accept* headers.
Testing against the parser. Inject the parser rather than statically calling it, and in tests feed handcrafted header strings (text/html;q=0.8, application/json;q=0.9, */*;q=0.1) to assert ordering. This is where the v1.0.0 parser refactor is most likely to surface a behavioral difference — re-run your negotiation tests after upgrading.
Caching. Header parsing is cheap but not free. If you negotiate on every request, cache the parsed result keyed by the raw header string per request (or in Octane/long-running workers, in a small per-process cache) — be careful not to cache across requests in a way that leaks a user's Accept header between workers.
Fallbacks. Always define a default representation when the parser returns nothing (missing header, all q=0, or unparseable input). A missing Accept header should mean "anything is acceptable," not "400".
The Composer rename is the breaking change in v1.0.0. Two things follow: anyone upgrading must edit composer.json (and any lockfile pins), and any internal documentation, CI scripts, or composer audit/composer outdated dashboards referencing the old name need updating. Plan for a lockfile refresh, not just a version bump.
Re-test edge cases after the parser refactor. The v1.0.0 notes call out "Refactor parser" with no behavioral detail. Refactors of this kind are usually safe but frequently change tie-breaking, whitespace tolerance, or how invalid q values are coerced. Your negotiation tests are the only reliable way to know — pin to ^1.0 and run the suite before deploying.
q=0 means "not acceptable", not "lowest priority". A media type with q=0 must be excluded from the candidate list. If your selection logic just sorts by q and takes the first entry, an explicit rejection can accidentally be selected. Verify the parser strips q=0 entries or filter them yourself.
Wildcards do not automatically match. */* and type/* need explicit handling in your matching layer — most parsers return the wildcard as a token, and it is your job to expand it against your supported types in the correct precedence order (exact match beats type/* beats */*).
Media types are case-insensitive; parameters are not. Normalize type/subtype case before comparing. Also be aware that parameters (charset, boundary) appear after ; and are not the same as the q weight — a naive explode(';') will treat both as parameters.
Vendor lock and maintenance. This is a single-maintainer package and v1.0.0 is its first stable release. Keep the usage surface narrow (a thin wrapper class in your app) so that swapping in a different Accept parser, or vendoring the logic, is a one-file change rather than a codebase-wide refactor.
Extension point. Whatever the parser returns, wrap it in your own small NegotiatedContent value object. That gives you a stable interface for controllers and tests and insulates you from any further return-type changes in the package's own API.
How can I help you explore Laravel packages today?