Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
100.00% |
23 / 23 |
|
100.00% |
4 / 4 |
CRAP | |
100.00% |
1 / 1 |
| QualityValue | |
100.00% |
23 / 23 |
|
100.00% |
4 / 4 |
11 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| getValue | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getQuality | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| parseList | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
8 | |||
| 1 | <?php |
| 2 | declare(strict_types=1); |
| 3 | /** |
| 4 | * Pop PHP Framework (https://www.popphp.org/) |
| 5 | * |
| 6 | * @link https://github.com/popphp/popphp-framework |
| 7 | * @author Nick Sagona, III <nick@popphp.org> |
| 8 | * @copyright Copyright (c) 2009-2026 Nick Sagona, III |
| 9 | * @license https://www.popphp.org/license New BSD License |
| 10 | */ |
| 11 | |
| 12 | /** |
| 13 | * @namespace |
| 14 | */ |
| 15 | namespace Pop\Http\Server; |
| 16 | |
| 17 | /** |
| 18 | * HTTP quality value class |
| 19 | * |
| 20 | * Generic RFC 7231 Section 5.3.1 "value;q=N" list parser - has no knowledge of media types, |
| 21 | * so it applies equally to Accept, Accept-Language, Accept-Encoding and Accept-Charset. |
| 22 | * |
| 23 | * @category Pop |
| 24 | * @package Pop\Http |
| 25 | * @author Nick Sagona, III <nick@popphp.org> |
| 26 | * @copyright Copyright (c) 2009-2026 Nick Sagona, III |
| 27 | * @license https://www.popphp.org/license New BSD License |
| 28 | * @version 6.0.0 |
| 29 | */ |
| 30 | class QualityValue |
| 31 | { |
| 32 | |
| 33 | /** |
| 34 | * The raw value (e.g. 'text/html', 'en-US') |
| 35 | * @var string |
| 36 | */ |
| 37 | protected string $value; |
| 38 | |
| 39 | /** |
| 40 | * The quality weight, 0.0 - 1.0 |
| 41 | * @var float |
| 42 | */ |
| 43 | protected float $quality; |
| 44 | |
| 45 | /** |
| 46 | * Constructor |
| 47 | * |
| 48 | * @param string $value |
| 49 | * @param float $quality |
| 50 | */ |
| 51 | public function __construct(string $value, float $quality = 1.0) |
| 52 | { |
| 53 | $this->value = $value; |
| 54 | $this->quality = $quality; |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * Get the raw value |
| 59 | * |
| 60 | * @return string |
| 61 | */ |
| 62 | public function getValue(): string |
| 63 | { |
| 64 | return $this->value; |
| 65 | } |
| 66 | |
| 67 | /** |
| 68 | * Get the quality weight |
| 69 | * |
| 70 | * @return float |
| 71 | */ |
| 72 | public function getQuality(): float |
| 73 | { |
| 74 | return $this->quality; |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * Parse a raw header value into a list of QualityValue entries, sorted by quality |
| 79 | * descending (stable sort - original order preserved among entries of equal quality). |
| 80 | * A missing or malformed 'q' parameter defaults to 1.0 rather than being dropped or |
| 81 | * throwing - this is client-controlled input, and a malformed weight shouldn't fail |
| 82 | * the request, only fail to deprioritize correctly. |
| 83 | * |
| 84 | * @param string $header |
| 85 | * @return QualityValue[] |
| 86 | */ |
| 87 | public static function parseList(string $header): array |
| 88 | { |
| 89 | $entries = []; |
| 90 | |
| 91 | foreach (explode(',', $header) as $part) { |
| 92 | $part = trim($part); |
| 93 | if ($part === '') { |
| 94 | continue; |
| 95 | } |
| 96 | |
| 97 | $segments = explode(';', $part); |
| 98 | $value = trim(array_shift($segments)); |
| 99 | $quality = 1.0; |
| 100 | |
| 101 | foreach ($segments as $segment) { |
| 102 | $segment = trim($segment); |
| 103 | if (stripos($segment, 'q=') === 0) { |
| 104 | $q = trim(substr($segment, 2)); |
| 105 | if (is_numeric($q) && ((float)$q >= 0) && ((float)$q <= 1)) { |
| 106 | $quality = (float)$q; |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | $entries[] = new QualityValue($value, $quality); |
| 112 | } |
| 113 | |
| 114 | usort($entries, function(QualityValue $a, QualityValue $b) { |
| 115 | return $b->getQuality() <=> $a->getQuality(); |
| 116 | }); |
| 117 | |
| 118 | return $entries; |
| 119 | } |
| 120 | |
| 121 | } |