Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
QualityValue
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
4 / 4
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getQuality
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 parseList
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
8
1<?php
2declare(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 */
15namespace 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 */
30class 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}