Completed
Pull Request — master (#4)
by Mārtiņš
01:53
created

Accept::obtainAssessedItem()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
ccs 9
cts 9
cp 1
rs 9.4285
cc 2
eloc 9
nc 2
nop 1
crap 2
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 '], $item[' index ']);
97 10
                $list[] = $item;
98
            }
99
        }
100
101 10
        return $list;
102
    }
103
104
105
    private function sortBySpecificity($list)
106
    {
107
        $i = 0;
108
109
        foreach ($list as $key => $item) {
110
            $list[$key][' spec '] = $this->computeSpecificity($item);
111
            $list[$key][' index '] = $i++;
112
        }
113
114
        usort($list, function($a, $b) {
115
            if ($a[' spec '] !== $b[' spec ']) {
116
                return $a[' spec '] > $b[' spec '] ? -1 : 1;
117
            }
118
119
            if ($a[' index '] !== $b[' index ']) {
120
                return $a[' index '] > $b[' index '] ? 1 : -1;
121
            }
122
123
            return 0;
124
        });
125
126
        return $list;
127
    }
128
129
130
    private function computeSpecificity($entry)
131
    {
132
        list($type, $subtype) = explode('/', $entry['value'] . '/');
133
        $specificity = count($entry) - 2;
134
135
        if ($type !== '*') {
136
            $specificity += 1000;
137
        }
138
139
        if ($subtype !== '*') {
140
            $specificity += 100;
141
        }
142
143
        return $specificity;
144
    }
145
146
147
    /**
148
     * @param string $type
149
     * @return bool
150
     */
151 2
    public function contains($type)
152
    {
153 2
        $expected = $this->obtainAssessedItem($type);
154 2
        unset($expected['q']);
155
156 2
        if ($this->data === null) {
157 1
            return false;
158
        }
159
160 1
        return $this->matchFound($this->data, $expected);
161
    }
162
163
164
    /**
165
     * @param array $data
166
     * @param array $expected
167
     * @return bool
168
     */
169 1
    private function matchFound($data, $expected)
170
    {
171 1
        foreach ($data as $item) {
172 1
            if ($this->isMatch($expected, $item)) {
173 1
                return true;
174
            }
175
        }
176
177 1
        return false;
178
    }
179
180
    /**
181
     * @param string $options
182
     * @return null|string
183
     */
184 13
    public function getPreferred($options)
185
    {
186 13
        $data = $this->extractData($options);
187
188 13
        if ($this->data === null) {
189 1
            return null;
190
        }
191
192 12
        return $this->findFormatedEntry($this->data, $data);
193
    }
194
195
196
    /**
197
     * @param array $data
198
     * @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...
199
     * @return null|string
200
     */
201 12
    private function findFormatedEntry($data, $options)
202
    {
203 12
        foreach ($data as $item) {
204 12
            $entry = $this->obtainEntryFromList($item, $options);
205
206 12
            if ($entry !== null) {
207 12
                return $this->getFormatedEntry($entry);
208
            }
209
        }
210
211 2
        return null;
212
    }
213
214
215
    /**
216
     * @param array $entry
217
     * @return string
218
     */
219 12
    public function getFormatedEntry($entry)
220
    {
221 12
        if (count($entry) === 1) {
222 10
            return $entry['value'];
223
        }
224
225 2
        $value = $entry['value'];
226 2
        unset($entry['value']);
227
228 2
        array_walk($entry, function (&$item, $key) {
229 2
            $item = $key . '=' . $item;
230 2
        });
231 2
        return $value . ';' .  implode(';', $entry);
232
    }
233
234
235
    /**
236
     * @param array $needle
237
     * @param array[] $haystack
238
     * @return null|array
239
     */
240 12
    private function obtainEntryFromList(array $needle, $haystack)
241
    {
242 12
        foreach ($haystack as $item) {
243 12
            if ($this->isMatch($item, $needle)) {
244 12
                return $item;
245
            }
246
        }
247
248 5
        return null;
249
    }
250
251
252
    /**
253
     * @param string $left
254
     * @param string $right
255
     * @return bool
256
     */
257 1
    private function isMatch(array $left, array $right)
258
    {
259 1
        if ($left == $right) {
260 1
            return true;
261
        }
262
263 1
        $left['value'] = $this->replaceStars($left['value'], $right['value']);
264 1
        $right['value'] = $this->replaceStars($right['value'], $left['value']);
265
266
        // compares two arrays with keys in different order
267 1
        return $left == $right;
268
    }
269
270
271
    /**
272
     * @param string $target
273
     * @param string pattern
274
     * @return string
275
     */
276 10
    private function replaceStars($target, $pattern)
277
    {
278 10
        $target = explode('/', $target . '/*');
279 10
        $pattern = explode('/', $pattern . '/*');
280
281 10
        if ($pattern[0] === '*') {
282 1
            $target[0] = '*';
283
        }
284
285 10
        if ($pattern[1] === '*') {
286 2
            $target[1] = '*';
287
        }
288
289 10
        return $target[0] . '/' . $target[1];
290
    }
291
}
292