Passed
Push — master ( 21a5dc...1ca6d2 )
by Nate
40s
created

ReflectionTypeAdapter::write()   F

Complexity

Conditions 18
Paths 325

Size

Total Lines 61
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 36
CRAP Score 18

Importance

Changes 0
Metric Value
cc 18
eloc 36
nc 325
nop 2
dl 0
loc 61
ccs 36
cts 36
cp 1
crap 18
rs 2.4708
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * Copyright (c) Nate Brunette.
4
 * Distributed under the MIT License (http://opensource.org/licenses/MIT)
5
 */
6
7
declare(strict_types=1);
8
9
namespace Tebru\Gson\Internal\TypeAdapter;
10
11
use Tebru\Gson\Annotation\JsonAdapter;
12
use Tebru\Gson\Internal\Data\Property;
13
use Tebru\Gson\Internal\DefaultClassMetadata;
14
use Tebru\Gson\Internal\DefaultDeserializationExclusionData;
15
use Tebru\Gson\Internal\DefaultSerializationExclusionData;
16
use Tebru\Gson\Internal\Excluder;
17
use Tebru\Gson\Internal\ObjectConstructor\CreateFromInstance;
18
use Tebru\Gson\Internal\ObjectConstructorAware;
19
use Tebru\Gson\Internal\ObjectConstructorAwareTrait;
20
use Tebru\Gson\Internal\TypeAdapterProvider;
21
use Tebru\Gson\JsonWritable;
22
use Tebru\Gson\Internal\Data\PropertyCollection;
23
use Tebru\Gson\JsonReadable;
24
use Tebru\Gson\Internal\ObjectConstructor;
25
use Tebru\Gson\JsonToken;
26
use Tebru\Gson\TypeAdapter;
27
use TypeError;
28
29
/**
30
 * Class ReflectionTypeAdapter
31
 *
32
 * Uses reflected class properties to read/write object
33
 *
34
 * @author Nate Brunette <[email protected]>
35
 */
36
final class ReflectionTypeAdapter extends TypeAdapter implements ObjectConstructorAware
37
{
38
    use ObjectConstructorAwareTrait;
39
40
    /**
41
     * @var PropertyCollection
42
     */
43
    private $properties;
44
45
    /**
46
     * @var DefaultClassMetadata
47
     */
48
    private $classMetadata;
49
50
    /**
51
     * @var Excluder
52
     */
53
    private $excluder;
54
55
    /**
56
     * @var TypeAdapterProvider
57
     */
58
    private $typeAdapterProvider;
59
60
    /**
61
     * @var null|string
62
     */
63
    private $classVirtualProperty;
64
65
    /**
66
     * @var bool
67
     */
68
    private $skipSerialize;
69
70
    /**
71
     * @var bool
72
     */
73
    private $skipDeserialize;
74
75
    /**
76
     * @var bool
77
     */
78
    private $hasClassSerializationStrategies;
79
80
    /**
81
     * @var bool
82
     */
83
    private $hasPropertySerializationStrategies;
84
85
    /**
86
     * @var bool
87
     */
88
    private $hasClassDeserializationStrategies;
89
90
    /**
91
     * @var bool
92
     */
93
    private $hasPropertyDeserializationStrategies;
94
95
    /**
96
     * An memory cache of used type adapters
97
     *
98
     * @var TypeAdapter[]
99
     */
100
    private $adapters = [];
101
102
    /**
103
     * A memory cache of read properties
104
     *
105
     * @var Property[]
106
     */
107
    private $propertyCache = [];
108
109
    /**
110
     * Constructor
111
     *
112
     * @param ObjectConstructor $objectConstructor
113
     * @param DefaultClassMetadata $classMetadata
114
     * @param Excluder $excluder
115
     * @param TypeAdapterProvider $typeAdapterProvider
116
     * @param null|string $classVirtualProperty
117
     */
118 17
    public function __construct(
119
        ObjectConstructor $objectConstructor,
120
        DefaultClassMetadata $classMetadata,
121
        Excluder $excluder,
122
        TypeAdapterProvider $typeAdapterProvider,
123
        ?string $classVirtualProperty
124
    ) {
125 17
        $this->objectConstructor = $objectConstructor;
126 17
        $this->classMetadata = $classMetadata;
127 17
        $this->excluder = $excluder;
128 17
        $this->typeAdapterProvider = $typeAdapterProvider;
129 17
        $this->classVirtualProperty = $classVirtualProperty;
130
131 17
        $this->properties = $classMetadata->getPropertyCollection();
132 17
        $this->skipSerialize = $classMetadata->skipSerialize();
133 17
        $this->skipDeserialize = $classMetadata->skipDeserialize();
134 17
        $this->hasClassSerializationStrategies = $this->excluder->hasClassSerializationStrategies();
135 17
        $this->hasPropertySerializationStrategies = $this->excluder->hasPropertySerializationStrategies();
136 17
        $this->hasClassDeserializationStrategies = $this->excluder->hasClassDeserializationStrategies();
137 17
        $this->hasPropertyDeserializationStrategies = $this->excluder->hasPropertyDeserializationStrategies();
138 17
    }
139
    /**
140
     * Read the next value, convert it to its type and return it
141
     *
142
     * @param JsonReadable $reader
143
     * @return object
144
     */
145 11
    public function read(JsonReadable $reader)
146
    {
147 11
        if ($this->skipDeserialize) {
148 1
            $reader->skipValue();
149 1
            return null;
150
        }
151
152 10
        if ($reader->peek() === JsonToken::NULL) {
153 1
            $reader->nextNull();
154 1
            return null;
155
        }
156
157 9
        $object = $this->objectConstructor->construct();
158 9
        $exclusionData = $this->hasClassDeserializationStrategies || $this->hasPropertyDeserializationStrategies
159 2
            ? new DefaultDeserializationExclusionData(clone $object, $reader)
160 9
            : null;
161
162 9
        if ($this->hasClassDeserializationStrategies && $exclusionData) {
163 1
            $this->excluder->applyClassDeserializationExclusionData($exclusionData);
164
165 1
            if ($this->excluder->excludeClassByDeserializationStrategy($this->classMetadata)) {
166 1
                $reader->skipValue();
167 1
                return null;
168
            }
169
        }
170
171 8
        if ($this->hasPropertyDeserializationStrategies && $exclusionData) {
172 1
            $this->excluder->applyPropertyDeserializationExclusionData($exclusionData);
173
        }
174
175 8
        $reader->beginObject();
176
177 8
        if ($this->classVirtualProperty !== null) {
178 1
            $reader->nextName();
179 1
            $reader->beginObject();
180
        }
181
182 8
        $usesExisting = $reader->getContext()->usesExistingObject();
183
184 8
        while ($reader->hasNext()) {
185 8
            $name = $reader->nextName();
186 8
            $property = $this->propertyCache[$name] ?? $this->propertyCache[$name] = $this->properties->getBySerializedName($name);
187
188 8
            if ($property === null) {
189 8
                $reader->skipValue();
190 8
                continue;
191
            }
192
193
            if (
194 6
                $property->skipDeserialize()
195 6
                || ($this->hasPropertyDeserializationStrategies && $this->excluder->excludePropertyByDeserializationStrategy($property))
196
            ) {
197 1
                $reader->skipValue();
198 1
                continue;
199
            }
200
201 6
            $realName = $property->getName();
202
203 6
            $adapter = $this->adapters[$realName] ?? null;
204 6
            if ($adapter === null) {
205
                /** @var JsonAdapter $jsonAdapterAnnotation */
206 6
                $jsonAdapterAnnotation = $property->getAnnotations()->get(JsonAdapter::class);
207 6
                $adapter = null === $jsonAdapterAnnotation
208 6
                    ? $this->typeAdapterProvider->getAdapter($property->getType())
209 4
                    : $this->typeAdapterProvider->getAdapterFromAnnotation(
210 4
                        $property->getType(),
211 6
                        $jsonAdapterAnnotation
212
                    );
213 6
                $this->adapters[$realName] = $adapter;
214
            }
215
216 6
            if ($adapter instanceof ObjectConstructorAware && $usesExisting) {
217
                try {
218 2
                    $nestedObject = $property->get($object);
219 1
                } /** @noinspection BadExceptionsProcessingInspection */ catch (TypeError $error) {
220
                    // this may occur when attempting to get a nested object that doesn't exist and
221
                    // the method return is not nullable. The type error only occurs because we are
222
                    // may be calling the getter before data exists.
223 1
                    $nestedObject = null;
224
                }
225
226 2
                if ($nestedObject !== null) {
227 1
                    $adapter->setObjectConstructor(new CreateFromInstance($nestedObject));
228
                }
229
            }
230
231 6
            $property->set($object, $adapter->read($reader));
232
        }
233 8
        $reader->endObject();
234
235 8
        if ($this->classVirtualProperty !== null) {
236 1
            $reader->endObject();
237
        }
238
239 8
        return $object;
240
    }
241
242
    /**
243
     * Write the value to the writer for the type
244
     *
245
     * @param JsonWritable $writer
246
     * @param object $value
247
     * @return void
248
     */
249 6
    public function write(JsonWritable $writer, $value): void
250
    {
251 6
        if ($this->skipSerialize || $value === null) {
252 2
            $writer->writeNull();
253 2
            return;
254
        }
255
256 4
        $exclusionData = $this->hasClassSerializationStrategies || $this->hasPropertySerializationStrategies
257 2
            ? new DefaultSerializationExclusionData($value, $writer)
258 4
            : null;
259
260 4
        if ($this->hasClassSerializationStrategies && $exclusionData) {
261 1
            $this->excluder->applyClassSerializationExclusionData($exclusionData);
262
263 1
            if ($this->excluder->excludeClassBySerializationStrategy($this->classMetadata)) {
264 1
                $writer->writeNull();
265 1
                return;
266
            }
267
        }
268
269 3
        if ($this->hasPropertySerializationStrategies && $exclusionData) {
270 1
            $this->excluder->applyPropertySerializationExclusionData($exclusionData);
271
        }
272
273 3
        $writer->beginObject();
274
275 3
        if ($this->classVirtualProperty !== null) {
276 1
            $writer->name($this->classVirtualProperty);
277 1
            $writer->beginObject();
278
        }
279
280
        /** @var Property $property */
281 3
        foreach ($this->properties as $property) {
282 3
            $realName = $property->getName();
283 3
            $writer->name($property->getSerializedName());
284
285
            if (
286 3
                $property->skipSerialize()
287 3
                || ($this->hasPropertySerializationStrategies && $this->excluder->excludePropertyBySerializationStrategy($property))
288
            ) {
289 3
                $writer->writeNull();
290
291 3
                continue;
292
            }
293
294 3
            $adapter = $this->adapters[$realName] ?? null;
295 3
            if ($adapter === null) {
296
                /** @var JsonAdapter $jsonAdapterAnnotation */
297 3
                $jsonAdapterAnnotation = $property->getAnnotations()->get(JsonAdapter::class);
298 3
                $adapter = null === $jsonAdapterAnnotation
299 3
                    ? $this->typeAdapterProvider->getAdapter($property->getType())
300 3
                    : $this->typeAdapterProvider->getAdapterFromAnnotation($property->getType(), $jsonAdapterAnnotation);
301 3
                $this->adapters[$realName] = $adapter;
302
            }
303 3
            $adapter->write($writer, $property->get($value));
304
        }
305
306 3
        $writer->endObject();
307
308 3
        if ($this->classVirtualProperty !== null) {
309 1
            $writer->endObject();
310
        }
311 3
    }
312
}
313