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
Push — master ( a572b7...e3df15 )
by Brent
17s queued 14s
created

Property::assertTypeEquals()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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