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 ( 643d67...78595b )
by Sergey
02:43
created

DataParser::parseInt()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 2
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 13
    public function setPath($path)
67
    {
68 13
        $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 1
        }
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 7
            } else {
125 1
                throw new \InvalidArgumentException(
126 1
                    sprintf("Value expected to be a string, but %s given", gettype($value)),
127
                    400
128 1
                );
129
            }
130 7
        }
131 7
        $this->restorePath();
132
133 7
        return $pathValue;
134
    }
135
136
    /**
137
     * @inheritdoc
138
     */
139 2
    public function parseInt($data, $path)
140
    {
141 2
        return $this->parseNumeric($data, $path, 'int');
142
    }
143
144
    /**
145
     * @inheritdoc
146
     */
147 2
    public function parseFloat($data, $path)
148
    {
149 2
        return $this->parseNumeric($data, $path, 'float');
150
    }
151
152
    /**
153
     * @inheritdoc
154
     */
155 2
    public function parseBool($data, $path)
156
    {
157 2
        $this->setPath($path);
158
159 2
        $pathValue = null;
160 2
        if ($this->hasValue($data, $path)) {
161 2
            $value = $this->getValue($data, $path);
162 2
            if ((null === $value) || (is_bool($value))) {
163 2
                $pathValue = $value;
164 2
            } elseif (is_string($value)) {
165 1
                $pathValue = (in_array($value, ['true', 'yes', 'y', 'on', 'enabled'])) ? true : false;
166 1
            } elseif (is_numeric($value)) {
167 1
                $pathValue = (bool) $value;
168 1
            } else {
169 1
                throw new \InvalidArgumentException(
170 1
                    sprintf("Value expected to be a boolean, but %s given", gettype($value)),
171
                    400
172 1
                );
173
            }
174 2
        }
175
176 2
        $this->restorePath();
177
178 2
        return $pathValue;
179
    }
180
181
    /**
182
     * @inheritdoc
183
     */
184 2
    public function parseDateTime($data, $path, $format = 'Y-m-d')
185
    {
186 2
        $this->setPath($path);
187
188 2
        $pathValue = null;
189 2
        if ($this->hasValue($data, $path)) {
190 2
            $value = $this->getValue($data, $path);
191 2
            if (null === $value) {
192
                $pathValue = $value;
193 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...
194 2
                if (is_string($value)) {
195 2
                    $pathValue = \DateTimeImmutable::createFromFormat($format, $value);
196 2
                }
197
198 2
                if (!$pathValue instanceof \DateTimeImmutable) {
199 1
                    throw new \InvalidArgumentException(
200 1
                        sprintf("Value expected to be a date/time string in '%s' format", $format),
201
                        400
202 1
                    );
203
                }
204
            }
205 2
        }
206
207 2
        $this->restorePath();
208
209 2
        return $pathValue;
210
    }
211
212
    /**
213
     * @inheritdoc
214
     */
215 1
    public function parseArray($data, $path, $itemsParser)
216
    {
217 1
        $this->setPath($path);
218
219 1
        $pathValue = null;
220 1
        if ($this->hasValue($data, $path)) {
221 1
            $value = $this->getValue($data, $path);
222 1
            if ((null !== $value) && (false === is_array($value))) {
223 1
                throw new \InvalidArgumentException(
224 1
                    sprintf("Value expected to be an array, but %s given", gettype($value)),
225
                    400
226 1
                );
227 1
            } elseif (is_array($value)) {
228 1
                $pathValue = [];
229 1
                $keys = array_keys($value);
230 1
                foreach ($keys as $key) {
231 1
                    $this->setPath($key);
232
233 1
                    $pathValue[$key] = $this->parseArrayItem($value, $key, $itemsParser);
234
235 1
                    $this->restorePath();
236 1
                }
237 1
            }
238 1
        }
239
240 1
        $this->restorePath();
241
242 1
        return $pathValue;
243
    }
244
245
    /**
246
     * @inheritdoc
247
     */
248 1
    public function parseResource($data, $path, $resType)
249
    {
250 1
        $this->setPath($path);
251
252 1
        $pathValue = null;
253 1
        if ($this->hasValue($data, $path)) {
254 1
            $this->setPath($path);
255
256 1
            $decoder = $this->factory->getResourceDecoder($resType);
257 1
            $pathValue = $decoder->decode($this->getValue($data, $path), $this);
258
259 1
            $this->restorePath();
260 1
        }
261
262 1
        return $pathValue;
263
    }
264
265
    /**
266
     * @inheritdoc
267
     */
268 2
    public function parseDocument($data, $docType)
