Completed
Push — caching ( 6c9b2f )
by Nate
03:39
created

ReflectionTypeAdapter::write()   C

Complexity

Conditions 17
Paths 24

Size

Total Lines 47
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 27
c 1
b 0
f 0
dl 0
loc 47
rs 5.2166
cc 17
nc 24
nop 2

How to fix   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\TypeAdapter;
10
11
use Tebru\Gson\Context\ReaderContext;
12
use Tebru\Gson\Context\WriterContext;
13
use Tebru\Gson\Internal\Data\Property;
14
use Tebru\Gson\Internal\DefaultClassMetadata;
15
use Tebru\Gson\Internal\ObjectConstructor;
16
use Tebru\Gson\Internal\ObjectConstructor\CreateFromInstance;
17
use Tebru\Gson\Internal\ObjectConstructorAware;
18
use Tebru\Gson\Internal\ObjectConstructorAwareTrait;
19
use Tebru\Gson\TypeAdapter;
20
use TypeError;
21
22
/**
23
 * Class ReflectionTypeAdapter
24
 *
25
 * Uses reflected class properties to read/write object
26
 *
27
 * @author Nate Brunette <[email protected]>
28
 */
29
class ReflectionTypeAdapter extends TypeAdapter implements ObjectConstructorAware
30
{
31
    use ObjectConstructorAwareTrait;
32
33
    /**
34
     * @var DefaultClassMetadata
35
     */
36
    protected $classMetadata;
37
38
    /**
39
     * @var Property[]
40
     */
41
    protected $properties;
42
43
    /**
44
     * @var null|string
45
     */
46
    protected $classVirtualProperty;
47
48
    /**
49
     * @var bool
50
     */
51
    protected $skipSerialize;
52
53
    /**
54
     * @var bool
55
     */
56
    protected $skipDeserialize;
57
58
    /**
59
     * Constructor
60
     *
61
     * @param ObjectConstructor $objectConstructor
62
     * @param DefaultClassMetadata $classMetadata
63
     * @param null|string $classVirtualProperty
64
     */
65
    public function __construct(
66
        ObjectConstructor $objectConstructor,
67
        DefaultClassMetadata $classMetadata,
68
        ?string $classVirtualProperty
69
    ) {
70
        $this->objectConstructor = $objectConstructor;
71
        $this->classVirtualProperty = $classVirtualProperty;
72
        $this->classMetadata = $classMetadata;
73
        $this->properties = $classMetadata->properties->elements;
74
        $this->skipSerialize = $classMetadata->skipSerialize;
75
        $this->skipDeserialize = $classMetadata->skipDeserialize;
76
    }
77
78
    /**
79
     * Read the next value, convert it to its type and return it
80
     *
81
     * @param array $value
82
     * @param ReaderContext $context
83
     * @return object
84
     */
85
    public function read($value, ReaderContext $context)
86
    {
87
        if ($this->skipDeserialize || $value === null) {
88
            return null;
89
        }
90
91
        if ($this->classVirtualProperty !== null) {
92
            $value = array_shift($value);
93
        }
94
95
        $object = $this->objectConstructor->construct();
96
        $usesExisting = $context->usesExistingObject;
97
        $enableScalarAdapters = $context->enableScalarAdapters;
98
        $typeAdapterProvider = $context->typeAdapterProvider;
99
        $excluder = $context->getExcluder();
100
        $payload = $context->getPayload();
101
102
        if ($this->classMetadata->hasExclusions && $excluder->skipClassDeserializeByStrategy($this->classMetadata, $object, $payload)) {
0 ignored issues
show
Bug introduced by
The property hasExclusions does not seem to exist on Tebru\Gson\Internal\DefaultClassMetadata.
Loading history...
103
            return null;
104
        }
105
106
        foreach ($value as $name => $item) {
107
            $property = $this->properties[$name] ?? null;
108
109
            if ($property === null || $property->skipDeserialize) {
110
                continue;
111
            }
112
113
            if ($property->hasExclusions && $excluder->skipPropertyDeserializeByStrategy($property, $object, $payload)) {
0 ignored issues
show
Bug introduced by
The property hasExclusions does not seem to exist on Tebru\Gson\Internal\Data\Property.
Loading history...
114
                continue;
115
            }
116
117
            $adapter = $property->adapter;
118
119
            if ($adapter === null) {
120
                // disabled scalar adapters
121
                if ($property->isScalar && !$enableScalarAdapters) {
122
                    $property->setterStrategy->set($object, $item);
123
                    continue;
124
                }
125
126
                $adapter = $property->adapter = $typeAdapterProvider->getAdapterFromProperty($property);
127
            }
128
129
            if ($usesExisting && $adapter instanceof ObjectConstructorAware) {
130
                try {
131
                    $nestedObject = $property->getterStrategy->get($object);
132
                } /** @noinspection BadExceptionsProcessingInspection */ catch (TypeError $error) {
133
                    // this may occur when attempting to get a nested object that doesn't exist and
134
                    // the method return is not nullable. The type error only occurs because we are
135
                    // may be calling the getter before data exists.
136
                    $nestedObject = null;
137
                }
138
139
                if ($nestedObject !== null) {
140
                    $adapter->setObjectConstructor(new CreateFromInstance($nestedObject));
141
                }
142
            }
143
144
            $property->setterStrategy->set($object, $adapter->read($item, $context));
145
        }
146
147
        return $object;
148
    }
149
150
    /**
151
     * Write the value to the writer for the type
152
     *
153
     * @param object $value
154
     * @param WriterContext $context
155
     * @return array|null
156
     */
157
    public function write($value, WriterContext $context): ?array
158
    {
159
        if ($this->skipSerialize || $value === null) {
160
            return null;
161
        }
162
163
        $result = [];
164
        $serializeNull = $context->serializeNull();
165
        $enableScalarAdapters = $context->enableScalarAdapters();
166
        $typeAdapterProvider = $context->getTypeAdapterProvider();
167
        $excluder = $context->getExcluder();
168
169
        if ($this->classMetadata->hasExclusions && $excluder->skipClassSerializeByStrategy($this->classMetadata, $value)) {
0 ignored issues
show
Bug introduced by
The property hasExclusions does not seem to exist on Tebru\Gson\Internal\DefaultClassMetadata.
Loading history...
170
            return null;
171
        }
172
173
        foreach ($this->properties as $property) {
174
            if ($property->skipSerialize) {
175
                continue;
176
            }
177
            if ($property->hasExclusions && $excluder->skipPropertySerializeByStrategy($property, $value)) {
0 ignored issues
show
Bug introduced by
The property hasExclusions does not seem to exist on Tebru\Gson\Internal\Data\Property.
Loading history...
178
                continue;
179
            }
180
181
            $serializedName = $property->serializedName;
182
            $propertyValue = $property->getterStrategy->get($value);
183
            $isNull = $propertyValue === null;
184
            $adapter = $property->adapter;
185
186
            if ($adapter === null && ($enableScalarAdapters || !$property->isScalar)) {
187
                $adapter = $typeAdapterProvider->getAdapterFromProperty($property);
188
            }
189
190
            if (!$isNull && $adapter !== null) {
191
                $propertyValue = $adapter->write($propertyValue, $context);
192
            }
193
194
            if ($serializeNull || !$isNull) {
195
                $result[$serializedName] = $propertyValue;
196
            }
197
        }
198
199
        if ($this->classVirtualProperty !== null) {
200
            $result = [$this->classVirtualProperty => $result];
201
        }
202
203
        return $result;
204
    }
205
206
    /**
207
     * Return true if object can be written to disk
208
     *
209
     * @return bool
210
     */
211
    public function canCache(): bool
212
    {
213
        return $this->objectConstructor->canCache();
214
    }
215
}
216