Passed
Pull Request — master (#1553)
by
unknown
03:06
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 (2 === count($type['params'])) {
72
            if (!is_array($type['params'][0]) || !array_key_exists('name', $type['params'][0])) {
73
                $lookupField = $type['params'][0];
74
                $unionMap = $type['params'][1];
75
76
                if (!array_key_exists($lookupField, $data)) {
77
                    throw new NonVisitableTypeException('Union Discriminator Field \'' . $lookupField . '\' not found in data');
78
                }
79
80
                $lkup = $data[$lookupField];
81
                if (!empty($unionMap)) {
82
                    if (array_key_exists($lkup, $unionMap)) {
83
                        $finalType = [
84
                            'name' => $unionMap[$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) {
103
                $finalType = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $finalType is dead and can be removed.
Loading history...
104
105
                if ($this->isPrimitiveType($possibleType['name']) && $this->testPrimitive($data, $possibleType['name'], $context->getFormat())) {
106
                    return $context->getNavigator()->accept($data, $possibleType);
107
                }
108
            }
109
        }
110
111
        return null;
112
    }
113
114
    private function matchSimpleType(mixed $data, array $type, Context $context): mixed
115
    {
116
        foreach ($type['params'] as $possibleType) {
117
            if ($this->isPrimitiveType($possibleType['name']) && !$this->testPrimitive($data, $possibleType['name'], $context->getFormat())) {
118
                continue;
119
            }
120
121
            try {
122
                return $context->getNavigator()->accept($data, $possibleType);
123
            } catch (NonVisitableTypeException $e) {
124
                continue;
125
            }
126
        }
127
128
        return null;
129
    }
130
131
    private function isPrimitiveType(string $type): bool
132
    {
133
        return in_array($type, ['int', 'integer', 'float', 'double', 'bool', 'boolean', 'string']);
134
    }
135
136
    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

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