Completed
Pull Request — master (#151)
by
unknown
02:09
created

HeaderHelper::getByQFactorSortedList()   B

Complexity

Conditions 11
Paths 18

Size

Total Lines 36
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 11

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 25
c 1
b 0
f 0
nc 18
nop 2
dl 0
loc 36
rs 7.3166
ccs 26
cts 26
cp 1
crap 11

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Yiisoft\Yii\Web\Helper;
4
5
use Psr\Http\Message\RequestInterface;
6
7
final class HeaderHelper
8
{
9
    /**
10
     * Explode header value to value and parameters (eg. text/html;q=2;version=6)
11
     * @param string $headerValue
12
     * @return array first element is the value, and key-value are the parameters
13
     */
14 27
    public static function getValueAndParameters(string $headerValue): array
15
    {
16 27
        $headerValue = trim($headerValue);
17 27
        if (strlen($headerValue) === 0) {
18 1
            return [];
19
        }
20 26
        $parts = preg_split('/\s*;\s*/', $headerValue, -1, PREG_SPLIT_NO_EMPTY);
21 26
        $output = [array_shift($parts)];
0 ignored issues
show
Bug introduced by
It seems like $parts can also be of type false; however, parameter $array of array_shift() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

21
        $output = [array_shift(/** @scrutinizer ignore-type */ $parts)];
Loading history...
22 26
        foreach ($parts as $part) {
23 19
            [$key, $headerValue] = explode('=', $part, 2);
24 19
            $output[$key] = $headerValue;
25
        }
26 26
        return $output;
27
    }
28
29
    /**
30
     * Getting header value as q factor sorted list
31
     * @param string|string[] $values Header value as a comma-separated string or already exploded string array.
32
     * @see getValueAndParameters
33
     * @link https://developer.mozilla.org/en-US/docs/Glossary/Quality_values
34
     */
35 31
    public static function getByQFactorSortedList($values, bool $outputValuesOnly = false): array
36
    {
37 31
        if (is_string($values)) {
38 21
            $values = preg_split('/\s*,\s*/', trim($values), -1, PREG_SPLIT_NO_EMPTY);
39
        }
40 31
        if (!is_array($values)) {
41 3
            throw new \InvalidArgumentException('Values ​​are neither array nor string!');
42
        }
43 28
        if (count($values) === 0) {
44 4
            return [];
45
        }
46 24
        $output = [];
47 24
        foreach ($values as $value) {
48 24
            $parse = self::getValueAndParameters($value);
49 24
            $q = $parse['q'] ?? 1.0;
50 24
            if (is_string($q) && preg_match('/^(?:0(?:\.\d{1,3})?|1(?:\.0{1,3})?)$/', $q) === 0) {
51 4
                throw new \InvalidArgumentException('Invalid q factor!');
52
            }
53 20
            $parse['q'] = floatval($q);
54 20
            $output[] = $parse;
55
        }
56
        usort($output, function ($a, $b) {
57 17
            $a = $a['q'];
58 17
            $b = $b['q'];
59 17
            if ($a === $b) {
60 10
                return 0;
61
            }
62 7
            return $a > $b ? -1 : 1;
63 20
        });
64 20
        if (!$outputValuesOnly) {
65 18
            return $output;
66
        }
67 2
        foreach ($output as &$value) {
68 2
            $value = array_shift($value);
69
        }
70 2
        return $output;
71
    }
72
73
    /**
74
     * @param $values string|string[]|RequestInterface $values Header value as a comma-separated string
75
     *                                                         or already exploded string array
76
     *                                                         or request interface with 'accept' header.
77
     * @return string[] sorted accept types. Note: According to RFC 7231, special parameters (except the q factor) are
78
     *                  added to the type, which are always appended by a semicolon and sorted by string.
79
     * @link https://tools.ietf.org/html/rfc7231#section-5.3.2
80
     */
81 17
    public static function getSortedAcceptTypes($values): array
82
    {
83 17
        if ($values instanceof RequestInterface) {
84 5
            $values = $values->getHeader('accept');
85
        }
86 17
        $output = self::getByQFactorSortedList($values);
87
        usort($output, function ($a, $b) {
88 12
            if ($a['q'] !== $b['q']) {
89
                // The higher q value wins
90 4
                return $a['q'] > $b['q'] ? -1 : 1;
91
            }
92 8
            $typeA = reset($a);
93 8
            $typeB = reset($b);
94 8
            if (strpos($typeA, '*') === false && strpos($typeB, '*') === false) {
95 8
                $countA = count($a);
96 8
                $countB = count($b);
97 8
                if ($countA === $countB) {
98
                    // They are equivalent for the same parameter number
99 5
                    return 0;
100
                }
101
                // No wildcard character, higher parameter number wins
102 3
                return $countA > $countB ? -1 : 1;
103
            }
104 1
            $endWildcardA = substr($typeA, -1, 1) === '*';
105 1
            $endWildcardB = substr($typeB, -1, 1) === '*';
106 1
            if (($endWildcardA && !$endWildcardB) || (!$endWildcardA && $endWildcardB)) {
107
                // The wildcard ends is the loser.
108 1
                return $endWildcardA ? 1 : -1;
109
            }
110
            // The wildcard starts is the loser.
111 1
            return strpos($typeA, '*') === 0 ? 1 : -1;
112 17
        });
113 17
        foreach ($output as $key => $value) {
114 15
            $type = array_shift($value);
115 15
            unset($value['q']);
116 15
            if (count($value) === 0) {
117 14
                $output[$key] = $type;
118 14
                continue;
119
            }
120 5
            foreach ($value as $k => $v) {
121 5
                $value[$k] = $k . '=' . $v;
122
            }
123 5
            asort($value, SORT_STRING);
124 5
            $output[$key] = $type . ';' . join(';', $value);
125
        }
126 17
        return $output;
127
    }
128
}
129