Completed
Pull Request — master (#447)
by Ivan
07:22
created

GenericDeserializationVisitor::visitProperty()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 29
rs 6.7272
cc 7
eloc 16
nc 8
nop 3
1
<?php
2
3
/*
4
 * Copyright 2013 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\DeserializeException;
22
use JMS\Serializer\Exception\RuntimeException;
23
use JMS\Serializer\Metadata\IndexMetadata;
24
use JMS\Serializer\Metadata\PropertyMetadata;
25
use JMS\Serializer\Metadata\ClassMetadata;
26
27
/**
28
 * Generic Deserialization Visitor.
29
 *
30
 * @author Johannes M. Schmitt <[email protected]>
31
 */
32
abstract class GenericDeserializationVisitor extends AbstractVisitor
33
{
34
    private $navigator;
35
    private $result;
36
    private $objectStack;
37
    private $currentObject;
38
39
    public function setNavigator(GraphNavigator $navigator)
40
    {
41
        $this->navigator = $navigator;
42
        $this->result = null;
43
        $this->objectStack = new \SplStack;
44
    }
45
46
    public function getNavigator()
47
    {
48
        return $this->navigator;
49
    }
50
51
    public function prepare($data)
52
    {
53
        return $this->decode($data);
54
    }
55
56
    public function visitNull($data, array $type, Context $context)
57
    {
58
        return null;
59
    }
60
61
    public function visitString($data, array $type, Context $context)
62
    {
63
        if (null === $data) {
64
            return;
65
        }
66
        if ( ! is_scalar($data)) {
67
            throw new DeserializeException($type, $data, $context);
68
        }
69
        $data = (string) $data;
70
71
        if (null === $this->result) {
72
            $this->result = $data;
73
        }
74
75
        return $data;
76
    }
77
78
    public function visitBoolean($data, array $type, Context $context)
79
    {
80
        if (null === $data) {
81
            return;
82
        }
83
        if ( ! is_scalar($data)) {
84
            throw new DeserializeException($type, $data, $context);
85
        }
86
        $data = (Boolean) $data;
87
88
        if (null === $this->result) {
89
            $this->result = $data;
90
        }
91
92
        return $data;
93
    }
94
95
    public function visitInteger($data, array $type, Context $context)
96
    {
97
        if (null === $data) {
98
            return;
99
        }
100
        if ( ! is_numeric($data)) {
101
            throw new DeserializeException($type, $data, $context);
102
        }
103
        $data = (integer) $data;
104
105
        if (null === $this->result) {
106
            $this->result = $data;
107
        }
108
109
        return $data;
110
    }
111
112
    public function visitDouble($data, array $type, Context $context)
113
    {
114
        if (null === $data) {
115
            return;
116
        }
117
        if ( ! is_numeric($data)) {
118
            throw new DeserializeException($type, $data, $context);
119
        }
120
        $data = (double) $data;
121
122
        if (null === $this->result) {
123
            $this->result = $data;
124
        }
125
126
        return $data;
127
    }
128
129
    public function visitArray($data, array $type, Context $context)
130
    {
131
        if ( ! is_array($data) && ! ($data instanceof \ArrayObject)) {
132
            throw new DeserializeException($type, $data, $context);
133
        }
134
135
        // If no further parameters were given, keys/values are just passed as is.
136
        if ( ! $type['params']) {
137
            if (null === $this->result) {
138
                $this->result = $data;
139
            }
140
141
            return $data;
142
        }
143
144
        switch (count($type['params'])) {
145
            case 1: // Array is a list.
146
                $listType = $type['params'][0];
147
148
                $result = array();
149
                if (null === $this->result) {
150
                    $this->result = &$result;
151
                }
152
153
                foreach ($data as $k => $v) {
154
                    $context->pushIndexMetadata(new IndexMetadata($k));
155
                    $result[] = $this->navigator->accept($v, $listType, $context);
156
                    $context->popIndexMetadata();
157
                }
158
159
                return $result;
160
161
            case 2: // Array is a map.
162
                list($keyType, $entryType) = $type['params'];
163
164
                $result = array();
165
                if (null === $this->result) {
166
                    $this->result = &$result;
167
                }
168
169
                foreach ($data as $k => $v) {
170
                    $context->pushIndexMetadata(new IndexMetadata($k));
171
                    $result[$this->navigator->accept($k, $keyType, $context)] = $this->navigator->accept($v, $entryType, $context);
172
                    $context->popIndexMetadata();
173
                }
174
175
                return $result;
176
177
            default:
178
                throw new RuntimeException(sprintf('Array type cannot have more than 2 parameters, but got %s.', json_encode($type['params'])));
179
        }
180
    }
181
182
    public function startVisitingObject(ClassMetadata $metadata, $object, array $type, Context $context)
183
    {
184
        $this->setCurrentObject($object);
185
186
        if (null === $this->result) {
187
            $this->result = $this->currentObject;
188
        }
189
    }
190
191
    public function visitProperty(PropertyMetadata $metadata, $data, Context $context)
192
    {
193
        $name = $this->namingStrategy->translateName($metadata);
194
195
        if (null === $data) {
196
            return;
197
        }
198
        if ( ! is_array($data)) {
199
            $context->popPropertyMetadata();
200
            throw new DeserializeException(array('name' => 'object', 'params' => array()), $data, $context);
201
        }
202
        if ( ! array_key_exists($name, $data)) {
203
            return;
204
        }
205
206
        if ( ! $metadata->type) {
207
            throw new RuntimeException(sprintf('You must define a type for %s::$%s.', $metadata->reflection->class, $metadata->name));
208
        }
209
210
        $v = $data[$name] !== null ? $this->navigator->accept($data[$name], $metadata->type, $context) : null;
211
212
        if (null === $metadata->setter) {
213
            $metadata->reflection->setValue($this->currentObject, $v);
214
215
            return;
216
        }
217
218
        $this->currentObject->{$metadata->setter}($v);
219
    }
220
221
    public function endVisitingObject(ClassMetadata $metadata, $data, array $type, Context $context)
222
    {
223
        $obj = $this->currentObject;
224
        $this->revertCurrentObject();
225
226
        return $obj;
227
    }
228
229
    public function getResult()
230
    {
231
        return $this->result;
232
    }
233
234
    public function setCurrentObject($object)
235
    {
236
        $this->objectStack->push($this->currentObject);
237
        $this->currentObject = $object;
238
    }
239
240
    public function getCurrentObject()
241
    {
242
        return $this->currentObject;
243
    }
244
245
    public function revertCurrentObject()
246
    {
247
        return $this->currentObject = $this->objectStack->pop();
248
    }
249
250
    abstract protected function decode($str);
251
}
252