Completed
Pull Request — master (#743)
by Asmir
07:41
created

JsonDeserializationVisitor::prepare()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 22.9226

Importance

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