Skip to content

Commit

Permalink
Merge pull request #115 from xp-forge/feature/qvalues
Browse files Browse the repository at this point in the history
Add Headers::qfactors() to implement content negotiation
  • Loading branch information
thekid authored May 26, 2024
2 parents 81e7160 + 6668f76 commit 10249e3
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 7 deletions.
36 changes: 35 additions & 1 deletion src/main/php/web/Headers.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*
* @see https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
* @see https://en.wikipedia.org/wiki/Content_negotiation
* @test xp://web.unittest.HeadersTest
* @test web.unittest.HeadersTest
*/
abstract class Headers {

Expand Down Expand Up @@ -95,6 +95,40 @@ protected function next($input, &$offset) {
};
}

/**
* Returns a new parser for quality-factor headers, e.g.:
*
* `Accept-Encoding: deflate, gzip;q=1.0, *;q=0.5`
*
* @return self
*/
public static function qfactors() {
return new class() extends Headers {
protected function next($input, &$offset) {
$weighted= [];
$q= 1.0;
do {
$s= strcspn($input, ',;', $offset);
$value= ltrim(substr($input, $offset, $s), ' ');
$offset+= $s + 1;

$c= $input[$offset - 1] ?? null;
if (';' === $c) {
$weighted[$value]= (float)(Headers::pairs()->next($input, $offset)['q'] ?? $q);
$c= $input[$offset - 1] ?? null;
} else {
$weighted[$value]= $q;
}

$q-= 0.0001;
} while ($c);

arsort($weighted, SORT_NUMERIC);
return $weighted;
}
};
}

/**
* Returns a new parser for headers with key/value pairs
*
Expand Down
16 changes: 10 additions & 6 deletions src/test/php/web/unittest/HeadersTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,16 @@ public function refresh($header) {
#[Test]
public function accept() {
Assert::equals(
[
new Parameterized('text/html', []),
new Parameterized('application/json', ['q' => '0.9']),
new Parameterized('*/*', ['q' => '0.8']),
],
Headers::values(Headers::parameterized())->parse('text/html, application/json;q=0.9, */*;q=0.8')
['text/html' => 1.0, 'application/json' => 0.9, '*/*' => 0.8],
Headers::qfactors()->parse('text/html, application/json;q=0.9, */*;q=0.8')
);
}

#[Test]
public function accept_encoding() {
Assert::equals(
['deflate' => 1.0, 'gzip' => 1.0, '*' => 0.5],
Headers::qfactors()->parse('deflate, gzip;q=1.0, *;q=0.5')
);
}

Expand Down

0 comments on commit 10249e3

Please sign in to comment.