Completed
Pull Request — master (#3)
by Mārtiņš
02:47
created

Accept::obtainAssessedItem()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

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