Completed
Pull Request — master (#923)
by Asmir
04:23 queued 01:58
created

JsonDeserializationVisitor::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 4
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 1
rs 9.6666
c 0
b 0
f 0
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
        GraphNavigatorInterface $navigator,
39
        DeserializationContext $context,
40
        int $options = 0, int $depth = 512)
41
    {
42 65
        parent::__construct($navigator, $context);
43 65
        $this->objectStack = new \SplStack;
44 65
        $this->options = $options;
45 65
        $this->depth = $depth;
46 65
    }
47
48 2
    public function visitNull($data, array $type): void
49
    {
50
51 2
    }
52
53 24
    public function visitString($data, array $type): string
54
    {
55 24
        return (string)$data;
56
    }
57
58 5
    public function visitBoolean($data, array $type): bool
59
    {
60 5
        return (bool)$data;
61
    }
62
63 9
    public function visitInteger($data, array $type): int
64
    {
65 9
        return (int)$data;
66
    }
67
68 15
    public function visitDouble($data, array $type): float
69
    {
70 15
        return (double)$data;
71
    }
72
73 19
    public function visitArray($data, array $type): array
74
    {
75 19
        if (!\is_array($data)) {
76
            throw new RuntimeException(sprintf('Expected array, but got %s: %s', \gettype($data), json_encode($data)));
77
        }
78
79
        // If no further parameters were given, keys/values are just passed as is.
80 19
        if (!$type['params']) {
81 4
            return $data;
82
        }
83
84 15
        switch (\count($type['params'])) {
85 15
            case 1: // Array is a list.
86 13
                $listType = $type['params'][0];
87
88 13
                $result = array();
89
90 13
                foreach ($data as $v) {
91 12
                    $result[] = $this->navigator->accept($v, $listType, $this->context);
92
                }
93
94 13
                return $result;
95
96 5
            case 2: // Array is a map.
97 5
                list($keyType, $entryType) = $type['params'];
98
99 5
                $result = array();
100
101 5
                foreach ($data as $k => $v) {
102 5
                    $result[$this->navigator->accept($k, $keyType, $this->context)] = $this->navigator->accept($v, $entryType, $this->context);
103
                }
104
105 5
                return $result;
106
107
            default:
108
                throw new RuntimeException(sprintf('Array type cannot have more than 2 parameters, but got %s.', json_encode($type['params'])));
109
        }
110
    }
111
112 6
    public function visitDiscriminatorMapProperty($data, ClassMetadata $metadata): string
113
    {
114 6
        if (isset($data[$metadata->discriminatorFieldName])) {
115 5
            return (string)$data[$metadata->discriminatorFieldName];
116
        }
117
118 1
        throw new LogicException(sprintf(
119 1
            'The discriminator field name "%s" for base-class "%s" was not found in input data.',
120 1
            $metadata->discriminatorFieldName,
121 1
            $metadata->name
122
        ));
123
    }
124
125 30
    public function startVisitingObject(ClassMetadata $metadata, object $object, array $type): void
126
    {
127 30
        $this->setCurrentObject($object);
128 30
    }
129
130 30
    public function visitProperty(PropertyMetadata $metadata, $data)
131
    {
132 30
        $name = $metadata->serializedName;
133
134 30
        if (null === $data) {
135
            return;
136
        }
137
138 30
        if (!\is_array($data)) {
139 1
            throw new RuntimeException(sprintf('Invalid data "%s"(%s), expected "%s".', $data, $metadata->type['name'], $metadata->reflection->class));
140
        }
141
142 30
        if (!array_key_exists($name, $data)) {
143 3
            return;
144
        }
145
146 28
        if (!$metadata->type) {
147
            throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->class, $metadata->name));
148
        }
149
150 28
        $v = $data[$name] !== null ? $this->navigator->accept($data[$name], $metadata->type, $this->context) : null;
151
152 28
        return $v;
153
    }
154
155 29
    public function endVisitingObject(ClassMetadata $metadata, $data, array $type): object
156
    {
157 29
        $obj = $this->currentObject;
158 29
        $this->revertCurrentObject();
159
160 29
        return $obj;
161
    }
162
163 61
    public function getResult($data)
164
    {
165 61
        return $data;
166
    }
167
168 30
    public function setCurrentObject($object)
169
    {
170 30
        $this->objectStack->push($this->currentObject);
171 30
        $this->currentObject = $object;
172 30
    }
173
174
    public function getCurrentObject()
175
    {
176
        return $this->currentObject;
177
    }
178
179 29
    public function revertCurrentObject()
180
    {
181 29
        return $this->currentObject = $this->objectStack->pop();
182
    }
183
184 63
    public function prepare($str)
185
    {
186 63
        $decoded = json_decode($str, true, $this->depth, $this->options);
187
188 63
        switch (json_last_error()) {
189 63
            case JSON_ERROR_NONE:
190 63
                return $decoded;
191
192
            case JSON_ERROR_DEPTH:
193
                throw new RuntimeException('Could not decode JSON, maximum stack depth exceeded.');
194
195
            case JSON_ERROR_STATE_MISMATCH:
196
                throw new RuntimeException('Could not decode JSON, underflow or the nodes mismatch.');
197
198
            case JSON_ERROR_CTRL_CHAR:
199
                throw new RuntimeException('Could not decode JSON, unexpected control character found.');
200
201
            case JSON_ERROR_SYNTAX:
202
                throw new RuntimeException('Could not decode JSON, syntax error - malformed JSON.');
203
204
            case JSON_ERROR_UTF8:
205
                throw new RuntimeException('Could not decode JSON, malformed UTF-8 characters (incorrectly encoded?)');
206
207
            default:
208
                throw new RuntimeException('Could not decode JSON.');
209
        }
210
    }
211
}
212