Search by

chubbyphp / chubbyphp-negotiation

dominikzogg

Chubbyphp Negotiation

Package info

github.com/chubbyphp/chubbyphp-negotiation

pkg:composer/chubbyphp/chubbyphp-negotiation

Statistics

Installs: 102 363

Dependents: 2

Suggesters: 0

Stars: 2

Open Issues: 0

2.3.4 2026-09-08 19:57 UTC

README

CI Coverage Status Mutation testing badge Latest Stable Version Total Downloads Monthly Downloads

bugs code_smells coverage duplicated_lines_density ncloc sqale_rating alert_status reliability_rating security_rating sqale_index vulnerabilities

Description

A small, dependency-light content negotiation library for PSR-7 requests.

It picks the best match between what the client asks for and what your application supports:

  • Accept → which media type to respond with
  • Accept-Language → which locale to respond in
  • Content-Type → whether the request body can be parsed

Each negotiator returns a NegotiatedValue (the matched value plus its header attributes such as q or charset), or null when nothing matches. Optional PSR-15 middlewares turn a failed negotiation into a 406 Not Acceptable or 415 Unsupported Media Type response and expose the negotiated value as a request attribute.

Requirements

  • php: ^8.3
  • psr/http-message: ^1.1|^2.0

Suggest

  • chubbyphp/chubbyphp-container: ^2.5.2 (for NegotiationServiceFactory)
  • chubbyphp/chubbyphp-http-exception: ^1.3.4 (required by the middlewares)
  • chubbyphp/chubbyphp-laminas-config-factory: ^1.5.3 (for the laminas-style ServiceFactory classes)
  • pimple/pimple: ^3.6.2 (for NegotiationServiceProvider)
  • psr/http-server-middleware: ^1.0.2 (required by the middlewares)

Installation

Through Composer as chubbyphp/chubbyphp-negotiation.

composer require chubbyphp/chubbyphp-negotiation "^2.3"

Usage

All negotiators share the same contract: pass the supported values to the constructor, call negotiate($request) and receive a NegotiatedValueInterface (value + header attributes) or null when nothing matches. The middlewares wrap a negotiator, store the result as a request attribute and throw an HttpException on failure.

Each section below shows the minimal call; the linked page documents the matching rules, edge cases and error data.

AcceptLanguageNegotiator

Negotiates Accept-Language. Exact locale first, then the language of a regional locale (en-USen), then *. Full documentation

$negotiator = new AcceptLanguageNegotiator(['en', 'de']);

$value = $negotiator->negotiate($request); // 'Accept-Language: de,en-US;q=0.7,en;q=0.3'
$value->getValue();                        // 'de'
$value->getAttributes();                   // ['q' => '1.0']

AcceptLanguageMiddleware

Stores the negotiated locale in the request attribute acceptLanguage, or throws 406 Not Acceptable. Full documentation

$middleware = new AcceptLanguageMiddleware(new AcceptLanguageNegotiator(['en', 'de']));

$response = $middleware->process($request, $handler); // $request->getAttribute('acceptLanguage') inside $handler

AcceptNegotiator

Negotiates Accept. Exact media type first, then structured suffix (+json), then type/*, then */*. Full documentation

$negotiator = new AcceptNegotiator(['application/json', 'application/xml', 'application/x-yaml']);

$value = $negotiator->negotiate($request); // 'Accept: text/html,application/xml;q=0.9,*/*;q=0.8'
$value->getValue();                        // 'application/xml'
$value->getAttributes();                   // ['q' => '0.9']

AcceptMiddleware

Stores the negotiated media type in the request attribute accept, or throws 406 Not Acceptable. Full documentation

$middleware = new AcceptMiddleware(new AcceptNegotiator(['application/json', 'application/xml']));

$response = $middleware->process($request, $handler); // $request->getAttribute('accept') inside $handler

ContentTypeNegotiator

Negotiates Content-Type. Exact media type first, then structured suffix (application/vnd.api+jsonapplication/json). Header parameters such as charset are returned as attributes. Full documentation

$negotiator = new ContentTypeNegotiator(['application/json', 'application/xml', 'application/x-yaml']);

$value = $negotiator->negotiate($request); // 'Content-Type: application/xml; charset=UTF-8'
$value->getValue();                        // 'application/xml'
$value->getAttributes();                   // ['charset' => 'UTF-8']

ContentTypeMiddleware

Stores the negotiated media type in the request attribute contentType, or throws 415 Unsupported Media Type. Full documentation

$middleware = new ContentTypeMiddleware(new ContentTypeNegotiator(['application/json', 'application/xml']));

$response = $middleware->process($request, $handler); // $request->getAttribute('contentType') inside $handler

NegotiationServiceFactory

Registers all negotiators and middlewares in a chubbyphp/chubbyphp-container under negotiator.* ids. The supported values are read from negotiator.*.values services, which default to []. Full documentation

$container = new Container();
$container->factories((new NegotiationServiceFactory())());
$container->factory('negotiator.acceptNegotiator.values', static fn (): array => ['application/json']);

$container->get('negotiator.acceptMiddleware')->process($request, $handler);

NegotiationServiceProvider

Registers the same services in a Pimple container, using the same service ids. Full documentation

$container = new Container();
$container->register(new NegotiationServiceProvider());
$container['negotiator.acceptNegotiator.values'] = ['application/json'];

$container['negotiator.acceptMiddleware']->process($request, $handler);

ServiceFactory

Invokable factories built on chubbyphp/chubbyphp-laminas-config-factory for laminas-servicemanager style containers. Each factory can be used unnamed or with a name ([Factory::class, 'name']) to register several independent instances.

Copyright

2026 Dominik Zogg