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 ( 642c1a...b5d278 )
by Sergey
12s
created

DataParser::parseResourceOrObject()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.3244

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 8
cts 11
cp 0.7272
rs 9.7333
c 0
b 0
f 0
cc 4
nc 4
nop 4
crap 4.3244
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
namespace Reva2\JsonApi\Decoders;
12
13
use Neomerx\JsonApi\Contracts\Encoder\Parameters\EncodingParametersInterface;
14
use Neomerx\JsonApi\Document\Error;
15
use Neomerx\JsonApi\Exceptions\JsonApiException;
16
use Reva2\JsonApi\Contracts\Decoders\CallbackResolverInterface;
17
use Reva2\JsonApi\Contracts\Decoders\DataParserInterface;
18
use Reva2\JsonApi\Contracts\Decoders\Mapping\ClassMetadataInterface;
19
use Reva2\JsonApi\Contracts\Decoders\Mapping\DocumentMetadataInterface;
20
use Reva2\JsonApi\Contracts\Decoders\Mapping\Factory\MetadataFactoryInterface;
21
use Reva2\JsonApi\Contracts\Decoders\Mapping\ObjectMetadataInterface;
22
use Reva2\JsonApi\Contracts\Decoders\Mapping\PropertyMetadataInterface;
23
use Reva2\JsonApi\Contracts\Decoders\Mapping\ResourceMetadataInterface;
24
use Symfony\Component\PropertyAccess\PropertyAccess;
25
use Symfony\Component\PropertyAccess\PropertyAccessor;
26
27
/**
28
 * Data parser
29
 *
30
 * @package Reva2\JsonApi\Decoders
31
 * @author Sergey Revenko <[email protected]>
32
 */
