Passed
Pull Request — master (#1553)
by
unknown
11:34
created

UnionHandler::determineType()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 3
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer\Handler;
6
7
use JMS\Serializer\Context;
8
use JMS\Serializer\DeserializationContext;
9
use JMS\Serializer\Exception\NonVisitableTypeException;
10
use JMS\Serializer\Exception\RuntimeException;
11
use JMS\Serializer\GraphNavigatorInterface;
12
use JMS\Serializer\SerializationContext;
13
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
14
use JMS\Serializer\Visitor\SerializationVisitorInterface;
15
16
final class UnionHandler implements SubscribingHandlerInterface
17
{
18
    private static $aliases = ['boolean' => 'bool', 'integer' => 'int', 'double' => 'float'];
0 ignored issues
show
introduced by
The private property $aliases is not used, and could be removed.
Loading history...
19
20
    /**
21
     * {@inheritdoc}
22
     */
23
    public static function getSubscribingMethods()
24
    {
25
        $methods = [];
26
        $formats = ['json', 'xml'];
27
28
        foreach ($formats as $format) {
29
            $methods[] = [
30
                'type' => 'union',
31
                'format' => $format,
32
                'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION,
33
                'method' => 'deserializeUnion',
34
            ];
35
            $methods[] = [
36
                'type' => 'union',
37
                'format' => $format,
38
                'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
39
                'method' => 'serializeUnion',
40
            ];
41
        }
42
43
        return $methods;
44
    }
45
46
    public function serializeUnion(
47
        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

47
        /** @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...
48
        mixed $data,
49
        array $type,
50
        SerializationContext $context
51
    ): mixed {
52
        if ($this->isPrimitiveType(gettype($data))) {
53
            return $this->matchSimpleType($data, $type, $context);
54
        } else {
55
            $resolvedType = [
56
                'name' => get_class($data),
57
                'params' => [],
58
            ];
59
60
            return $context->getNavigator()->accept($data, $resolvedType);
61
        }
62
    }
63
64
    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

64
    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...
65
    {
66
        if ($data instanceof \SimpleXMLElement) {
67
            throw new RuntimeException('XML deserialisation into union types is not supported yet.');
68
        }
69
70
        foreach ($type['params'] as $possibleType) {
71
            $finalType = null;
72
73
            if (!$context->getMetadataStack()->isEmpty()) {
74
                $propertyMetadata = $context->getMetadataStack()->top();
75
                if (null !== $propertyMetadata->unionDiscriminatorField) {
76
                    if (!array_key_exists($propertyMetadata->unionDiscriminatorField, $data)) {
77
                        throw new NonVisitableTypeException('Union Discriminator Field \'' . $propertyMetadata->unionDiscriminatorField . '\' not found in data');
78
                    }
79
80
                    $lkup = $data[$propertyMetadata->unionDiscriminatorField];
81
                    if (!empty($propertyMetadata->unionDiscriminatorMap)) {
82
                        if (array_key_exists($lkup, $propertyMetadata->unionDiscriminatorMap)) {
83
                            $finalType = [
84
                                'name' => $propertyMetadata->unionDiscriminatorMap[$lkup],
85
                                'params' => [],
86
                            ];
87
                        } else {
88
                            throw new NonVisitableTypeException('Union Discriminator Map does not contain key \'' . $lkup . '\'');
89
                        }
90
                    } else {
91
                        $finalType = [
92
                            'name' => $lkup,
93
                            'params' => [],
94
                        ];
95
                    }
96
                }
97
            }
98
99
            if (null !== $finalType && null !== $finalType['name']) {
100
                return $context->getNavigator()->accept($data, $finalType);
101
            } else {
102
                foreach ($type['params'] as $possibleType) {
0 ignored issues
show
Comprehensibility Bug introduced by
$possibleType is overwriting a variable from outer foreach loop.
Loading history...
103
                    if ($this->isPrimitiveType($possibleType['name']) && $this->testPrimitive($data, $possibleType['name'], $context->getFormat())) {
104
                        return $context->getNavigator()->accept($data, $possibleType);
105
                    }
106
                }
107
            }
108
        }
109
110
        return null;
111
    }
112
113
    private function matchSimpleType(mixed $data, array $type, Context $context): mixed
114
    {
115
        foreach ($type['params'] as $possibleType) {
116
            if ($this->isPrimitiveType($possibleType['name']) && !$this->testPrimitive($data, $possibleType['name'], $context->getFormat())) {
117
                continue;
118
            }
119
120
            try {
121
                return $context->getNavigator()->accept($data, $possibleType);
122
            } catch (NonVisitableTypeException $e) {
123
                continue;
124
            }
125
        }
126
127
        return null;
128
    }
129
130
    private function isPrimitiveType(string $type): bool
131
    {
132
        return in_array($type, ['int', 'integer', 'float', 'double', 'bool', 'boolean', 'string']);
133
    }
134
135
    private function testPrimitive(mixed $data, string $type, string $format): bool
0 ignored issues
show
Unused Code introduced by
The parameter $format 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

135
    private function testPrimitive(mixed $data, string $type, /** @scrutinizer ignore-unused */ string $format): bool

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...
136
    {
137
        switch ($type) {
138
            case 'integer':
139
            case 'int':
140
                return (string) (int) $data === (string) $data;
141
142
            case 'double':
143
            case 'float':
144
                return (string) (float) $data === (string) $data;
145
146
            case 'bool':
147
            case 'boolean':
148
                return (string) (bool) $data === (string) $data;
149
150
            case 'string':
151
                return (string) $data === (string) $data;
152
        }
153
154
        return false;
155
    }
156
}
157