Skip to content

fix: do not let wildcards select values excluded with q=0 in Negotiate - #10661

Open
wleeaf wants to merge 1 commit into
codeigniter4:developfrom
wleeaf:negotiate-wildcard-excluded-values
Open

wleeaf wants to merge 1 commit into
codeigniter4:developfrom
wleeaf:negotiate-wildcard-excluded-values

Conversation

@wleeaf

@wleeaf wleeaf commented Oct 25, 2026

Copy link
Copy Markdown

Description
When the header holds a wildcard, Negotiate returns the first supported value, even if the same header excludes that value with q=0. In RFC 9110, a weight of 0 means "not acceptable" (section 12.4.2), and a more specific entry overrides a wildcard (sections 12.5.1 and 12.5.3). Examples:

  • Accept-Encoding: gzip;q=0, * with encoding(['gzip', 'br']) returns gzip.
  • Accept: application/json;q=0, */* with media(['application/json', 'text/html'], true) returns application/json.
  • Accept: text/*, text/html;q=0 with media(['text/html', 'text/plain'], true) returns text/html.
  • charset() behaves the same way, and so does language(), with and without $strictLocaleNegotiation. For example, Accept-Language: en;q=0, * gives en.

getBestMatch() and getBestLocaleMatch() now skip values that the header excludes when they match through a wildcard (*, */* or type/*). A new private isExcluded() helper does the check. An exact match with a positive weight is unaffected. When nothing matches, $strictMatch = false still falls back to the first supported value, as documented and as testBestMatchOnlyLowQuality expects.

testWildcardsSkipExcludedValues in tests/system/HTTP/NegotiateTest.php covers media(), charset(), encoding() and language(), including the strict locale negotiation path. Without the change it fails ('application/json' is returned instead of 'text/html'). The v4.7.6 changelog has a Bugs Fixed entry.

tests/system/HTTP/ passes. php-cs-fixer, PHPStan and Rector (dry run) report nothing for the changed files.

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

A wildcard returned the first supported value even when the header
excluded that value with q=0. For example, `Accept-Encoding: gzip;q=0, *`
selected gzip, `Accept: application/json;q=0, */*` selected
application/json, and `Accept: text/*, text/html;q=0` selected text/html.
The same applied to `charset()`, and to `language()` with and without
strict locale negotiation.

A wildcard now skips the supported values that the header excludes.
Without a match, the fallback to the first supported value when
`$strictMatch` is false is unchanged.
@carson-codeigniter4 carson-codeigniter4 Bot added the bug Verified issues on the current code behavior or pull requests that will fix them label Oct 25, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Verified issues on the current code behavior or pull requests that will fix them

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant