Passed
Branch master (bf85d9)
by Johannes
05:40
created

JsonDeserializationVisitor::visitArray()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 36
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 7.0572

Importance

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