GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Parser::processMask()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 6

Duplication

Lines 3
Ratio 50 %

Code Coverage

Tests 3
CRAP Score 3.576

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 3
loc 6
ccs 3
cts 5
cp 0.6
crap 3.576
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Nathanmac\Utilities\Parser;
4
5
use Nathanmac\Utilities\Parser\Formats\BSON;
6
use Nathanmac\Utilities\Parser\Formats\FormatInterface;
7
use Nathanmac\Utilities\Parser\Formats\JSON;
8
use Nathanmac\Utilities\Parser\Formats\MSGPack;
9
use Nathanmac\Utilities\Parser\Formats\QueryStr;
10
use Nathanmac\Utilities\Parser\Formats\Serialize;
11
use Nathanmac\Utilities\Parser\Formats\XML;
12
use Nathanmac\Utilities\Parser\Formats\YAML;
13
14
/**
15
 * Parser Library, designed to parse payload data from various formats to php array.
16
 *
17
 * @package    Nathanmac\Utilities\Parser
18
 * @author     Nathan Macnamara <[email protected]>
19
 * @license    https://github.com/nathanmac/Parser/blob/master/LICENSE.md  MIT
20
 */
21
class Parser
22
{
23
    /**
24
     * @var string
25
     */
26
    private $wildcards = '/^(\*|%|:first|:last|:(index|item)\[\d+\])$/';
27
28
    /**
29
     * @var array Supported Formats
30
     */
31
    private $supported_formats = [
32
      // XML
33
        'application/xml' => 'Nathanmac\Utilities\Parser\Formats\XML',
34
        'text/xml'        => 'Nathanmac\Utilities\Parser\Formats\XML',
35
        'xml'             => 'Nathanmac\Utilities\Parser\Formats\XML',
36
      // JSON
37
        'application/json'         => 'Nathanmac\Utilities\Parser\Formats\JSON',
38
        'application/x-javascript' => 'Nathanmac\Utilities\Parser\Formats\JSON',
39
        'text/javascript'          => 'Nathanmac\Utilities\Parser\Formats\JSON',
40
        'text/x-javascript'        => 'Nathanmac\Utilities\Parser\Formats\JSON',
41
        'text/x-json'              => 'Nathanmac\Utilities\Parser\Formats\JSON',
42
        'json'                     => 'Nathanmac\Utilities\Parser\Formats\JSON',
43
      // BSON
44
        'application/bson' => 'Nathanmac\Utilities\Parser\Formats\BSON',
45
        'bson'             => 'Nathanmac\Utilities\Parser\Formats\BSON',
46
      // YAML
47
        'text/yaml'          => 'Nathanmac\Utilities\Parser\Formats\YAML',
48
        'text/x-yaml'        => 'Nathanmac\Utilities\Parser\Formats\YAML',
49
        'application/yaml'   => 'Nathanmac\Utilities\Parser\Formats\YAML',
50
        'application/x-yaml' => 'Nathanmac\Utilities\Parser\Formats\YAML',
51
        'yaml'               => 'Nathanmac\Utilities\Parser\Formats\YAML',
52
      // MSGPACK
53
        'application/msgpack'   => 'Nathanmac\Utilities\Parser\Formats\MSGPack',
54
        'application/x-msgpack' => 'Nathanmac\Utilities\Parser\Formats\MSGPack',
55
        'msgpack'               => 'Nathanmac\Utilities\Parser\Formats\MSGPack',
56
      // MISC
57
        'application/vnd.php.serialized'    => 'Nathanmac\Utilities\Parser\Formats\Serialize',
58
        'serialize'                         => 'Nathanmac\Utilities\Parser\Formats\Serialize',
59
        'application/x-www-form-urlencoded' => 'Nathanmac\Utilities\Parser\Formats\QueryStr',
60
        'querystr'                          => 'Nathanmac\Utilities\Parser\Formats\QueryStr',
61
    ];
62
63
    /* ------------ Access Methods/Helpers ------------ */
64
65
    /**
66
     * Get a subset of the items from the payload data.
67
     *
68
     * @param  string|array $keys
69
     *
70
     * @return array
71
     */
72 9
    public function only($keys)
73
    {
74 9
        $keys = is_array($keys) ? $keys : func_get_args();
75
76 9
        $results = [];
77 9
        foreach ($keys as $key) {
78 9
            $results = array_merge_recursive($results, $this->buildArray(explode('.', $key), $this->get($key)));
79 9
        }
80
81 9
        return $results;
82
    }
83
84
    /**
85
     * Get all of the input except for a specified array of items.
86
     *
87
     * @param  string|array  $keys
88
     * @return array
89
     */
90 6
    public function except($keys)
91
    {
92 6
        $keys = is_array($keys) ? $keys : func_get_args();
93
94 6
        $results = $this->payload();
95
96 6
        foreach ($keys as $key) {
97 6
            $this->removeValue($results, $key);
98 6
        }
99 6
        return $results;
100
    }
101
102
    /**
103
     * Determine if the payload contains a non-empty value for a given key.
104
     *
105
     * @param  string|array $keys
106
     *
107
     * @return bool
108
     */
109 21
    public function has($keys)
110
    {
111 21
        $keys = is_array($keys) ? $keys : func_get_args();
112
113 21
        $results = $this->payload();
114
115 21
        foreach ($keys as $value) {
116 21
            if ($this->hasValueAtKey($value, $results) === false) {
117 18
                return false;
118
            }
119 21
        }
120 21
        return true;
121
    }
122
123
    /**
124
     * Retrieve an payload item from the payload data, return default item if item not found.
125
     *
126
     * @param string $key
127
     * @param string $default
128
     *
129
     * @return mixed|null
130
     */
131 18
    public function get($key = null, $default = null)
132
    {
133 18
        if ($this->has($key)) {
134 18
            return $this->getValueAtKey($key, $this->payload());
135
        }
136 9
        return $default;
137
    }
138
139
    /**
140
     * Mask input data with a given mapping.
141
     *
142
     * @param array $mask
143
     *
144
     * @return array
145
     */
146 3
    public function mask(array $mask)
147
    {
148 3
        $keys = [];
149 3 View Code Duplication
        foreach ($mask as $key => $value) {
150 3
            $keys[] = $key . (is_array($value) ? $this->processMask($value) : '');
151 3
        }
152
153 3
        return $this->only($keys);
154
    }
155
156
    /**
157
     * Recursive processor for processing user masks.
158
     *
159
     * @param array $mask
160
     *
161
     * @return string
162
     */
163 3
    private function processMask($mask)
164
    {
165 3 View Code Duplication
        foreach ($mask as $key => $value) {
166 3
            return '.' . $key . (is_array($value) ? $this->processMask($value) : '');
167
        }
168
    }
169
170
    /**
171
     * Parse the HTTP payload data, autodetect format and return all data in array.
172
     *  Override the format by providing a content type.
173
     *
174
     * @param string $format
175
     *
176
     * @return array
177
     */
178 57
    public function payload($format = '')
179
    {
180 57
        $class = $this->getFormatClass($format);
181 57
        return $this->parse($this->getPayload(), new $class);
182
    }
183
184
    /**
185
     * Alias to the payload function.
186
     *
187
     * @return array
188
     */
189 3
    public function all()
190
    {
191 3
        return $this->payload();
192
    }
193
194
    /**
195
     * Autodetect the payload data type using content-type value.
196
     *
197
     * @return string Return the name of the formatter class.
198
     */
199 66
    public function getFormatClass($format = '')
200
    {
201 66
        if ( ! empty($format)) {
202 9
            return $this->processContentType($format);
203
        }
204
205 57
        if (isset($_SERVER['CONTENT_TYPE'])) {
206 18
            $type = $this->processContentType($_SERVER['CONTENT_TYPE']);
207 18
            if ($type !== false) {
208 3
                return $type;
209
            }
210 18
        }
211
212 57
        if (isset($_SERVER['HTTP_CONTENT_TYPE'])) {
213 27
            $type = $this->processContentType($_SERVER['HTTP_CONTENT_TYPE']);
214 27
            if ($type !== false) {
215 21
                return $type;
216
            }
217 6
        }
218
219 36
        return 'Nathanmac\Utilities\Parser\Formats\JSON';
220
    }
221
222
    /**
223
     * Process the content-type values
224
     *
225
     * @param string $contentType Content-Type raw string
226
     *
227
     * @return bool|string
228
     */
229 36
    private function processContentType($contentType)
230
    {
231 36
        foreach (explode(';', $contentType) as $type) {
232 36
            $type = strtolower(trim($type));
233 36
            if (isset($this->supported_formats[$type])) {
234 33
                return $this->supported_formats[$type];
235
            }
236 18
        }
237
238 18
        return false;
239
    }
240
241
    /**
242
     * Return the payload data from the HTTP post request.
243
     *
244
     * @codeCoverageIgnore
245
     *
246
     * @return string
247
     */
248
    protected function getPayload()
249
    {
250
        return file_get_contents('php://input');
251
    }
252
253
    /**
254
     * Parse payload string using given formatter.
255
     *
256
     * @param string $payload
257
     * @param FormatInterface $format
258
     *
259
     * @return array
260
     */
261 136
    public function parse($payload, FormatInterface $format)
262
    {
263 136
        return $format->parse($payload);
264
    }
265
266
    /* ------------ Format Registration Methods ------------ */
267
268
    /**
269
     * Register Format Class.
270
     *
271
     * @param $format
272
     * @param $class
273
     *
274
     * @throws InvalidArgumentException
275
     *
276
     * @return self
277
     */
278 3
    public function registerFormat($format, $class)
279
    {
280 3
        if ( ! class_exists($class)) {
281
            throw new \InvalidArgumentException("Parser formatter class {$class} not found.");
282
        }
283 3
        if ( ! is_a($class, 'Nathanmac\Utilities\Parser\Formats\FormatInterface', true)) {
284
            throw new \InvalidArgumentException('Parser formatters must implement the Nathanmac\Utilities\Parser\Formats\FormatInterface interface.');
285
        }
286
287 3
        $this->supported_formats[$format] = $class;
288
289 3
        return $this;
290
    }
291
292
    /* ------------ Helper Methods ------------ */
293
294
    /**
295
     * XML parser, helper function.
296
     *
297
     * @param $payload
298
     *
299
     * @throws Exceptions\ParserException
300
     *
301
     * @return array
302
     */
303 36
    public function xml($payload)
304
    {
305 36
        return $this->parse($payload, new XML());
306
    }
307
308
    /**
309
     * JSON parser, helper function.
310
     *
311
     * @param $payload
312
     *
313
     * @throws Exceptions\ParserException
314
     * @return array
315
     */
316 9
    public function json($payload)
317
    {
318 9
        return $this->parse($payload, new JSON());
319
    }
320
321
    /**
322
     * BSON parser, helper function.
323
     *
324
     * @param $payload
325
     *
326
     * @throws Exceptions\ParserException
327
     *
328
     * @return array
329
     */
330 3
    public function bson($payload)
331
    {
332 3
        return $this->parse($payload, new BSON());
333
    }
334
335
    /**
336
     * Serialized Data parser, helper function.
337
     *
338
     * @param $payload
339
     *
340
     * @throws Exceptions\ParserException
341
     *
342
     * @return array
343
     */
344 9
    public function serialize($payload)
345
    {
346 9
        return $this->parse($payload, new Serialize());
347
    }
348
349
    /**
350
     * Query String parser, helper function.
351
     *
352
     * @param $payload
353
     *
354
     * @return array
355
     */
356 6
    public function querystr($payload)
357
    {
358 6
        return $this->parse($payload, new QueryStr());
359
    }
360
361
    /**
362
     * YAML parser, helper function.
363
     *
364
     * @param $payload
365
     *
366
     * @throws Exceptions\ParserException
367
     *
368
     * @return array
369
     */
370 9
    public function yaml($payload)
371
    {
372 9
        return $this->parse($payload, new Yaml());
373
    }
374
375
    /**
376
     * MSGPack parser, helper function.
377
     *
378
     * @param $payload
379
     *
380
     * @throws Exceptions\ParserException
381
     *
382
     * @return array
383
     */
384 7
    public function msgpack($payload)
385
    {
386 7
        return $this->parse($payload, new MSGPack());
387
    }
388
389
    /* ------------ Construction Methods ------------ */
390
391
    /**
392
     * Return a value from the array identified from the key.
393
     *
394
     * @param $key
395
     * @param $data
396
     * @return mixed
397
     */
398 18
    private function getValueAtKey($key, $data)
399
    {
400 18
        $keys = explode('.', $key);
401
402 18
        while (count($keys) > 1) {
403 12
            $key = array_shift($keys);
404
405
            // Wildcard Key
406 12
            if (preg_match($this->wildcards, $key) && is_array($data) && ! empty($data)) {
407
                // Shift the first item of the array
408 3 View Code Duplication
                if (preg_match('/^:(index|item)\[\d+\]$/', $key)) {
409 3
                    for ($x = substr($key, 7, -1); $x >= 0; $x--) {
410 3
                        if (empty($data)) {
411
                            return false;
412
                        }
413 3
                        $item = array_shift($data);
414 3
                    }
415 3
                } elseif ($key == ':last') {
416 3
                    $item = array_pop($data);
417 3
                } else {
418 3
                    $item = array_shift($data);
419
                }
420 3
                $data =& $item;
421 3
            } else {
422 12
                if ( ! isset($data[$key]) || ! is_array($data[$key])) {
423
                    return false;
424
                }
425
426 12
                $data =& $data[$key];
427
            }
428 12
        }
429
430
        // Return value
431 18
        $key = array_shift($keys);
432 18
        if (preg_match($this->wildcards, $key)) {
433 6
            if (preg_match('/^:(index|item)\[\d+\]$/', $key)) {
434 6
                for ($x = substr($key, 7, -1); $x >= 0; $x--) {
435 6
                    if (empty($data)) {
436
                        return false;
437
                    }
438 6
                    $item = array_shift($data);
439 6
                }
440 6
                return $item;
441 6
            } elseif ($key == ':last') {
442 6
                return array_pop($data);
443
            }
444 6
            return array_shift($data); // First Found
445
        }
446 15
        return ($data[$key]);
447
    }
448
449
    /**
450
     * Array contains a value identified from the key, returns bool
451
     *
452
     * @param $key
453
     * @param $data
454
     * @return bool
455
     */
456 21
    private function hasValueAtKey($key, $data)
457
    {
458 21
        $keys = explode('.', $key);
459
460 21
        while (count($keys) > 0) {
461 21
            $key = array_shift($keys);
462
463
            // Wildcard Key
464 21
            if (preg_match($this->wildcards, $key) && is_array($data) && ! empty($data)) {
465
                // Shift the first item of the array
466 6 View Code Duplication
                if (preg_match('/^:(index|item)\[\d+\]$/', $key)) {
467 6
                    for ($x = substr($key, 7, -1); $x >= 0; $x--) {
468 6
                        if (empty($data)) {
469 3
                            return false;
470
                        }
471 6
                        $item = array_shift($data);
472 6
                    }
473 6
                } elseif ($key == ':last') {
474 6
                    $item = array_pop($data);
475 6
                } else {
476 6
                    $item = array_shift($data);
477
                }
478 6
                $data =& $item;
479 6
            } else {
480 21
                if ( ! isset($data[$key])) {
481 15
                    return false;
482
                }
483
484 21
                if (is_bool($data[$key])) {
485 3
                    return true;
486
                }
487
488 21
                if ($data[$key] === '') {
489 6
                    return false;
490
                }
491
492 21
                $data =& $data[$key];
493
            }
494 21
        }
495 21
        return true;
496
    }
497
498
    /**
499
     * Build the array structure for value.
500
     *
501
     * @param $route
502
     * @param null $data
503
     * @return array|null
504
     */
505 9
    private function buildArray($route, $data = null)
506
    {
507 9
        $key  = array_pop($route);
508 9
        $data = [$key => $data];
509 9
        if (count($route) == 0) {
510 9
            return $data;
511
        }
512 6
        return $this->buildArray($route, $data);
513
    }
514
515
    /**
516
     * Remove a value identified from the key
517
     *
518
     * @param $array
519
     * @param $key
520
     */
521 6
    private function removeValue(&$array, $key)
522
    {
523 6
        $keys = explode('.', $key);
524
525 6
        while (count($keys) > 1) {
526 6
            $key = array_shift($keys);
527
528 6
            if ( ! isset($array[$key]) || ! is_array($array[$key])) {
529 3
                return;
530
            }
531
532 3
            $array =& $array[$key];
533 3
        }
534
535 6
        unset($array[array_shift($keys)]);
536 6
    }
537
}
538