Passed
Pull Request — master (#1549)
by
unknown
02:43
created

getRequireAllRequiredProperties()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
ccs 0
cts 1
cp 0
crap 2
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
    /**
37
     * @var bool
38
     */
39 5
    private $requireAllRequiredProperties;
40
41 5
    public function __construct(
42
        int $options = 0,
43
        int $depth = 512,
44 9
        bool $requireAllRequiredProperties = false
45
    ) {
46 9
        $this->objectStack = new \SplStack();
47
        $this->options = $options;
48
        $this->depth = $depth;
49 15
        $this->requireAllRequiredProperties = $requireAllRequiredProperties;
50
    }
51 15
52
    public function setRequireAllRequiredProperties(bool $requireAllRequiredProperties): void
53
    {
54 19
        $this->requireAllRequiredProperties = $requireAllRequiredProperties;
55
    }
56 19
57
    public function getRequireAllRequiredProperties(): bool
58
    {
59
        return $this->requireAllRequiredProperties;
60
    }
61 19
62 4
    /**
63
     * {@inheritdoc}
64
     */
65 15
    public function visitNull($data, array $type)
66 15
    {
67 13
        return null;
68
    }
69 13
70
    /**
71 13
     * {@inheritdoc}
72 12
     */
73
    public function visitString($data, array $type): string
74
    {
75 13
        $this->assertValueCanBeCastToString($data);
76
77 5
        return (string) $data;
78 5
    }
79
80 5
    /**
81
     * {@inheritdoc}
82 5
     */
83 5
    public function visitBoolean($data, array $type): bool
84
    {
85
        return (bool) $data;
86 5
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function visitInteger($data, array $type): int
92
    {
93 6
        $this->assertValueCanBeCastToInt($data);
94
95 6
        return (int) $data;
96 5
    }
97
98
    /**
99 1
     * {@inheritdoc}
100 1
     */
101 1
    public function visitDouble($data, array $type): float
102 1
    {
103
        $this->assertValueCanCastToFloat($data);
104
105
        return (float) $data;
106 33
    }
107
108 33
    /**
109 33
     * {@inheritdoc}
110
     */
111 33
    public function visitArray($data, array $type): array
112
    {
113 33
        if (!\is_array($data)) {
114
            throw new RuntimeException(sprintf('Expected array, but got %s: %s', \gettype($data), json_encode($data)));
115 33
        }
116
117
        // If no further parameters were given, keys/values are just passed as is.
118
        if (!$type['params']) {
119 33
            return $data;
120 1
        }
121
122
        switch (\count($type['params'])) {
123 33
            case 1: // Array is a list.
124 3
                $listType = $type['params'][0];
125
126
                $result = [];
127 31
128
                foreach ($data as $v) {
129
                    $result[] = $this->navigator->accept($v, $listType);
130
                }
131 31
132
                return $result;
133 31
134
            case 2: // Array is a map.
135
                [$keyType, $entryType] = $type['params'];
136 32
137
                $result = [];
138 32
139 32
                foreach ($data as $k => $v) {
140
                    $result[$this->navigator->accept($k, $keyType)] = $this->navigator->accept($v, $entryType);
141 32
                }
142
143
                return $result;
144 64
145
            default:
146 64
                throw new RuntimeException(sprintf('Array type cannot have more than 2 parameters, but got %s.', json_encode($type['params'])));
147
        }
148
    }
149 33
150
    /**
151 33
     * {@inheritdoc}
152 33
     */
153 33
    public function visitDiscriminatorMapProperty($data, ClassMetadata $metadata): string
154
    {
155
        if (isset($data[$metadata->discriminatorFieldName])) {
156
            return (string) $data[$metadata->discriminatorFieldName];
157
        }
158
159
        throw new LogicException(sprintf(
160 32
            'The discriminator field name "%s" for base-class "%s" was not found in input data.',
161
            $metadata->discriminatorFieldName,
162 32
            $metadata->name,
163
        ));
164
    }
165 66
166
    /**
167 66
     * {@inheritdoc}
168
     */
169 66
    public function startVisitingObject(ClassMetadata $metadata, object $object, array $type): void
170 66
    {
171 66
        $cur = $this->getCurrentObject() ? get_class($this->getCurrentObject()) : 'null';
0 ignored issues
show
Unused Code introduced by
The assignment to $cur is dead and can be removed.
Loading history...
172
        $objtype = $object ? get_class($object) : 'null';
0 ignored issues
show
introduced by
$object is of type object, thus it always evaluated to true.
Loading history...
Unused Code introduced by
The assignment to $objtype is dead and can be removed.
Loading history...
173
        $stacksize = $this->objectStack->count();
0 ignored issues
show
Unused Code introduced by
The assignment to $stacksize is dead and can be removed.
Loading history...
174
        $this->setCurrentObject($object);
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    public function visitProperty(PropertyMetadata $metadata, $data)
181
    {
182
        $name = $metadata->serializedName;
183
184
        if (null === $data) {
185
            return;
186
        }
187
188
        if (!\is_array($data)) {
189
            throw new RuntimeException(sprintf('Invalid data %s (%s), expected "%s".', json_encode($data), $metadata->type['name'], $metadata->class));
190
        }
191
192
        if (true === $metadata->inline) {
193
            if (!$metadata->type) {
194
                throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name);
195
            }
196
197
            return $this->navigator->accept($data, $metadata->type);
198
        }
199
200
        if (!array_key_exists($name, $data)) {
201
            throw new NotAcceptableException();
202
        }
203
204
        if (!$metadata->type) {
205
            throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name);
206
        }
207
208
        return null !== $data[$name] ? $this->navigator->accept($data[$name], $metadata->type) : null;
209
    }
210
211
    /**
212
     * {@inheritdoc}
213
     */
214
    public function endVisitingObject(ClassMetadata $metadata, $data, array $type): object
215
    {
216
        $obj = $this->currentObject;
217
        $prevObj = $this->objectStack->top();
218
        $prevObjType = $prevObj ? get_class($prevObj) : 'null';
0 ignored issues
show
Unused Code introduced by
The assignment to $prevObjType is dead and can be removed.
Loading history...
219
220
        $this->revertCurrentObject();
221
        $stacksize = $this->objectStack->count();
0 ignored issues
show
Unused Code introduced by
The assignment to $stacksize is dead and can be removed.
Loading history...
222
223
        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...
224
    }
225
226
    /**
227
     * {@inheritdoc}
228
     */
229
    public function getResult($data)
230
    {
231
        unset($this->navigator);
232
233
        return $data;
234
    }
235
236
    public function setCurrentObject(object $object): void
237
    {
238
        $this->objectStack->push($this->currentObject);
239
        $this->currentObject = $object;
240
    }
241
242
    public function getCurrentObject(): ?object
243
    {
244
        return $this->currentObject;
245
    }
246
247
    public function revertCurrentObject(): ?object
248
    {
249
        return $this->currentObject = $this->objectStack->pop();
250
    }
251
252
    /**
253
     * {@inheritdoc}
254
     */
255
    public function prepare($str)
256
    {
257
        $decoded = json_decode($str, true, $this->depth, $this->options);
258
259
        switch (json_last_error()) {
260
            case JSON_ERROR_NONE:
261
                return $decoded;
262
263
            case JSON_ERROR_DEPTH:
264
                throw new RuntimeException('Could not decode JSON, maximum stack depth exceeded.');
265
266
            case JSON_ERROR_STATE_MISMATCH:
267
                throw new RuntimeException('Could not decode JSON, underflow or the nodes mismatch.');
268
269
            case JSON_ERROR_CTRL_CHAR:
270
                throw new RuntimeException('Could not decode JSON, unexpected control character found.');
271
272
            case JSON_ERROR_SYNTAX:
273
                throw new RuntimeException('Could not decode JSON, syntax error - malformed JSON.');
274
275
            case JSON_ERROR_UTF8:
276
                throw new RuntimeException('Could not decode JSON, malformed UTF-8 characters (incorrectly encoded?)');
277
278
            default:
279
                throw new RuntimeException('Could not decode JSON.');
280
        }
281
    }
282
}
283