Completed
Push — master ( 4dc94a...3c2d00 )
by Marcin
28s queued 24s
created

UnionHandler::testPrimitive()   C

Complexity

Conditions 13
Paths 14

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 19
nc 14
nop 2
dl 0
loc 29
rs 6.6166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer\Handler;
6
7
use JMS\Serializer\DeserializationContext;
8
use JMS\Serializer\Exception\NonVisitableTypeException;
9
use JMS\Serializer\Exception\NotAcceptableException;
10
use JMS\Serializer\Exception\RuntimeException;
11
use JMS\Serializer\GraphNavigatorInterface;
12
use JMS\Serializer\SerializationContext;
13
use JMS\Serializer\Type\Type;
14
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
15
use JMS\Serializer\Visitor\SerializationVisitorInterface;
16
17
/**
18
 * @phpstan-import-type TypeArray from Type
19
 */
20
final class UnionHandler implements SubscribingHandlerInterface
21
{
22
    private static $aliases = ['boolean' => 'bool', 'integer' => 'int', 'double' => 'float'];
23
24
    /**
25
     * {@inheritdoc}
26
     */
27
    public static function getSubscribingMethods()
28
    {
29
        $methods = [];
30
        $formats = ['json', 'xml'];
31
32
        foreach ($formats as $format) {
33
            $methods[] = [
34
                'type' => 'union',
35
                'format' => $format,
36
                'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION,
37
                'method' => 'deserializeUnion',
38
            ];
39
            $methods[] = [
40
                'type' => 'union',
41
                'format' => $format,
42
                'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
43
                'method' => 'serializeUnion',
44
            ];
45
        }
46
47
        return $methods;
48
    }
49
50
    /**
51
     * @param TypeArray $type
0 ignored issues
show
Bug introduced by
The type JMS\Serializer\Handler\TypeArray was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
52
     */
53
    public function serializeUnion(
54
        SerializationVisitorInterface $visitor,
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

54
        /** @scrutinizer ignore-unused */ SerializationVisitorInterface $visitor,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
55
        mixed $data,
56
        array $type,
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

56
        /** @scrutinizer ignore-unused */ array $type,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
57
        SerializationContext $context
58
    ): mixed {
59
        if ($this->isPrimitiveType(gettype($data))) {
60
            $resolvedType = [
61
                'name' => gettype($data),
62
                'params' => [],
63
            ];
64
        } else {
65
            $resolvedType = [
66
                'name' => get_class($data),
67
                'params' => [],
68
            ];
69
        }
70
71
        return $context->getNavigator()->accept($data, $resolvedType);
72
    }
73
74
    /**
75
     * @param TypeArray $type
76
     */
77
    public function deserializeUnion(DeserializationVisitorInterface $visitor, mixed $data, array $type, DeserializationContext $context): mixed
0 ignored issues
show
Unused Code introduced by
The parameter $visitor is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

77
    public function deserializeUnion(/** @scrutinizer ignore-unused */ DeserializationVisitorInterface $visitor, mixed $data, array $type, DeserializationContext $context): mixed

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
78
    {
79
        if ($data instanceof \SimpleXMLElement) {
80
            throw new RuntimeException('XML deserialisation into union types is not supported yet.');
81
        }
82
83
        if (3 === count($type['params'])) {
84
            $lookupField = $type['params'][1];
85
            if (empty($data[$lookupField])) {
86
                throw new NonVisitableTypeException(sprintf('Union Discriminator Field "%s" not found in data', $lookupField));
87
            }
88
89
            $unionMap = $type['params'][2];
90
            $lookupValue = $data[$lookupField];
91
            if (empty($unionMap[$lookupValue])) {
92
                throw new NonVisitableTypeException(sprintf('Union Discriminator Map does not contain key "%s"', $lookupValue));
93
            }
94
95
            $finalType = [
96
                'name' => $unionMap[$lookupValue],
97
                'params' => [],
98
            ];
99
100
            return $context->getNavigator()->accept($data, $finalType);
101
        }
102
103
        $dataType = gettype($data);
104
105
        if (
106
            array_filter(
107
                $type['params'][0],
108
                static fn (array $type): bool => $type['name'] === $dataType || (isset(self::$aliases[$dataType]) && $type['name'] === self::$aliases[$dataType]),
109
            )
110
        ) {
111
            return $context->getNavigator()->accept($data, [
112
                'name' => $dataType,
113
                'params' => [],
114
            ]);
115
        }
116
117
        foreach ($type['params'][0] as $possibleType) {
118
            if ($this->isPrimitiveType($possibleType['name']) && $this->testPrimitive($data, $possibleType['name'])) {
119
                return $context->getNavigator()->accept($data, $possibleType);
120
            }
121
        }
122
123
        throw new NotAcceptableException();
124
    }
125
126
    private function isPrimitiveType(string $type): bool
127
    {
128
        return in_array($type, ['int', 'integer', 'float', 'double', 'bool', 'boolean', 'true', 'false', 'string', 'array'], true);
129
    }
130
131
    private function testPrimitive(mixed $data, string $type): bool
132
    {
133
        switch ($type) {
134
            case 'array':
135
                return is_array($data);
136
137
            case 'integer':
138
            case 'int':
139
                return (string) (int) $data === (string) $data;
140
141
            case 'double':
142
            case 'float':
143
                return (string) (float) $data === (string) $data;
144
145
            case 'bool':
146
            case 'boolean':
147
                return !is_array($data) && (string) (bool) $data === (string) $data;
148
149
            case 'true':
150
                return true === $data;
151
152
            case 'false':
153
                return false === $data;
154
155
            case 'string':
156
                return !is_array($data) && !is_object($data);
157
        }
158
159
        return false;
160
    }
161
}
162