33
class DataParser implements DataParserInterface
34
{
35
    const ERROR_CODE = 'ee2c1d49-ba40-4077-a6bb-b06baceb3e97';
36
37
    /**
38
     * Current path
39
     *
40
     * @var \SplStack
41
     */
42
    protected $path;
43
44
    /**
45
     * Resource decoders factory
46
     *
47
     * @var MetadataFactoryInterface
48
     */
49
    protected $factory;
50
51
    /**
52
     * @var PropertyAccessor
53
     */
54
    protected $accessor;
55
56
    /**
57
     * @var CallbackResolverInterface
58
     */
59
    protected $callbackResolver;
60
61
    /**
62
     * Serialization groups
63
     *
64
     * @var string[]
65
     */
66
    protected $serializationGroups = ['Default'];
67
68
    /**
69
     * Constructor
70
     *
71
     * @param MetadataFactoryInterface $factory
72
     * @param CallbackResolverInterface $callbackResolver
73
     */
74 19
    public function __construct(MetadataFactoryInterface $factory, CallbackResolverInterface $callbackResolver)
75
    {
76 19
        $this->factory = $factory;
77 19
        $this->callbackResolver = $callbackResolver;
78 19
        $this->accessor = PropertyAccess::createPropertyAccessorBuilder()
79 19
            ->enableExceptionOnInvalidIndex()
80 19
            ->getPropertyAccessor();
81
        
82 19
        $this->initPathStack();
83 19
    }
84
85
    /**
86
     * @inheritdoc
87
     */
88 14
    public function setPath($path)
89
    {
90 14
        $this->path->push($this->preparePathSegment($path));
91
92 14
        return $this;
93
    }
94
95
    /**
96
     * @inheritdoc
97
     */
98 14
    public function restorePath()
99
    {
100 14
        $this->path->pop();
101
102 14
        return $this;
103
    }
104
105
    /**
106
     * @return string[]
107
     */
108 3
    public function getSerializationGroups()
109
    {
110 3
        return $this->serializationGroups;
111
    }
112
113
    /**
114
     * @param string[] $serializationGroups
115
     * @return $this
116
     */
117 3
    public function setSerializationGroups(array $serializationGroups)
118
    {
119 3
        $this->serializationGroups = $serializationGroups;
120
121 3
        return $this;
122
    }
123
124
    /**
125
     * @inheritdoc
126
     */
127 3
    public function getPath()
128
    {
129 2
        $segments = [];
130 2
        foreach ($this->path as $segment) {
131 2
            $segments[] = $segment;
132 2
        }
133
134 3
        return '/' . implode('/', array_reverse($segments));
135
    }
136
137
    /**
138
     * @inheritdoc
139
     */
140 14
    public function hasValue($data, $path)
141
    {
142 14
        return $this->accessor->isReadable($data, $path);
143
    }
144
145
    /**
146
     * @inheritdoc
147
     */
148 14
    public function getValue($data, $path)
149
    {
150 14
        return $this->accessor->getValue($data, $path);
151
    }
152
153
    /**
154
     * @inheritdoc
155
     */
156 8
    public function parseString($data, $path)
157
    {
158 8
        $this->setPath($path);
159
160 8
        $pathValue = null;
161 8
        if ($this->hasValue($data, $path)) {
162 8
            $value = $this->getValue($data, $path);
163 8
            if ((null === $value) || (is_string($value))) {
164 8
                $pathValue = $value;
165 8
            } else {
166 1
                throw new \InvalidArgumentException(
167 1
                    sprintf("Value expected to be a string, but %s given", gettype($value)),
168
                    400
169 1
                );
170
            }
171 8
        }
172 8
        $this->restorePath();
173
174 8
        return $pathValue;
175
    }
176
177
    /**
178
     * @inheritdoc
179
     */
180 4
    public function parseInt($data, $path)
181
    {
182 4
        return $this->parseNumeric($data, $path, 'int');
183
    }
184
185
    /**
186
     * @inheritdoc
187
     */
188 2
    public function parseFloat($data, $path)
189
    {
190 2
        return $this->parseNumeric($data, $path, 'float');
191
    }
192
193
    /**
194
     * @inheritdoc
195
     */
196 1
    public function parseRaw($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...
197
    {
198 1
        $this->setPath($path);
199
200 1
        $pathValue = null;
201 1
        if ($this->hasValue($data, $path)) {
202 1
            $pathValue = $this->getValue($data, $path);
203 1
        }
204
205 1
        $this->restorePath();
206
207 1
        return $pathValue;
208
    }
209
210
    /**
211
     * @inheritdoc
212
     */
213 4
    public function parseCallback($data, $path, $callback)
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...
214
    {
215 4
        $this->setPath($path);
216
217 4
        $pathValue = null;
218 4
        if ($this->hasValue($data, $path)) {
219 4
            $pathValue = call_user_func($callback, $this->getValue($data, $path));
220 4
        }
221
222 4
        $this->restorePath();
223
224 4
        return $pathValue;
225
    }
226
227
    /**
228
     * @inheritdoc
229
     */
230 2
    public function parseBool($data, $path)
231
    {
232 2
        $this->setPath($path);
233
234 2
        $pathValue = null;
235 2
        if ($this->hasValue($data, $path)) {
236 2
            $value = $this->getValue($data, $path);
237 2
            if ((null === $value) || (is_bool($value))) {
238 1
                $pathValue = $value;
239 2
            } elseif (is_string($value)) {
240 2
                $pathValue = (in_array($value, ['true', 'yes', 'y', 'on', 'enabled'])) ? true : false;
241 2
            } elseif (is_numeric($value)) {
242 1
                $pathValue = (bool) $value;
243 1
            } else {
244 1
                throw new \InvalidArgumentException(
245 1
                    sprintf("Value expected to be a boolean, but %s given", gettype($value)),
246
                    400
247 1
                );
248
            }
249 2
        }
250
251 2
        $this->restorePath();
252
253 2
        return $pathValue;
254
    }
255
256
    /**
257
     * @inheritdoc
258
     */
259 2
    public function parseDateTime($data, $path, $format = 'Y-m-d')
260
    {
261 2
        $this->setPath($path);
262
263 2
        $pathValue = null;
264 2
        if ($this->hasValue($data, $path)) {
265 2
            $value = $this->getValue($data, $path);
266 2
            if (null !== $value) {
267 2
                if (is_string($value)) {
268 2
                    $pathValue = \DateTimeImmutable::createFromFormat($format, $value);
269 2
                }
270
271 2
                if (!$pathValue instanceof \DateTimeImmutable) {
272 1
                    throw new \InvalidArgumentException(
273 1
                        sprintf("Value expected to be a date/time string in '%s' format", $format),
274
                        400
275 1
                    );
276
                }
277 2
            }
278 2
        }
279
280 2
        $this->restorePath();
281
282 2
        return $pathValue;
283
    }
284
285
    /**
286
     * @inheritdoc
287
     */
288 4
    public function parseArray($data, $path, \Closure $itemsParser)
289
    {
290 4
        $this->setPath($path);
291
292 4
        $pathValue = null;
293 4
        if ($this->hasValue($data, $path)) {
294 4
            $value = $this->getValue($data, $path);
295 4 View Code Duplication
            if (false === is_array($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...
296 1
                throw new \InvalidArgumentException(
297 1
                    sprintf("Value expected to be an array, but %s given", gettype($value)),
298
                    400
299 1
                );
300
            }
301
302 4
            $pathValue = [];
303 4
            $keys = array_keys($value);
304 4
            foreach ($keys as $key) {
305 4
                $arrayPath = sprintf("[%s]", $key);
306
307 4
                $pathValue[$key] = $itemsParser($value, $arrayPath, $this);
308 3
            }
309 3
        }
310
311 3
        $this->restorePath();
312
313 3
        return $pathValue;
314
    }
315
316
    /**
317
     * Parse data object value at specified path as object of specified class
318
     *
319
     * @param array|object $data
320
     * @param string $path
321
     * @param string $objType
322
     * @return null
323
     */
324 2
    public function parseObject($data, $path, $objType)
325
    {
326 2
        $this->setPath($path);
327
328 2
        $pathValue = null;
329 2
        if ((true === $this->hasValue($data, $path)) &&
330 2
            (null !== ($value = $this->getValue($data, $path)))
331 2
        ) {
332 2
            $this->restorePath();
333
334 2
            $pathValue = $this->parseObjectValue($value, $objType);
335 2
        }
336
337 2
        return $pathValue;
338
    }
339
340
    /**
341
     * @inheritdoc
342
     */
343 5
    public function parseResource($data, $path, $resType, $loader = null)
344
    {
345 5
        $this->setPath($path);
346
347 5
        $pathValue = null;
348 5
        if ((true === $this->hasValue($data, $path)) &&
349 5
            (null !== ($value = $this->getValue($data, $path)))
350 5
        ) {
351 5
            $metadata = $this->factory->getMetadataFor($resType);
352
            /* @var $metadata ResourceMetadataInterface */
353
354 5
            $name = $this->parseString($value, 'type');
355 5
            if ($name !== $metadata->getName()) {
356 1
                throw new \InvalidArgumentException(
357 1
                    sprintf("Value must contain resource of type '%s'", $metadata->getName()),
358
                    409
359 1
                );
360
            }
361
362 5
            $pathValue = null;
363
364 5
            if ($loader === null) {
365 5
                $loader = $metadata->getLoader();
366 5
            }
367
368 5
            if ((null !== $loader) && (true === $this->hasValue($value, 'id'))) {
369
                $callback = $this->callbackResolver->resolveCallback($loader);
370
                $pathValue = call_user_func($callback, $this->getValue($value, 'id'), $metadata);
371
            }
372
373 5
            if (null === $pathValue) {
374 5
                $discClass = $this->getClassByDiscriminator($metadata, $value);
375 4
                if ((null !== $discClass) && ($discClass !== $resType)) {
376 4
                    $metadata = $this->factory->getMetadataFor($discClass);
377 4
                }
378
379 4
                $objClass = $metadata->getClassName();
380 4
                $pathValue = new $objClass();
381
382 4
                if (null !== ($idMetadata = $metadata->getIdMetadata())) {
383 4
                    $this->parseProperty($value, $pathValue, $idMetadata);
384 4
                }
385 4
            } else {
386
                $valueClass = get_class($pathValue);
387
                if ($valueClass !== $resType) {
388
                    $metadata = $this->factory->getMetadataFor($valueClass);
389
                }
390
            }
391
392 4
            foreach ($metadata->getAttributes() as $attribute) {
393 4
                $this->parseProperty($value, $pathValue, $attribute);
394 4
            }
395
396 4
            foreach ($metadata->getRelationships() as $relationship) {
397 4
                $this->parseProperty($value, $pathValue, $relationship);
398 3
            }
399 3
        }
400
401 3
        $this->restorePath();
402
403 3
        return $pathValue;
404
    }
405
406
    /**
407
     * @inheritdoc
408
     */
409 4
    public function parseDocument($data, $docType)
410
    {
411
        try {
412 4
            $this->initPathStack();
413
414 4
            $metadata = $this->factory->getMetadataFor($docType);
415 4
            if (!$metadata instanceof DocumentMetadataInterface) {
416 1
                throw new \InvalidArgumentException(sprintf("Failed to parse %s as JSON API document", $docType));
417
            }
418
419
            /* @var $metadata \Reva2\JsonApi\Contracts\Decoders\Mapping\DocumentMetadataInterface */
420
421 3
            $docClass = $metadata->getClassName();
422 3
            $doc = new $docClass();
423
424 3
            $this->parseProperty($data, $doc, $metadata->getContentMetadata());
425
426 2
            $docMetadata = $metadata->getMetadata();
427 2
            if ($docMetadata !== null) {
428 1
                $this->parseProperty($data, $doc, $metadata->getMetadata());
429 1
            }
430
431 2
            return $doc;
432 2
        } catch (JsonApiException $e) {
433
            throw $e;
434 2
        } catch (\Exception $e) {
435 2
            throw  $this->convertToApiException($e, 'document');
436
        }
437
    }
438
439
    /**
440
     * @inheritdoc
441
     */
442 3
    public function parseQueryParams($data, $paramsType)
443
    {
444
        try {
445 3
            $this->initPathStack();
446
447 3
            $query = $this->parseObjectValue($data, $paramsType);
448 3
            if (!$query instanceof EncodingParametersInterface) {
449
                throw new \InvalidArgumentException(sprintf(
450
                    "Query parameters object must implement interface %s",
451
                    EncodingParametersInterface::class
452
                ));
453
            }
454
455 3
            return $query;
456
        } catch (JsonApiException $e) {
457
            throw $e;
458
        } catch (\Exception $e) {
459
            throw  $this->convertToApiException($e, 'query');
460
        }
461
    }
462
463
    /**
464
     * Prepare path segment
465
     *
466
     * @param string $path
467
     * @return string
468
     */
469 14
    protected function preparePathSegment($path)
470
    {
471 14
        return trim(preg_replace('~[\/]+~si', '/', str_replace(['.', '[', ']'], '/', (string) $path)), '/');
472
    }
473
474
    /**
475
     * Initialize stack that store current path
476
     */
477 19
    protected function initPathStack()
478
    {
479 19
        $this->path = new \SplStack();
480 19
    }
481
482
    /**
483
     * Parse numeric value
484
     *
485
     * @param mixed $data
486
     * @param string $path
487
     * @param string $type
488
     * @return float|int|null
489
     */
490 5
    protected function parseNumeric($data, $path, $type)
491
    {
492 5
        $this->setPath($path);
493
494 5
        $pathValue = null;
495 5
        if ($this->hasValue($data, $path)) {
496 5
            $value = $this->getValue($data, $path);
497 5
            $rightType = ('int' === $type) ? is_int($value) : is_float($value);
498 5
            if ($rightType) {
499 4
                $pathValue = $value;
500 5
            } elseif (is_numeric($value)) {
501 4
                $pathValue = ('int' === $type) ? (int) $value : (float) $value;
502 4 View Code Duplication
            } elseif (null !== $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...
503 2
                throw new \InvalidArgumentException(
504 2
                    sprintf("Value expected to be %s, but %s given", $type, gettype($value)),
505
                    400
506 2
                );
507
            }
508 5
        }
509
510 5
        $this->restorePath();
511
512 5
        return $pathValue;
513
    }
514
515
    /**
516
     * Parse property of specified object
517
     *
518
     * @param object|array $data
519
     * @param object $obj
520
     * @param PropertyMetadataInterface $metadata
521
     */
522 7
    private function parseProperty($data, $obj, PropertyMetadataInterface $metadata)
523
    {
524 7
        $path = $metadata->getDataPath();
525
526 7
        if ((false === $this->hasValue($data, $path)) ||
527 7
            (true === $this->isExcludedProperty($metadata))
528 7
        ) {
529 6
            return;
530
        }
531
532 7
        if ('custom' === $metadata->getDataType()) {
533 4
            $value = $this->parseCallback($data, $path, [$obj, $metadata->getDataTypeParams()]);
534 4
        } else {
535 6
            $value = $this->parsePropertyValue($data, $path, $metadata);
536
        }
537
538 7
        if (null !== ($converter = $metadata->getConverter())) {
539 3
            $callback = $this->callbackResolver->resolveCallback($converter);
540
541 3
            $value = call_user_func($callback, $value);
542 3
        }
543
544 7
        $setter = $metadata->getSetter();
545 7
        if (null !== $setter) {
546 6
            $obj->{$setter}($value);
547 6
        } else {
548 5
            $setter = $metadata->getPropertyName();
549 5
            $obj->{$setter} = $value;
550
        }
551 7
    }
552
553
    /**
554
     * Parse value of specified property
555
     *
556
     * @param object|array $data
557
     * @param string $path
558
     * @param PropertyMetadataInterface $metadata
559
     * @return mixed|null
560
     */
561 6
    private function parsePropertyValue($data, $path, PropertyMetadataInterface $metadata)
562
    {
563 6
        switch ($metadata->getDataType()) {
564 6
            case 'scalar':
565 6
                return $this->parseScalarValue($data, $path, $metadata->getDataTypeParams());
566
567 5
            case 'datetime':
568 1
                $format = $metadata->getDataTypeParams();
569 1
                if (empty($format)) {
570 1
                    $format = 'Y-m-d';
571 1
                }
572
573 1
                return $this->parseDateTime($data, $path, $format);
574
575 5
            case 'array':
576 3
                return $this->parseArrayValue($data, $path, $metadata->getDataTypeParams(), $metadata);
577
578 5
            case 'object':
579 5
                return $this->parseResourceOrObject($data, $path, $metadata->getDataTypeParams(), $metadata);
580
581 1
            case 'raw':
582 1
                return $this->parseRaw($data, $path);
583
584
            default:
585
                throw new \InvalidArgumentException(sprintf(
586
                    "Unsupported property data type '%s'",
587
                    $metadata->getDataType()
588
                ));
589
        }
590
    }
591
592
    /**
593
     * Parse value as JSON API resource or object
594
     *
595
     * @param object|array $data
596
     * @param string $path
597
     * @param string $objClass
598
     * @param PropertyMetadataInterface $propMetadata
599
     * @return mixed|null
600
     */
601 5
    public function parseResourceOrObject($data, $path, $objClass, PropertyMetadataInterface $propMetadata)
602
    {
603 5
        $objMetadata = $this->factory->getMetadataFor($objClass);
604 5
        if ($objMetadata instanceof ResourceMetadataInterface) {
605 4
            $loader = null;
606 4
            foreach ($propMetadata->getLoaders() as $group => $groupLoader) {
607
                if (in_array($group, $this->serializationGroups)) {
608
                    $loader = $groupLoader;
609
                }
610 4
            }
611
612 4
            return $this->parseResource($data, $path, $objClass, $loader);
613
        }
614
615 2
        return $this->parseObject($data, $path, $objClass);
616
    }
617
618
    /**
619
     * Parse value that contains JSON API object
620
     *
621
     * @param object|array $data
622
     * @param string $objType
623
     * @return mixed
624
     */
625 5
    public function parseObjectValue($data, $objType)
626
    {
627 5
        $metadata = $this->factory->getMetadataFor($objType);
628 5
        if (!$metadata instanceof ObjectMetadataInterface) {
629
            throw new \InvalidArgumentException('Invalid object metadata');
630
        }
631
632 5
        $discClass = $this->getClassByDiscriminator($metadata, $data);
633 5
        if ((null !== $discClass) && ($discClass !== $objType)) {
634 1
            return $this->parseObjectValue($data, $discClass);
635
        }
636
637 5
        $objClass = $metadata->getClassName();
638 5
        $obj = new $objClass();
639
640 5
        $properties = $metadata->getProperties();
641 5
        foreach ($properties as $property) {
642 5
            $this->parseProperty($data, $obj, $property);
643 5
        }
644
645 5
        return $obj;
646
    }
647
648
    /**
649
     * Parse value that contains array
650
     *
651
     * @param object|array $data
652
     * @param string $path
653
     * @param array $params
654
     * @param PropertyMetadataInterface $propMetadata
655
     * @return array|null
656
     */
657 3
    public function parseArrayValue($data, $path, array $params, PropertyMetadataInterface $propMetadata)
658
    {
659 3
        $type = $params[0];
660 3
        $typeParams = $params[1];
661
662
        switch ($type) {
663 3
            case 'scalar':
664 1
                return $this->parseArray(
665 1
                    $data,
666 1
                    $path,
667
                    function ($data, $path, DataParser $parser) use ($typeParams) {
668 1
                        return $parser->parseScalarValue($data, $path, $typeParams);
669
                    }
670 1
                );
671
672 3
            case 'datetime':
673 1
                $format = (!empty($typeParams)) ? $typeParams : 'Y-m-d';
674 1
                return $this->parseArray(
675 1
                    $data,
676 1
                    $path,
677
                    function ($data, $path, DataParser $parser) use ($format) {
678 1
                        return $parser->parseDateTime($data, $path, $format);
679
                    }
680 1
                );
681
682 3
            case 'object':
683 3
                return $this->parseArray(
684 3
                    $data,
685 3
                    $path,
686
                    function ($data, $path, DataParser $parser) use ($typeParams, $propMetadata) {
687 3
                        return $parser->parseResourceOrObject($data, $path, $typeParams, $propMetadata);
688
                    }
689 3
                );
690
691 1
            case 'array':
692 1
                return $this->parseArray(
693 1
                    $data,
694 1
                    $path,
695
                    function ($data, $path, DataParser $parser) use ($typeParams, $propMetadata) {
696 1
                        return $parser->parseArrayValue($data, $path, $typeParams, $propMetadata);
697
                    }
698 1
                );
699
700 1
            case 'raw':
701 1
                return $this->parseArray(
702 1
                    $data,
703 1
                    $path,
704 1
                    function ($data, $path, DataParser $parser) {
705 1
                        return $parser->parseRaw($data, $path);
706
                    }
707 1
                );
708
709
            default:
710
                throw new \InvalidArgumentException(sprintf(
711
                    "Unsupported array item type '%s' specified",
712
                    $type
713
                ));
714
        }
715
    }
716
717
    /**
718
     * Parse scalar value
719
     *
720
     * @param object|array $data
721
     * @param string $path
722
     * @param string $type
723
     * @return bool|float|int|null|string
724
     */
725 6
    public function parseScalarValue($data, $path, $type)
726
    {
727
        switch ($type) {
728 6
            case 'string':
729 5
                return $this->parseString($data, $path);
730
731 3
            case 'bool':
732 3
            case 'boolean':
733 1
                return $this->parseBool($data, $path);
734
735 3
            case 'int':
736 3
            case 'integer':
737 3
                return $this->parseInt($data, $path);
738
739 1
            case 'float':
740 1
            case 'double':
741 1
                return $this->parseFloat($data, $path);
742
743
            default:
744
                throw new \InvalidArgumentException(sprintf("Unsupported scalar type '%s' specified", $type));
745
        }
746
    }
747
748
    /**
749
     * Convert any exception to JSON API exception
750
     *
751
     * @param \Exception $e
752
     * @param string $objType
753
     * @return JsonApiException
754
     */
755 2
    private function convertToApiException(\Exception $e, $objType)
756
    {
757 2
        $status = $e->getCode();
758 2
        $message = 'Failed to parse request';
759 2
        if (empty($status)) {
760 1
            $message = 'Internal server error';
761 1
            $status = 500;
762 1
        }
763
764 2
        $source = null;
765
        switch ($objType) {
766 2
            case 'document':
767 2
                $source = ['pointer' => $this->getPath()];
768 2
                break;
769
770
            case 'query':
771
                $source = ['parameter' => $this->getPath()];
772
                break;
773
        }
774
775 2
        $error = new Error(rand(), null, $status, self::ERROR_CODE, $message, $e->getMessage(), $source);
776
777 2
        return new JsonApiException($error, $status, $e);
778
    }
779
780
    /**
781
     * Returns appropriate discriminator class for specified data
782
     *
783
     * @param ClassMetadataInterface $metadata
784
     * @param array|object $data
785
     * @return string|null
786
     */
787 8
    private function getClassByDiscriminator(ClassMetadataInterface $metadata, $data)
788
    {
789 8
        if (null === ($discField = $metadata->getDiscriminatorField())) {
790 6
            return null;
791
        }
792
793 6
        $discValue = $this->parseString($data, $discField->getDataPath());
794 6
        if (empty($discValue)) {
795 1
            $this->setPath($discField->getDataPath());
796
797 1
            throw new \InvalidArgumentException("Field value required and can not be empty", 422);
798
        }
799
800 5
        return $metadata->getDiscriminatorClass($discValue);
801
    }
802
803
    /**
804
     * Check if specified property should be excluded
805
     *
806
     * @param PropertyMetadataInterface $metadata
807
     * @return bool
808
     */
809 7
    private function isExcludedProperty(PropertyMetadataInterface $metadata)
810
    {
811 7
        $propertyGroups = $metadata->getGroups();
812 7
        foreach ($propertyGroups as $group) {
813 7
            if (in_array($group, $this->serializationGroups)) {
814 7
                return false;
815
            }
816
        }
817
818
        return true;
819
    }
820
}
821