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.
Completed
Push — master ( d96736...643d67 )
by Sergey
03:19
created

DataParser::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
crap 1
1
<?php
2
/*
3
 * This file is part of the reva2/jsonapi.
4
 *
5
 * (c) Sergey Revenko <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
12
namespace Reva2\JsonApi\Decoders;
13
14
use Neomerx\JsonApi\Document\Error;
15
use Neomerx\JsonApi\Exceptions\JsonApiException;
16
use Reva2\JsonApi\Contracts\Decoders\DataParserInterface;
17
use Reva2\JsonApi\Contracts\Decoders\DecodersFactoryInterface;
18
use Symfony\Component\PropertyAccess\PropertyAccess;
19
use Symfony\Component\PropertyAccess\PropertyAccessor;
20
21
/**
22
 * Data parser
23
 *
24
 * @package Reva2\JsonApi\Decoders
25
 * @author Sergey Revenko <[email protected]>
26
 */
27
class DataParser implements DataParserInterface
28
{
29
    const ERROR_CODE = 'ee2c1d49-ba40-4077-a6bb-b06baceb3e97';
30
31
    /**
32
     * Current path
33
     *
34
     * @var \SplStack
35
     */
36
    protected $path;
37
38
    /**
39
     * Resource decoders factory
40
     *
41
     * @var DecodersFactoryInterface
42
     */
43
    protected $factory;
44
45
    /**
46
     * @var PropertyAccessor
47
     */
48
    protected $accessor;
49
50
    /**
51
     * Constructor
52
     *
53
     * @param DecodersFactoryInterface $factory
54
     */
55 14
    public function __construct(DecodersFactoryInterface $factory)
56
    {
57 14
        $this->factory = $factory;
58 14
        $this->accessor = PropertyAccess::createPropertyAccessor();
59
        
60 14
        $this->initPathStack();
61 14
    }
62
63
    /**
64
     * @inheritdoc
65
     */
66 12
    public function setPath($path)
67
    {
68 12
        $this->path->push($this->preparePathSegment($path));
69
70 12
        return $this;
71
    }
72
73
    /**
74
     * @inheritdoc
75
     */
76 12
    public function restorePath()
77
    {
78 12
        $this->path->pop();
79
80 12
        return $this;
81
    }
82
83
    /**
84
     * @inheritdoc
85
     */
86 1
    public function getPath()
87
    {
88 1
        $segments = [];
89 1
        foreach ($this->path as $segment) {
90
            $segments[] = $segment;
91
        }
92
93 1
        return '/' . implode('/', array_reverse($segments));
94
    }
95
96
    /**
97
     * @inheritdoc
98
     */
99 12
    public function hasValue($data, $path)
100
    {
101 12
        return $this->accessor->isReadable($data, $path);
102
    }
103
104
    /**
105
     * @inheritdoc
106
     */
107 12
    public function getValue($data, $path)
108
    {
109 12
        return $this->accessor->getValue($data, $path);
110
    }
111
112
    /**
113
     * @inheritdoc
114
     */
115 7
    public function parseString($data, $path)
116
    {
117 7
        $this->setPath($path);
118
119 7
        $pathValue = null;
120 7
        if ($this->hasValue($data, $path)) {
121 7
            $value = $this->getValue($data, $path);
122 7 View Code Duplication
            if ((null === $value) || (is_string($value))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
123 7
                $pathValue = $value;
124
            } else {
125 1
                throw new \InvalidArgumentException(
126 1
                    sprintf("Value expected to be a string, but %s given", gettype($value)),
127 1
                    400
128
                );
129
            }
130
        }
131 7
        $this->restorePath();
132
133 7
        return $pathValue;
134
    }
135
136
    /**
137
     * @inheritdoc
138
     */
139 2 View Code Duplication
    public function parseInt($data, $path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
    {
141 2
        $this->setPath($path);
142
143 2
        $pathValue = null;
144 2
        if ($this->hasValue($data, $path)) {
145 2
            $value = $this->getValue($data, $path);
146 2
            if ((null === $value) || (is_int($value))) {
147 2
                $pathValue = $value;
148 1
            } elseif (is_numeric($value)) {
149 1
                $pathValue = (int) $value;
150
            } else {
151 1
                throw new \InvalidArgumentException(
152 1
                    sprintf("Value expected to be an integer, but %s given", gettype($value)),
153 1
                    400
154
                );
155
            }
156
        }
157
158 2
        $this->restorePath();
159
160 2
        return $pathValue;
161
    }
162
163
    /**
164
     * @inheritdoc
165
     */
166 2 View Code Duplication
    public function parseFloat($data, $path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
    {
168 2
        $this->setPath($path);
169
170 2
        $pathValue = null;
171 2
        if ($this->hasValue($data, $path)) {
172 2
            $value = $this->getValue($data, $path);
173 2
            if ((null === $value) || (is_float($value))) {
174 2
                $pathValue = $value;
175 1
            } elseif (is_numeric($pathValue)) {
176
                $pathValue = (float) $value;
177
            } else {
178 1
                throw new \InvalidArgumentException(
179 1
                    sprintf("Value expected to be a float, but %s given", gettype($value)),
180 1
                    400
181
                );
182
            }
183
        }
184
185 2
        $this->restorePath();
186
187 2
        return $pathValue;
188
    }
189
190
    /**
191
     * @inheritdoc
192
     */
193 2
    public function parseBool($data, $path)
194
    {
195 2
        $this->setPath($path);
196
197 2
        $pathValue = null;
198 2
        if ($this->hasValue($data, $path)) {
199 2
            $value = $this->getValue($data, $path);
200 2
            if ((null === $value) || (is_bool($value))) {
201 2
                $pathValue = $value;
202 1
            } elseif (is_string($value)) {
203 1
                $pathValue = (in_array($value, ['true', 'yes', 'y', 'on', 'enabled'])) ? true : false;
204 1
            } elseif (is_numeric($value)) {
205 1
                $pathValue = (bool) $value;
206
            } else {
207 1
                throw new \InvalidArgumentException(
208 1
                    sprintf("Value expected to be a boolean, but %s given", gettype($value)),
209 1
                    400
210
                );
211
            }
212
        }
213
214 2
        $this->restorePath();
215
216 2
        return $pathValue;
217
    }
218
219
    /**
220
     * @inheritdoc
221
     */
222 2
    public function parseDateTime($data, $path, $format = 'Y-m-d')
223
    {
224 2
        $this->setPath($path);
225
226 2
        $pathValue = null;
227 2
        if ($this->hasValue($data, $path)) {
228 2
            $value = $this->getValue($data, $path);
229 2
            if (null === $value) {
230
                $pathValue = $value;
231 View Code Duplication
            } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
232 2
                if (is_string($value)) {
233 2
                    $pathValue = \DateTimeImmutable::createFromFormat($format, $value);
234
                }
235
236 2
                if (!$pathValue instanceof \DateTimeImmutable) {
237 1
                    throw new \InvalidArgumentException(
238 1
                        sprintf("Value expected to be a date/time string in '%s' format", $format),
239 1
                        400
240
                    );
241
                }
242
            }
243
        }
244
245 2
        $this->restorePath();
246
247 2
        return $pathValue;
248
    }
249
250
    /**
251
     * @inheritdoc
252
     */
253 1
    public function parseArray($data, $path, $itemsParser)
254
    {
255 1
        $this->setPath($path);
256
257 1
        $pathValue = null;
258 1
        if ($this->hasValue($data, $path)) {
259 1
            $value = $this->getValue($data, $path);
260 1
            if ((null !== $value) && (false === is_array($value))) {
261 1
                throw new \InvalidArgumentException(
262 1
                    sprintf("Value expected to be an array, but %s given", gettype($value)),
263 1
                    400
264
                );
265 1
            } elseif (is_array($value)) {
266 1
                $pathValue = [];
267 1
                $keys = array_keys($value);
268 1
                foreach ($keys as $key) {
269 1
                    $this->setPath($key);
270
271 1
                    $pathValue[$key] = $this->parseArrayItem($value, $key, $itemsParser);
272
273 1
                    $this->restorePath();
274
                }
275
            }
276
        }
277
278 1
        $this->restorePath();
279
280 1
        return $pathValue;
281
    }
282
283
    /**
284
     * @inheritdoc
285
     */
286 1
    public function parseResource($data, $path, $resType)
287
    {
288 1
        $this->setPath($path);
289
290 1
        $pathValue = null;
291 1
        if ($this->hasValue($data, $path)) {
292 1
            $this->setPath($path);
293
294 1
            $decoder = $this->factory->getResourceDecoder($resType);
295 1
            $pathValue = $decoder->decode($this->getValue($data, $path), $this);
296
297 1
            $this->restorePath();
298
        }
299
300 1
        return $pathValue;
301
    }
302
303
    /**
304
     * @inheritdoc
305
     */
306 2
    public function parseDocument($data, $docType)
307
    {
308
        try {
309 2
            $this->initPathStack();
310
            
311 2
            $decoder = $this->factory->getDocumentDecoder($docType);
312
313 2
            return $decoder->decode($data, $this);
314 1
        } catch (JsonApiException $e) {
315
            throw $e;
316 1
        } catch (\Exception $e) {
317 1
            $status = $e->getCode();
318 1
            $message = 'Failed to parse document';
319 1
            if (empty($status)) {
320 1
                $message = 'Internal server error';
321 1
                $status = 500;
322
            }
323
324 1
            $error = new Error(
325
                rand(),
326 1
                null,
327
                $status,
328 1
                self::ERROR_CODE,
329
                $message,
330 1
                $e->getMessage(),
331 1
                ['pointer' => $this->getPath()]
332
            );
333
334 1
            throw new JsonApiException($error, $status, $e);
335
        }
336
    }
337
338
    /**
339
     * @inheritdoc
340
     */
341
    public function parseQueryParams($data, $paramsType)
342
    {
343
        throw new \RuntimeException('Not implemented');
344
    }
345
346
    /**
347
     * Prepare path segment
348
     *
349
     * @param string $path
350
     * @return string
351
     */
352 12
    protected function preparePathSegment($path)
353
    {
354 12
        return trim(preg_replace('~[\/]+~si', '/', str_replace(['.', '[', ']'], '/', (string) $path)), '/');
355
    }
356
357
    /**
358
     * Initialize stack that store current path
359
     */
360 14
    protected function initPathStack()
361
    {
362 14
        $this->path = new \SplStack();
363 14
    }
364
365
    /**
366
     * Parse single array item
367
     *
368
     * @param object|array $value
369
     * @param string $key
370
     * @param string|\Closure $itemsParser
371
     * @return mixed
372
     */
373 1
    protected function parseArrayItem($value, $key, $itemsParser)
374
    {
375 1
        $arrayPath = sprintf('[%s]', $key);
376
377 1
        if (is_string($itemsParser)) {
378
            switch ($itemsParser) {
379 1
                case 'string':
380 1
                    return $this->parseString($value, $arrayPath);
381
382 1
                case 'int':
383 1
                case 'integer':
384 1
                    return $this->parseInt($value, $arrayPath);
385
386 1
                case 'float':
387 1
                case 'double':
388 1
                    return $this->parseFloat($value, $arrayPath);
389
390 1
                case 'bool':
391 1
                case 'boolean':
392 1
                    return $this->parseBool($value, $arrayPath);
393
394 1
                case 'date':
395 1
                    return $this->parseDateTime($value, $arrayPath);
396
397 1
                case 'datetime':
398 1
                    return $this->parseDateTime($value, $arrayPath, 'Y-m-d H:i:s');
399
400 1
                case 'time':
401 1
                    return $this->parseDateTime($value, $arrayPath, 'H:i:s');
402
403
                default:
404
                    throw new \InvalidArgumentException(
405
                        sprintf("Unknown array items parser '%s' specified", $itemsParser),
406
                        500
407
                    );
408
            }
409
        } elseif ($itemsParser instanceof \Closure) {
410 1
            return $itemsParser($value, $arrayPath, $this);
411
        } else {
412
            throw new \InvalidArgumentException(
413
                sprintf(
414
                    "Array items parser must be a string or \\Closure, but %s given",
415
                    gettype($itemsParser)
416
                ),
417
                500
418
            );
419
        }
420
    }
421
}