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 (#77)
by Brent
03:51
created

Property::isValidType()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 8.9777
c 0
b 0
f 0
cc 6
nc 5
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spatie\DataTransferObject;
6
7
use ReflectionProperty;
8
9
class Property extends ReflectionProperty
10
{
11
    protected static array
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_ARRAY, expecting T_FUNCTION or T_CONST
Loading history...
12
13
 $typeMapping = [
14
        'int' => 'integer',
15
        'bool' => 'boolean',
16
        'float' => 'double',
17
    ];
18
19
    protected DataTransferObject $dataTransferObject;
20
21
    protected bool $hasTypeDeclaration = false;
22
23
    protected bool $isNullable = false;
24
25
    protected bool $isInitialised = false;
26
27
    protected array $types = [];
28
29
    protected array $arrayTypes = [];
30
31
    public static function fromReflection(DataTransferObject $dataTransferObject, ReflectionProperty $reflectionProperty)
32
    {
33
        return new self($dataTransferObject, $reflectionProperty);
34
    }
35
36
    public function __construct(DataTransferObject $dataTransferObject, ReflectionProperty $reflectionProperty)
37
    {
38
        parent::__construct($reflectionProperty->class, $reflectionProperty->getName());
39
40
        $this->dataTransferObject = $dataTransferObject;
41
42
        $this->resolveTypeDefinition();
43
    }
44
45
    public function set($value)
46
    {
47
        if (is_array($value)) {
48
            $value = $this->shouldBeCastToCollection($value) ? $this->castCollection($value) : $this->cast($value);
49
        }
50
51
        if (! $this->isValidType($value)) {
52
            throw DataTransferObjectError::invalidType($this, $value);
53
        }
54
55
        $this->isInitialised = true;
56
57
        $this->dataTransferObject->{$this->getName()} = $value;
58
    }
59
60
    public function getTypes(): array
61
    {
62
        return $this->types;
63
    }
64
65
    public function getFqn(): string
66
    {
67
        return "{$this->getDeclaringClass()->getName()}::{$this->getName()}";
68
    }
69
70
    public function isNullable(): bool
71
    {
72
        return $this->isNullable;
73
    }
74
75
    protected function resolveTypeDefinition()
76
    {
77
        $docComment = $this->getDocComment();
78
79
        if (! $docComment) {
80
            $this->isNullable = true;
81
82
            return;
83
        }
84
85
        preg_match('/\@var ((?:(?:[\w|\\\\<>])+(?:\[\])?)+)/', $docComment, $matches);
86
87
        if (! count($matches)) {
88
            $this->isNullable = true;
89
90
            return;
91
        }
92
93
        $this->hasTypeDeclaration = true;
94
95
        $varDocComment = end($matches);
96
97
        $this->types = explode('|', $varDocComment);
98
        $this->arrayTypes = str_replace('[]', '', $this->types);
99
100
        if (preg_match_all('/iterable<([^|]*)>/', $varDocComment, $matches)) {
101
            $this->arrayTypes = [...$this->arrayTypes, ...$matches[1]];
102
        }
103
104
        $this->isNullable = strpos($varDocComment, 'null') !== false;
105
    }
106
107
    protected function isValidType($value): bool
108
    {
109
        if (! $this->hasTypeDeclaration) {
110
            return true;
111
        }
112
113
        if ($this->isNullable && $value === null) {
114
            return true;
115
        }
116
117
        foreach ($this->types as $currentType) {
118
            $isValidType = $this->assertTypeEquals($currentType, $value);
119
120
            if ($isValidType) {
121
                return true;
122
            }
123
        }
124
125
        return false;
126
    }
127
128
    protected function cast($value)
129
    {
130
        $castTo = null;
131
132
        foreach ($this->types as $type) {
133
            if (! is_subclass_of($type, DataTransferObject::class)) {
134
                continue;
135
            }
136
137
            $castTo = $type;
138
139
            break;
140
        }
141
142
        if (! $castTo) {
143
            return $value;
144
        }
145
146
        return new $castTo($value);
147
    }
148
149
    protected function castCollection(array $values)
150
    {
151
        $castTo = null;
152
153
        foreach ($this->arrayTypes as $type) {
154
            if (! is_subclass_of($type, DataTransferObject::class)) {
155
                continue;
156
            }
157
158
            $castTo = $type;
159
160
            break;
161
        }
162
163
        if (! $castTo) {
164
            return $values;
165
        }
166
167
        $casts = [];
168
169
        foreach ($values as $value) {
170
            $casts[] = new $castTo($value);
171
        }
172
173
        return $casts;
174
    }
175
176
    protected function shouldBeCastToCollection(array $values): bool
177
    {
178
        if (empty($values)) {
179
            return false;
180
        }
181
182
        foreach ($values as $key => $value) {
183
            if (is_string($key)) {
184
                return false;
185
            }
186
187
            if (! is_array($value)) {
188
                return false;
189
            }
190
        }
191
192
        return true;
193
    }
194
195
    protected function assertTypeEquals(string $type, $value): bool
196
    {
197
        if (strpos($type, '[]') !== false) {
198
            return $this->isValidArray($type, $value);
199
        }
200
201
        if ($type === 'iterable' || strpos($type, 'iterable<') === 0) {
202
            return $this->isValidIterable($type, $value);
203
        }
204
205
        if ($type === 'mixed' && $value !== null) {
206
            return true;
207
        }
208
209
        return $value instanceof $type
210
            || gettype($value) === (self::$typeMapping[$type] ?? $type);
211
    }
212
213
    protected function isValidArray(string $type, $collection): bool
214
    {
215
        if (! is_array($collection)) {
216
            return false;
217
        }
218
219
        $valueType = str_replace('[]', '', $type);
220
221
        return $this->isValidGenericCollection($valueType, $collection);
222
    }
223
224
    protected function isValidIterable(string $type, $collection): bool
225
    {
226
        if (! is_iterable($collection)) {
227
            return false;
228
        }
229
230
        if (preg_match('/^iterable<(.*)>$/', $type, $matches)) {
231
            return $this->isValidGenericCollection($matches[1], $collection);
232
        }
233
234
        return true;
235
    }
236
237
    protected function isValidGenericCollection(string $type, $collection): bool
238
    {
239
        foreach ($collection as $value) {
240
            if (! $this->assertTypeEquals($type, $value)) {
241
                return false;
242
            }
243
        }
244
245
        return true;
246
    }
247
}
248