Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Negotiation Laravel Package

willdurand/negotiation

HTTP content negotiation library for PHP. Parses Accept* headers to match the best media type, language, charset, or encoding, with flexible matchers and prioritization. Handy for APIs and middleware to select response formats based on client preferences.

View on GitHub
Deep Wiki
Context7

Getting Started

Begin by installing the package via Composer:

composer require willdurand/negotiation

The primary entry points are RequestHeaderNegotiator, AcceptHeaderNegotiator, LanguageNegotiator, CharsetNegotiator, and EncodingNegotiator. Start with AcceptHeaderNegotiator to handle Accept headers for content-type negotiation:

use Negotiation\AcceptHeaderNegotiator;

$negotiator = new AcceptHeaderNegotiator();

// Server-supported formats (in order of preference)
$preferred = $negotiator->getBest('application/json', 'text/html');
// or
$preferred = $negotiator->getBest(['application/json', 'text/html']);

The getBest() method returns a HeaderInterface instance (e.g., AcceptHeader) representing the best match, or null if none found. Use getValue() to extract the matched type:

if ($preferred) {
    $contentType = $preferred->getValue(); // e.g., "application/json"
}

Check the README for usage examples and examples/ directory for quick demos.

Implementation Patterns

  • Multi-format API Responses: In a framework-agnostic controller, decide response format based on Accept:
$negotiator = new AcceptHeaderNegotiator();
$serverFormats = ['application/json', 'application/xml', 'text/html'];
$best = $negotiator->getBest($serverFormats);

if (!$best) {
    return new Response('Not Acceptable', 406);
}

$response = match ($best->getValue()) {
    'application/json' => $this->renderJson($data),
    'application/xml'  => $this->renderXml($data),
    default            => $this->renderHtml($data),
};
  • Language Negotiation: For multilingual APIs or localized content:
$languageNegotiator = new LanguageNegotiator();
$locales = ['en-US', 'fr-FR', 'es-ES'];
$bestLanguage = $languageNegotiator->getBest($locales);

if ($bestLanguage) {
    $locale = $bestLanguage->getValue(); // e.g., "fr-FR"
    // Set locale, fetch translated content, etc.
}
  • Integrate with PSR-7/15 Middleware: Use in a middleware to set response headers automatically:
$app->add(function ($request, $handler) {
    $negotiator = new AcceptHeaderNegotiator();
    $supported = ['application/json', 'text/html'];
    $best = $negotiator->getBest(...$supported);

    $response = $handler->handle($request);
    if ($best) {
        $response = $response->withHeader('Content-Type', $best->getValue());
    }
    return $response;
});
  • Fallback Strategy: Combine with a default + wildcards for graceful degradation:
$serverFormats = ['application/json', 'text/html', '*/*'];
$best = $negotiator->getBest(...$serverFormats);

Use getBestMatch() instead of getBest() when you want the full HeaderInterface, including q value for logging or debugging.

Gotchas and Tips

  • Q-value Ignoring: getBest() ignores q=0 (explicit reject). If a client sends Accept: text/html;q=0, application/json, application/json will be selected — be sure your defaults align with expectations.

  • Wildcard Matching is Conservative: */* matches any, but text/* won’t match application/json. Prefer explicit types for security-sensitive or format-critical endpoints.

  • Case Sensitivity: MIME types and languages are not normalized — Text/HTML and text/html may not match as expected. Normalize input before negotiation if required.

  • Null handling: getBest() returns null on no match. Always check for null before calling getValue() — return 406 Not Acceptable when no match.

  • Testing: The library is easily mockable — inject AcceptHeaderNegotiatorInterface in production and use MockObject in tests to assert negotiation behavior.

  • Version Stability: Last release in 2022, but stable. No active development — suitable for stable APIs. Check for forks (e.g.,symfony/serializer may use it internally) for newer forks or alternatives.

  • Framework Integration: While framework-agnostic, many frameworks (e.g., Symfony via Negotiation component or Laravel via Accept middleware packages) abstract this — evaluate if reinvention is needed.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport