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
02:14
created

Property::shouldBeCastToCollection()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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