Completed
Pull Request — master (#925)
by Asmir
02:40
created

JsonDeserializationVisitor   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Test Coverage

Coverage 80.23%

Importance

Changes 0
Metric Value
dl 0
loc 177
ccs 69
cts 86
cp 0.8023
rs 9.2
c 0
b 0
f 0
wmc 34

16 Methods

Rating   Name   Duplication   Size   Complexity  
C visitArray() 0 36 7
A setCurrentObject() 0 4 1
A visitDouble() 0 3 1
A revertCurrentObject() 0 3 1
A getResult() 0 3 1
A getCurrentObject() 0 3 1
A endVisitingObject() 0 6 1
C prepare() 0 25 7
A startVisitingObject() 0 3 1
A visitBoolean() 0 3 1
A visitNull() 0 2 1
A visitDiscriminatorMapProperty() 0 10 2
A visitInteger() 0 3 1
A __construct() 0 6 1
B visitProperty() 0 23 6
A visitString() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright 2016 Johannes M. Schmitt <[email protected]>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
namespace JMS\Serializer;
22
23
use JMS\Serializer\Accessor\AccessorStrategyInterface;
24
use JMS\Serializer\Exception\LogicException;
25
use JMS\Serializer\Exception\RuntimeException;
26
use JMS\Serializer\Metadata\ClassMetadata;
27
use JMS\Serializer\Metadata\PropertyMetadata;
28
29
class JsonDeserializationVisitor extends AbstractVisitor implements DeserializationVisitorInterface
30
{
31
    private $options = 0;
32
    private $depth = 512;
33
34
    private $objectStack;
35
    private $currentObject;
36
37 65
    public function __construct(
38
        int $options = 0, int $depth = 512)
39
    {
40 65
        $this->objectStack = new \SplStack;
41 65
        $this->options = $options;
42 65
        $this->depth = $depth;
43 65
    }
44
45 2
    public function visitNull($data, array $type): void
46
    {
47
48 2
    }
49
50 24
    public function visitString($data, array $type): string
51
    {
52 24
        return (string)$data;
53
    }
54
55 5
    public function visitBoolean($data, array $type): bool
56
    {
57 5
        return (bool)$data;
58
    }
59
60 9
    public function visitInteger($data, array $type): int
61
    {
62 9
        return (int)$data;
63
    }
64
65 15
    public function visitDouble($data, array $type): float
66
    {
67 15
        return (double)$data;
68
    }
69
70 19
    public function visitArray($data, array $type): array
71
    {
72 19
        if (!\is_array($data)) {
73
            throw new RuntimeException(sprintf('Expected array, but got %s: %s', \gettype($data), json_encode($data)));
74
        }
75
76
        // If no further parameters were given, keys/values are just passed as is.
77 19
        if (!$type['params']) {
78 4
            return $data;
79
        }
80
81 15
        switch (\count($type['params'])) {
82 15
            case 1: // Array is a list.
83 13
                $listType = $type['params'][0];
84
85 13
                $result = array();
86
87 13
                foreach ($data as $v) {
88 12
                    $result[] = $this->navigator->accept($v, $listType);
89
                }
90
91 13
                return $result;
92
93 5
            case 2: // Array is a map.
94 5
                list($keyType, $entryType) = $type['params'];
95
96 5
                $result = array();
97
98 5
                foreach ($data as $k => $v) {
99 5
                    $result[$this->navigator->accept($k, $keyType)] = $this->navigator->accept($v, $entryType);
100
                }
101
102 5
                return $result;
103
104
            default:
105
                throw new RuntimeException(sprintf('Array type cannot have more than 2 parameters, but got %s.', json_encode($type['params'])));
106
        }
107
    }
108
109 6
    public function visitDiscriminatorMapProperty($data, ClassMetadata $metadata): string
110
    {
111 6
        if (isset($data[$metadata->discriminatorFieldName])) {
112 5
            return (string)$data[$metadata->discriminatorFieldName];
113
        }
114
115 1
        throw new LogicException(sprintf(
116 1
            'The discriminator field name "%s" for base-class "%s" was not found in input data.',
117 1
            $metadata->discriminatorFieldName,
118 1
            $metadata->name
119
        ));
120
    }
121
122 30
    public function startVisitingObject(ClassMetadata $metadata, object $object, array $type): void
123
    {
124 30
        $this->setCurrentObject($object);
125 30
    }
126
127 30
    public function visitProperty(PropertyMetadata $metadata, $data)
128
    {
129 30
        $name = $metadata->serializedName;
130
131 30
        if (null === $data) {
132
            return;
133
        }
134
135 30
        if (!\is_array($data)) {
136 1
            throw new RuntimeException(sprintf('Invalid data "%s"(%s), expected "%s".', $data, $metadata->type['name'], $metadata->reflection->class));
137
        }
138
139 30
        if (!array_key_exists($name, $data)) {
140 3
            return;
141
        }
142
143 28
        if (!$metadata->type) {
144
            throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->class, $metadata->name));
145
        }
146
147 28
        $v = $data[$name] !== null ? $this->navigator->accept($data[$name], $metadata->type) : null;
148
149 28
        return $v;
150
    }
151
152 29
    public function endVisitingObject(ClassMetadata $metadata, $data, array $type): object
153
    {
154 29
        $obj = $this->currentObject;
155 29
        $this->revertCurrentObject();
156
157 29
        return $obj;
158
    }
159
160 61
    public function getResult($data)
161
    {
162 61
        return $data;
163
    }
164
165 30
    public function setCurrentObject($object)
166
    {
167 30
        $this->objectStack->push($this->currentObject);
168 30
        $this->currentObject = $object;
169 30
    }
170
171
    public function getCurrentObject()
172
    {
173
        return $this->currentObject;
174
    }
175
176 29
    public function revertCurrentObject()
177
    {
178 29
        return $this->currentObject = $this->objectStack->pop();
179
    }
180
181 63
    public function prepare($str)
182
    {
183 63
        $decoded = json_decode($str, true, $this->depth, $this->options);
184
185 63
        switch (json_last_error()) {
186 63
            case JSON_ERROR_NONE:
187 63
                return $decoded;
188
189
            case JSON_ERROR_DEPTH:
190
                throw new RuntimeException('Could not decode JSON, maximum stack depth exceeded.');
191
192
            case JSON_ERROR_STATE_MISMATCH:
193
                throw new RuntimeException('Could not decode JSON, underflow or the nodes mismatch.');
194
195
            case JSON_ERROR_CTRL_CHAR:
196
                throw new RuntimeException('Could not decode JSON, unexpected control character found.');
197
198
            case JSON_ERROR_SYNTAX:
199
                throw new RuntimeException('Could not decode JSON, syntax error - malformed JSON.');
200
201
            case JSON_ERROR_UTF8:
202
                throw new RuntimeException('Could not decode JSON, malformed UTF-8 characters (incorrectly encoded?)');
203
204
            default:
205
                throw new RuntimeException('Could not decode JSON.');
206
        }
207
    }
208
}
209