JsonDeserializationVisitor   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 242
Duplicated Lines 0 %

Test Coverage

Coverage 80.23%

Importance

Changes 0
Metric Value
eloc 81
dl 0
loc 242
ccs 69
cts 86
cp 0.8023
rs 9.52
c 0
b 0
f 0
wmc 36

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A visitNull() 0 3 1
B visitArray() 0 36 7
A visitDouble() 0 5 1
A endVisitingObject() 0 6 1
A startVisitingObject() 0 3 1
A visitBoolean() 0 3 1
A visitDiscriminatorMapProperty() 0 10 2
A visitInteger() 0 5 1
B visitProperty() 0 29 8
A visitString() 0 5 1
A setCurrentObject() 0 4 1
A revertCurrentObject() 0 3 1
A getResult() 0 5 1
A getCurrentObject() 0 3 1
B prepare() 0 25 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer;
6
7
use JMS\Serializer\Exception\LogicException;
8
use JMS\Serializer\Exception\NotAcceptableException;
9
use JMS\Serializer\Exception\RuntimeException;
10
use JMS\Serializer\Metadata\ClassMetadata;
11
use JMS\Serializer\Metadata\PropertyMetadata;
12
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
13
14
final class JsonDeserializationVisitor extends AbstractVisitor implements DeserializationVisitorInterface
15
{
16
    /**
17
     * @var int
18
     */
19
    private $options;
20
21 72
    /**
22
     * @var int
23
     */
24 72
    private $depth;
25 72
26 72
    /**
27 72
     * @var \SplStack
28
     */
29 2
    private $objectStack;
30
31
    /**
32 2
     * @var object|null
33
     */
34 27
    private $currentObject;
35
36 27
    public function __construct(
37
        int $options = 0,
38
        int $depth = 512
39 5
    ) {
40
        $this->objectStack = new \SplStack();
41 5
        $this->options = $options;
42
        $this->depth = $depth;
43
    }
44 9
45
    /**
46 9
     * {@inheritdoc}
47
     */
48
    public function visitNull($data, array $type)
49 15
    {
50
        return null;
51 15
    }
52
53
    /**
54 19
     * {@inheritdoc}
55
     */
56 19
    public function visitString($data, array $type): string
57
    {
58
        $this->assertValueCanBeCastToString($data);
59
60
        return (string) $data;
61 19
    }
62 4
63
    /**
64
     * {@inheritdoc}
65 15
     */
66 15
    public function visitBoolean($data, array $type): bool
67 13
    {
68
        return (bool) $data;
69 13
    }
70
71 13
    /**
72 12
     * {@inheritdoc}
73
     */
74
    public function visitInteger($data, array $type): int
75 13
    {
76
        $this->assertValueCanBeCastToInt($data);
77 5
78 5
        return (int) $data;
79
    }
80 5
81
    /**
82 5
     * {@inheritdoc}
83 5
     */
84
    public function visitDouble($data, array $type): float
85
    {
86 5
        $this->assertValueCanCastToFloat($data);
87
88
        return (float) $data;
89
    }
90
91
    /**
92
     * {@inheritdoc}
93 6
     */
94
    public function visitArray($data, array $type): array
95 6
    {
96 5
        if (!\is_array($data)) {
97
            throw new RuntimeException(sprintf('Expected array, but got %s: %s', \gettype($data), json_encode($data)));
98
        }
99 1
100 1
        // If no further parameters were given, keys/values are just passed as is.
101 1
        if (!$type['params']) {
102 1
            return $data;
103
        }
104
105
        switch (\count($type['params'])) {
106 33
            case 1: // Array is a list.
107
                $listType = $type['params'][0];
108 33
109 33
                $result = [];
110
111 33
                foreach ($data as $v) {
112
                    $result[] = $this->navigator->accept($v, $listType);
113 33
                }
114
115 33
                return $result;
116
117
            case 2: // Array is a map.
118
                [$keyType, $entryType] = $type['params'];
119 33
120 1
                $result = [];
121
122
                foreach ($data as $k => $v) {
123 33
                    $result[$this->navigator->accept($k, $keyType)] = $this->navigator->accept($v, $entryType);
124 3
                }
125
126
                return $result;
127 31
128
            default:
129
                throw new RuntimeException(sprintf('Array type cannot have more than 2 parameters, but got %s.', json_encode($type['params'])));
130
        }
131 31
    }
132
133 31
    /**
134
     * {@inheritdoc}
135
     */
136 32
    public function visitDiscriminatorMapProperty($data, ClassMetadata $metadata): string
137
    {
138 32
        if (isset($data[$metadata->discriminatorFieldName])) {
139 32
            return (string) $data[$metadata->discriminatorFieldName];
140
        }
141 32
142
        throw new LogicException(sprintf(
143
            'The discriminator field name "%s" for base-class "%s" was not found in input data.',
144 64
            $metadata->discriminatorFieldName,
145
            $metadata->name,
146 64
        ));
147
    }
148
149 33
    /**
150
     * {@inheritdoc}
151 33
     */
152 33
    public function startVisitingObject(ClassMetadata $metadata, object $object, array $type): void
153 33
    {
154
        $this->setCurrentObject($object);
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160 32
    public function visitProperty(PropertyMetadata $metadata, $data)
161
    {
162 32
        $name = $metadata->serializedName;
163
164
        if (null === $data) {
165 66
            return;
166
        }
167 66
168
        if (!\is_array($data)) {
169 66
            throw new RuntimeException(sprintf('Invalid data %s (%s), expected "%s".', json_encode($data), $metadata->type['name'], $metadata->class));
170 66
        }
171 66
172
        if (true === $metadata->inline) {
173
            if (!$metadata->type) {
174
                throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name);
175
            }
176
177
            return $this->navigator->accept($data, $metadata->type);
178
        }
179
180
        if (!array_key_exists($name, $data)) {
181
            throw new NotAcceptableException();
182
        }
183
184
        if (!$metadata->type) {
185
            throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name);
186
        }
187
188
        return null !== $data[$name] ? $this->navigator->accept($data[$name], $metadata->type) : null;
189
    }
