Passed
Pull Request — master (#1549)
by
unknown
02:34
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
        $this->setCurrentObject($object);
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function visitProperty(PropertyMetadata $metadata, $data)
178
    {
179
        $name = $metadata->serializedName;
180
181
        if (null === $data) {
182
            return;
183
        }
184
185
        if (!\is_array($data)) {
186
            throw new RuntimeException(sprintf('Invalid data %s (%s), expected "%s".', json_encode($data), $metadata->type['name'], $metadata->class));
187
        }
188
189
        if (true === $metadata->inline) {
190
            if (!$metadata->type) {
191
                throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name);
192
            }
193
194
            return $this->navigator->accept($data, $metadata->type);
195
        }
196
197
        if (!array_key_exists($name, $data)) {
198
            throw new NotAcceptableException();
199
        }
200
201
        if (!$metadata->type) {
202
            throw RuntimeException::noMetadataForProperty($metadata->class, $metadata->name);
203
        }
204
205
        return null !== $data[$name] ? $this->navigator->accept($data[$name], $metadata->type) : null;
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211
    public function endVisitingObject(ClassMetadata $metadata, $data, array $type): object
212
    {
213
        $obj = $this->currentObject;
214
        $this->revertCurrentObject();
215
216
        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...
217
    }
218
219
    /**
220
     * {@inheritdoc}
221
     */
222
    public function getResult($data)
223
    {
224
        unset($this->navigator);
225
226
        return $data;
227
    }
228
229
    public function setCurrentObject(object $object): void
230
    {
231
        $this->objectStack->push($this->currentObject);
232
        $this->currentObject = $object;
233
    }
234
235
    public function getCurrentObject(): ?object
236
    {
237
        return $this->currentObject;
238
    }
239
240
    public function revertCurrentObject(): ?object
241
    {
242
        return $this->currentObject = $this->objectStack->pop();
243
    }
244
245
    /**
246
     * {@inheritdoc}
247
     */
248
    public function prepare($str)
249
    {
250
        $decoded = json_decode($str, true, $this->depth, $this->options);
251
252
        switch (json_last_error()) {
253
            case JSON_ERROR_NONE:
254
                return $decoded;
255
256
            case JSON_ERROR_DEPTH:
257
                throw new RuntimeException('Could not decode JSON, maximum stack depth exceeded.');
258
259
            case JSON_ERROR_STATE_MISMATCH:
260
                throw new RuntimeException('Could not decode JSON, underflow or the nodes mismatch.');
261
262
            case JSON_ERROR_CTRL_CHAR:
263
                throw new RuntimeException('Could not decode JSON, unexpected control character found.');
264
265
            case JSON_ERROR_SYNTAX:
266
                throw new RuntimeException('Could not decode JSON, syntax error - malformed JSON.');
267
268
            case JSON_ERROR_UTF8:
269
                throw new RuntimeException('Could not decode JSON, malformed UTF-8 characters (incorrectly encoded?)');
270
271
            default:
272
                throw new RuntimeException('Could not decode JSON.');
273
        }
274
    }
275
}
276