GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#56)
by Arthur
03:40 queued 01:33
created

Property   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 286
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 55
lcom 1
cbo 1
dl 0
loc 286
rs 6
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A fromReflection() 0 4 1
A __construct() 0 6 1
A set() 0 14 4
A setUninitialized() 0 4 1
A isInitialized() 0 4 1
A getTypes() 0 4 1
A getFqn() 0 4 1
A isNullable() 0 4 1
A setNullable() 0 4 1
A isImmutable() 0 4 1
A setIsImmutable() 0 4 1
A resolveTypeDefinition() 0 33 5
B isValidType() 0 20 6
A cast() 0 20 4
A castCollection() 0 26 5
A shouldBeCastToCollection() 0 18 5
A assertTypeEquals() 0 13 5
A isValidGenericCollection() 0 16 4
A getDefault() 0 4 1
A setDefault() 0 4 1
A getValue() 0 8 3
A getValueFromReflection() 0 4 1
A getName() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Property often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Property, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spatie\DataTransferObject;
6
7
use ReflectionProperty;
8
9
class Property
10
{
11
    /** @var array */
12
    protected static $typeMapping = [
13
        'int' => 'integer',
14
        'bool' => 'boolean',
15
        'float' => 'double',
16
        'immutable' => immutable::class,
17
    ];
18
19
    /** @var bool */
20
    protected $hasTypeDeclaration = false;
21
22
    /** @var bool */
23
    protected $isNullable = false;
24
25
    /** @var bool */
26
    protected $isInitialised = false;
27
28
    /** @var bool */
29
    protected $isImmutable = false;
30
31
    /** @var array */
32
    protected $types = [];
33
34
    /** @var array */
35
    protected $arrayTypes = [];
36
37
    /** @var mixed */
38
    protected $default;
39
40
    /** @var mixed */
41
    protected $value;
42
43
    /** @var ReflectionProperty */
44
    protected $reflection;
45
46
    public static function fromReflection(ReflectionProperty $reflectionProperty): self
47
    {
48
        return new static($reflectionProperty);
49
    }
50
51
    public function __construct(ReflectionProperty $reflectionProperty)
52
    {
53
        $this->reflection = $reflectionProperty;
54
55
        $this->resolveTypeDefinition();
56
    }
57
58
    public function set($value)
59
    {
60
        if (is_array($value)) {
61
            $value = $this->shouldBeCastToCollection($value) ? $this->castCollection($value) : $this->cast($value);
62
        }
63
64
        if (! $this->isValidType($value)) {
65
            throw DataTransferObjectError::invalidType($this, $value);
66
        }
67
68
        $this->isInitialised = true;
69
70
        $this->value = $value;
71
    }
72
73
    public function setUninitialized()
74
    {
75
        $this->isInitialised = false;
76
    }
77
78
    public function isInitialized()
79
    {
80
        return $this->isInitialised;
81
    }
82
83
    public function getTypes(): array
84
    {
85
        return $this->types;
86
    }
87
88
    public function getFqn(): string
89
    {
90
        return "{$this->reflection->getDeclaringClass()->getName()}::{$this->reflection->getName()}";
91
    }
92
93
    public function isNullable(): bool
94
    {
95
        return $this->isNullable;
96
    }
97
98
    public function setNullable(bool $bool): void
99
    {
100
        $this->isNullable = $bool;
101
    }
102
103
    public function isImmutable(): bool
104
    {
105
        return $this->isImmutable;
106
    }
107
108
    public function setIsImmutable(bool $isImmutable): void
109
    {
110
        $this->isImmutable = $isImmutable;
111
    }
112
113
    protected function resolveTypeDefinition()
114
    {
115
        $docComment = $this->reflection->getDocComment();
116
117
        if (! $docComment) {
118
            $this->setNullable(true);
119
120
            return;
121
        }
122
123
        preg_match('/\@var ((?:(?:[\w|\\\\])+(?:\[\])?)+)/', $docComment, $matches);
124
125
        if (! count($matches)) {
126
            $this->setNullable(true);
127
128
            return;
129
        }
130
131
        $this->hasTypeDeclaration = true;
132
133
        $varDocComment = end($matches);
134
135
        $this->types = explode('|', $varDocComment);
136
        $this->arrayTypes = str_replace('[]', '', $this->types);
137
138
        $this->setNullable(strpos($varDocComment, 'null') !== false);
139
140
        if (in_array('immutable', $this->types) || in_array('Immutable', $this->types)) {
141
            $this->setIsImmutable(true);
142
            unset($this->types['immutable']);
143
            unset($this->types['Immutable']);
144
        }
145
    }
146
147
    protected function isValidType($value): bool
148
    {
149
        if (! $this->hasTypeDeclaration) {
150
            return true;
151
        }
152
153
        if ($this->isNullable() && $value === null) {
154
            return true;
155
        }
156
157
        foreach ($this->types as $currentType) {
158
            $isValidType = $this->assertTypeEquals($currentType, $value);
159
160
            if ($isValidType) {
161
                return true;
162
            }
163
        }
164
165
        return false;
166
    }
167
168
    protected function cast($value)
169
    {
170
        $castTo = null;
171
172
        foreach ($this->types as $type) {
173
            if (! is_subclass_of($type, DataTransferObject::class)) {
174
                continue;
175
            }
176
177
            $castTo = $type;
178
179
            break;
180
        }
181
182
        if (! $castTo) {
183
            return $value;
184
        }
185
186
        return new $castTo($value);
187
    }
188
189
    protected function castCollection(array $values)
190
    {
191
        $castTo = null;
192
193
        foreach ($this->arrayTypes as $type) {
194
            if (! is_subclass_of($type, DataTransferObject::class)) {
195
                continue;
196
            }
197
198
            $castTo = $type;
199
200
            break;
201
        }
202
203
        if (! $castTo) {
204
            return $values;
205
        }
206
207
        $casts = [];
208
209
        foreach ($values as $value) {
210
            $casts[] = new $castTo($value);
211
        }
212
213
        return $casts;
214
    }
215
216
    protected function shouldBeCastToCollection(array $values): bool
217
    {
218
        if (empty($values)) {
219
            return false;
220
        }
221
222
        foreach ($values as $key => $value) {
223
            if (is_string($key)) {
224
                return false;
225
            }
226
227
            if (! is_array($value)) {
228
                return false;
229
            }
230
        }
231
232
        return true;
233
    }
234
235
    protected function assertTypeEquals(string $type, $value): bool
236
    {
237
        if (strpos($type, '[]') !== false) {
238
            return $this->isValidGenericCollection($type, $value);
239
        }
240
241
        if ($type === 'mixed' && $value !== null) {
242
            return true;
243
        }
244
245
        return $value instanceof $type
246
            || gettype($value) === (self::$typeMapping[$type] ?? $type);
247
    }
248
249
    protected function isValidGenericCollection(string $type, $collection): bool
250
    {
251
        if (! is_array($collection)) {
252
            return false;
253
        }
254
255
        $valueType = str_replace('[]', '', $type);
256
257
        foreach ($collection as $value) {
258
            if (! $this->assertTypeEquals($valueType, $value)) {
259
                return false;
260
            }
261
        }
262
263
        return true;
264
    }
265
266
    public function getDefault()
267
    {
268
        return $this->default;
269
    }
270
271
    public function setDefault($default): void
272
    {
273
        $this->default = $default;
274
    }
275
276
    public function getValue()
277
    {
278
        if (! $this->isNullable() && $this->value == null) {
279
            return $this->getDefault();
280
        }
281
282
        return $this->value;
283
    }
284
285
    public function getValueFromReflection($object)
286
    {
287
        return $this->reflection->getValue($object);
288
    }
289
290
    public function getName()
291
    {
292
        return $this->reflection->getName();
293
    }
294
}
295