269
    {
270
        try {
271 2
            $this->initPathStack();
272
            
273 2
            $decoder = $this->factory->getDocumentDecoder($docType);
274
275 2
            return $decoder->decode($data, $this);
276 1
        } catch (JsonApiException $e) {
277
            throw $e;
278 1
        } catch (\Exception $e) {
279 1
            $status = $e->getCode();
280 1
            $message = 'Failed to parse document';
281 1
            if (empty($status)) {
282 1
                $message = 'Internal server error';
283 1
                $status = 500;
284 1
            }
285
286 1
            $error = new Error(
287 1
                rand(),
288 1
                null,
289 1
                $status,
290 1
                self::ERROR_CODE,
291 1
                $message,
292 1
                $e->getMessage(),
293 1
                ['pointer' => $this->getPath()]
294 1
            );
295
296 1
            throw new JsonApiException($error, $status, $e);
297
        }
298
    }
299
300
    /**
301
     * @inheritdoc
302
     */
303
    public function parseQueryParams($data, $paramsType)
304
    {
305
        throw new \RuntimeException('Not implemented');
306
    }
307
308
    /**
309
     * Prepare path segment
310
     *
311
     * @param string $path
312
     * @return string
313
     */
314 12
    protected function preparePathSegment($path)
315
    {
316 12
        return trim(preg_replace('~[\/]+~si', '/', str_replace(['.', '[', ']'], '/', (string) $path)), '/');
317
    }
318
319
    /**
320
     * Initialize stack that store current path
321
     */
322 14
    protected function initPathStack()
323
    {
324 14
        $this->path = new \SplStack();
325 14
    }
326
327
    /**
328
     * Parse single array item
329
     *
330
     * @param object|array $value
331
     * @param string $key
332
     * @param string|\Closure $itemsParser
333
     * @return mixed
334
     */
335 1
    protected function parseArrayItem($value, $key, $itemsParser)
336
    {
337 1
        $arrayPath = sprintf('[%s]', $key);
338
339 1
        if (is_string($itemsParser)) {
340
            switch ($itemsParser) {
341 1
                case 'string':
342 1
                    return $this->parseString($value, $arrayPath);
343
344 1
                case 'int':
345 1
                case 'integer':
346 1
                    return $this->parseInt($value, $arrayPath);
347
348 1
                case 'float':
349 1
                case 'double':
350 1
                    return $this->parseFloat($value, $arrayPath);
351
352 1
                case 'bool':
353 1
                case 'boolean':
354 1
                    return $this->parseBool($value, $arrayPath);
355
356 1
                case 'date':
357 1
                    return $this->parseDateTime($value, $arrayPath);
358
359 1
                case 'datetime':
360 1
                    return $this->parseDateTime($value, $arrayPath, 'Y-m-d H:i:s');
361
362 1
                case 'time':
363 1
                    return $this->parseDateTime($value, $arrayPath, 'H:i:s');
364
365
                default:
366
                    throw new \InvalidArgumentException(
367
                        sprintf("Unknown array items parser '%s' specified", $itemsParser),
368
                        500
369
                    );
370
            }
371 1
        } elseif ($itemsParser instanceof \Closure) {
372 1
            return $itemsParser($value, $arrayPath, $this);
373
        } else {
374
            throw new \InvalidArgumentException(
375
                sprintf(
376
                    "Array items parser must be a string or \\Closure, but %s given",
377
                    gettype($itemsParser)
378
                ),
379
                500
380
            );
381
        }
382
    }
383
384
    /**
385
     * Parse numeric value
386
     *
387
     * @param mixed $data
388
     * @param string $path
389
     * @param string $type
390
     * @return float|int|null
391
     */
392 3
    protected function parseNumeric($data, $path, $type)
393
    {
394 3
        $this->setPath($path);
395
396 3
        $pathValue = null;
397 3
        if ($this->hasValue($data, $path)) {
398 3
            $value = $this->getValue($data, $path);
399 3
            $rightType = ('int' === $type) ? is_int($value) : is_float($value);
400 3
            if ($rightType) {
401 3
                $pathValue = $value;
402 3
            } elseif (is_numeric($pathValue)) {
403
                $pathValue = ('int' === $type) ? (int) $value : (float) $value;
404 2
            } elseif (null !== $value) {
405 2
                throw new \InvalidArgumentException(
406 2
                    sprintf("Value expected to be %s, but %s given", $type, gettype($value)),
407
                    400
408 2
                );
409
            }
410 3
        }
411
412 3
        $this->restorePath();
413
414 3
        return $pathValue;
415
    }
416
}