Completed
Push — master ( 2d7110...752264 )
by Mārtiņš
8s
created

Accept::computeSpecificity()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
ccs 0
cts 0
cp 0
rs 9.4285
cc 3
eloc 8
nc 4
nop 1
crap 12
1
<?php
2
3
namespace Fracture\Http\Headers;
4
5
class Accept extends Common
6
{
7
8
    protected $headerName = 'Accept';
9
10
11
    /**
12
     * @param string $headerValue
13
     * @return array[]
14
     */
15 10
    protected function extractData($headerValue)
16
    {
17 10
        $elements = preg_split('#,\s?#', $headerValue, -1, \PREG_SPLIT_NO_EMPTY);
18 10
        $elements = $this->obtainGroupedElements($elements);
19 10
        $keys = $this->obtainSortedQualityList($elements);
20 10
        return $this->obtainSortedElements($elements, $keys);
21
    }
22
23
24
    /**
25
     * @param array $elements
26
     */
27 10
    private function obtainGroupedElements($elements)
28
    {
29 10
        $result = [];
30
31 10
        foreach ($elements as $item) {
32 10
            $item = $this->obtainAssessedItem($item);
33 10
            $quality = $item[ 'q' ];
34
35 10
            if (array_key_exists($quality, $result) === false) {
36 10
                $result[$quality] = [];
37
            }
38
39 10
            $result[$quality][] = $item;
40
        }
41
42 10
        return $result;
43
    }
44
45
46
    /**
47
     * @param string $item
48
     * @return array
49
     */
50 11
    private function obtainAssessedItem($item)
51
    {
52 11
        $result = [];
53 11
        $parts = preg_split('#;\s?#', $item, -1, \PREG_SPLIT_NO_EMPTY);
54 11
        $result['value'] = array_shift($parts);
55
56 11
        foreach ($parts as $item) {
57 9
            list($key, $value) = explode('=', $item . '=');
58 9
            $result[$key] = $value;
59
        }
60
61 11
        $result = $result + ['q' => '1'];
62
63 11
        return $result;
64
    }
65
66
67
    /**
68
     * @param array[] $elements
69
     * @return array[]
70
     */
71 10
    private function obtainSortedQualityList($elements)
72
    {
73 10
        $keys = array_keys($elements);
74
        $keys = array_map(function ($value) {
75 10
            return (float)$value;
76 10
        }, $keys);
77 10
        rsort($keys);
78
        return array_map(function ($value) {
79 10
            return (string)$value;
80 10
        }, $keys);
81
    }
82
83
84
    /**
85
     * @param array[] $elements
86
     * @param array $keys
87
     * @return array[]
88
     */
89 10
    private function obtainSortedElements($elements, $keys)
90
    {
91 10
        $list = [];
92
93 10
        foreach ($keys as $key) {
94 10
            $sorted = $this->sortBySpecificity($elements[$key]);
95 10
            foreach ($sorted as $item) {
96 10
                unset($item['q'], $item[' spec ']);
97 10
                $list[] = $item;
98
            }
99
        }
100
101 10
        return $list;
102
    }
103
104
105
    private function sortBySpecificity($list)
106
    {
107
108
        foreach ($list as $key => $item) {
109
            $list[$key][' spec '] = $this->computeSpecificity($item);
110
        }
111
112
        uksort($list, function($a, $b) use ($list) {
113
            if ($list[$a][' spec '] === $list[$b][' spec ']) {
114
                return $a > $b ? 1 : -1;
115
            }
116
117
            return $list[$a][' spec '] > $list[$b][' spec '] ? -1 : 1;
118
        });
119
120
        return $list;
121
    }
122
123
124
    private function computeSpecificity($entry)
125
    {
126
        list($type, $subtype) = explode('/', $entry['value'] . '/');
127
        $specificity = count($entry) - 2;
128
129
        if ($type !== '*') {
130
            $specificity += 1000;
131
        }
132
133
        if ($subtype !== '*') {
134
            $specificity += 100;
135
        }
136
137
        return $specificity;
138
    }
139
140
141
    /**
142
     * @param string $type
143
     * @return bool
144
     */
145 2
    public function contains($type)
146
    {
147 2
        $expected = $this->obtainAssessedItem($type);
148 2
        unset($expected['q']);
149
150 2
        if ($this->data === null) {
151 1
            return false;
152
        }
153
154 1
        return $this->matchFound($this->data, $expected);
155
    }
156
157
158
    /**
159
     * @param array $data
160
     * @param array $expected
161
     * @return bool
162
     */
163 1
    private function matchFound($data, $expected)
164
    {
165 1
        foreach ($data as $item) {
166 1
            if ($this->isMatch($expected, $item)) {
167 1
                return true;
168
            }
169
        }
170
171 1
        return false;
172
    }
173
174
    /**
175
     * @param string $options
176
     * @return null|string
177
     */
178 13
    public function getPreferred($options)
179
    {
180 13
        $data = $this->extractData($options);
181
182 13
        if ($this->data === null) {
183 1
            return null;
184
        }
185
186 12
        return $this->findFormatedEntry($this->data, $data);
187
    }
188
189
190
    /**
191
     * @param array $data
192
     * @param array $expected
0 ignored issues
show
Bug introduced by
There is no parameter named $expected. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
193
     * @return null|string
194
     */
195 12
    private function findFormatedEntry($data, $options)
196
    {
197 12
        foreach ($data as $item) {
198 12
            $entry = $this->obtainEntryFromList($item, $options);
199
200 12
            if ($entry !== null) {
201 12
                return $this->getFormatedEntry($entry);
202
            }
203
        }
204
205 2
        return null;
206
    }
207
208
209
    /**
210
     * @param array $entry
211
     * @return string
212
     */
213 12
    public function getFormatedEntry($entry)
214
    {
215 12
        if (count($entry) === 1) {
216 10
            return $entry['value'];
217
        }
218
219 2
        $value = $entry['value'];
220 2
        unset($entry['value']);
221
222 2
        array_walk($entry, function (&$item, $key) {
223 2
            $item = $key . '=' . $item;
224 2
        });
225 2
        return $value . ';' .  implode(';', $entry);
226
    }
227
228
229
    /**
230
     * @param array $needle
231
     * @param array[] $haystack
232
     * @return null|array
233
     */
234 12
    private function obtainEntryFromList(array $needle, $haystack)
235
    {
236 12
        foreach ($haystack as $item) {
237 12
            if ($this->isMatch($item, $needle)) {
238 12
                return $item;
239
            }
240
        }
241
242 5
        return null;
243
    }
244
245
246
    /**
247
     * @param string $left
248
     * @param string $right
249
     * @return bool
250
     */
251 1
    private function isMatch(array $left, array $right)
252
    {
253 1
        if ($left == $right) {
254 1
            return true;
255
        }
256
257 1
        $left['value'] = $this->replaceStars($left['value'], $right['value']);
258 1
        $right['value'] = $this->replaceStars($right['value'], $left['value']);
259
260
        // compares two arrays with keys in different order
261 1
        return $left == $right;
262
    }
263
264
265
    /**
266
     * @param string $target
267
     * @param string pattern
268
     * @return string
269
     */
270 10
    private function replaceStars($target, $pattern)
271
    {
272 10
        $target = explode('/', $target . '/*');
273 10
        $pattern = explode('/', $pattern . '/*');
274
275 10
        if ($pattern[0] === '*') {
276 1
            $target[0] = '*';
277
        }
278
279 10
        if ($pattern[1] === '*') {
280 2
            $target[1] = '*';
281
        }
282
283 10
        return $target[0] . '/' . $target[1];
284
    }
285
}
286