190
191
    /**
192
     * {@inheritdoc}
193
     */
194
    public function endVisitingObject(ClassMetadata $metadata, $data, array $type): object
195
    {
196
        $obj = $this->currentObject;
197
        $this->revertCurrentObject();
198
199
        return $obj;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $obj could return the type null which is incompatible with the type-hinted return object. Consider adding an additional type-check to rule them out.
Loading history...
200
    }
201
202
    /**
203
     * {@inheritdoc}
204
     */
205
    public function getResult($data)
206
    {
207
        unset($this->navigator);
208
209
        return $data;
210
    }
211
212
    public function setCurrentObject(object $object): void
213
    {
214
        $this->objectStack->push($this->currentObject);
215
        $this->currentObject = $object;
216
    }
217
218
    public function getCurrentObject(): ?object
219
    {
220
        return $this->currentObject;
221
    }
222
223
    public function revertCurrentObject(): ?object
224
    {
225
        return $this->currentObject = $this->objectStack->pop();
226
    }
227
228
    /**
229
     * {@inheritdoc}
230
     */
231
    public function prepare($str)
232
    {
233
        $decoded = json_decode($str, true, $this->depth, $this->options);
234
235
        switch (json_last_error()) {
236
            case JSON_ERROR_NONE:
237
                return $decoded;
238
239
            case JSON_ERROR_DEPTH:
240
                throw new RuntimeException('Could not decode JSON, maximum stack depth exceeded.');
241
242
            case JSON_ERROR_STATE_MISMATCH:
243
                throw new RuntimeException('Could not decode JSON, underflow or the nodes mismatch.');
244
245
            case JSON_ERROR_CTRL_CHAR:
246
                throw new RuntimeException('Could not decode JSON, unexpected control character found.');
247
248
            case JSON_ERROR_SYNTAX:
249
                throw new RuntimeException('Could not decode JSON, syntax error - malformed JSON.');
250
251
            case JSON_ERROR_UTF8:
252
                throw new RuntimeException('Could not decode JSON, malformed UTF-8 characters (incorrectly encoded?)');
253
254
            default:
255
                throw new RuntimeException('Could not decode JSON.');
256
        }
257
    }
258
}
259