Accept   B
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 281
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 10
Bugs 0 Features 0
Metric Value
wmc 38
c 10
b 0
f 0
lcom 1
cbo 1
dl 0
loc 281
ccs 104
cts 104
cp 1
rs 8.3999

15 Methods

Rating   Name   Duplication   Size   Complexity  
A extractData() 0 7 1
A obtainSortedQualityList() 0 11 1
A obtainGroupedElements() 0 17 3
A obtainAssessedItem() 0 15 2
A obtainSortedElements() 0 14 3
A contains() 0 11 2
A matchFound() 0 10 3
A getPreferred() 0 10 2
A findFormatedEntry() 0 12 3
A getFormatedEntry() 0 14 2
A obtainEntryFromList() 0 10 3
A isMatch() 0 12 2
A replaceStars() 0 15 3
B sortBySpecificity() 0 17 5
A computeSpecificity() 0 15 3
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 13
    private function sortBySpecificity($list)
106
    {
107
108 13
        foreach ($list as $key => $item) {
109 13
            $list[$key][' spec '] = $this->computeSpecificity($item);
110
        }
111
112
        uksort($list, function($a, $b) use ($list) {
113 7
            if ($list[$a][' spec '] === $list[$b][' spec ']) {
114 6
                return $a > $b ? 1 : -1;
115
            }
116
117 3
            return $list[$a][' spec '] > $list[$b][' spec '] ? -1 : 1;
118 13
        });
119
120 13
        return $list;
121
    }
122
123
124 13
    private function computeSpecificity($entry)
125
    {
126 13
        list($type, $subtype) = explode('/', $entry['value'] . '/');
127 13
        $specificity = count($entry) - 2;
128
129 13
        if ($type !== '*') {
130 13
            $specificity += 1000;
131
        }
132
133 13
        if ($subtype !== '*') {
134 13
            $specificity += 100;
135
        }
136
137 13
        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 $options
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