Passed
Pull Request — master (#1553)
by
unknown
02:33
created

UnionHandler::isPrimitiveType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
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
        $finalType = null;
71
        if (1 === count($type['params'])) {
72
            if ($data[$type['params'][0]]) {
73
                $lookupField = $type['params'][0];
74
75
                if (!array_key_exists($lookupField, $data)) {
76
                    throw new NonVisitableTypeException('Union Discriminator Field \'' . $lookupField . '\' not found in data');
77
                }
78
79
                $lkup = $data[$lookupField];
80
                $finalType = [
81
                    'name' => $lkup,
82
                    'params' => [],
83
                ];
84
            }
85
        } elseif (2 === count($type['params'])) {
86
            if (is_array($type['params'][1]) && !array_key_exists('name', $type['params'][1])) {
87
                $lookupField = $type['params'][0];
88
                $unionMap = $type['params'][1];
89
90
                if (!array_key_exists($lookupField, $data)) {
91
                    throw new NonVisitableTypeException('Union Discriminator Field \'' . $lookupField . '\' not found in data');
92
                }
93
94
                $lkup = $data[$lookupField];
95
                if (!empty($unionMap)) {
96
                    if (array_key_exists($lkup, $unionMap)) {
97
                        $finalType = [
98
                            'name' => $unionMap[$lkup],
99
                            'params' => [],
100
                        ];
101
                    } else {
102
                        throw new NonVisitableTypeException('Union Discriminator Map does not contain key \'' . $lkup . '\'');
103
                    }
104
                } else {
105
                    $finalType = [
106
                        'name' => $lkup,
107
                        'params' => [],
108
                    ];
109
                }
110
            }
111
        }
112
113
        if (null !== $finalType && null !== $finalType['name']) {
114
            return $context->getNavigator()->accept($data, $finalType);
115
        } else {
116
            foreach ($type['params'] as $possibleType) {
117
                $finalType = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $finalType is dead and can be removed.
Loading history...
118
119
                if ($this->isPrimitiveType($possibleType['name']) && $this->testPrimitive($data, $possibleType['name'], $context->getFormat())) {
120
                    return $context->getNavigator()->accept($data, $possibleType);
121
                }
122
            }
123
        }
124
125
        return null;
126
    }
127
128
    private function matchSimpleType(mixed $data, array $type, Context $context): mixed
129
    {
130
        foreach ($type['params'] as $possibleType) {
131
            if ($this->isPrimitiveType($possibleType['name']) && !$this->testPrimitive($data, $possibleType['name'], $context->getFormat())) {
132
                continue;
133
            }
134
135
            try {
136
                return $context->getNavigator()->accept($data, $possibleType);
137
            } catch (NonVisitableTypeException $e) {
138
                continue;
139
            }
140
        }
141
142
        return null;
143
    }
144
145
    private function isPrimitiveType(string $type): bool
146
    {
147
        return in_array($type, ['int', 'integer', 'float', 'double', 'bool', 'boolean', 'string']);
148
    }
149
150
    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

150
    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...
151
    {
152
        switch ($type) {
153
            case 'integer':
154
            case 'int':
155
                return (string) (int) $data === (string) $data;
156
157
            case 'double':
158
            case 'float':
159
                return (string) (float) $data === (string) $data;
160
161
            case 'bool':
162
            case 'boolean':
163
                return (string) (bool) $data === (string) $data;
164
165
            case 'string':
166
                return (string) $data === (string) $data;
167
        }
168
169
        return false;
170
    }
171
}
172