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
01:25
created

Property::nullable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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