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 (#66)
by Tom
01:07
created

Property::resolveTypeDefinition()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 45

Duplication

Lines 12
Ratio 26.67 %

Importance

Changes 0
Metric Value
dl 12
loc 45
rs 8.8888
c 0
b 0
f 0
cc 5
nc 5
nop 0
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
    /** @var array[] */
37
    protected static $cache = [];
38
39
    public static function fromReflection(DataTransferObject $valueObject, ReflectionProperty $reflectionProperty)
40
    {
41
        return new self($valueObject, $reflectionProperty);
42
    }
43
44
    public function __construct(DataTransferObject $valueObject, ReflectionProperty $reflectionProperty)
45
    {
46
        parent::__construct($reflectionProperty->class, $reflectionProperty->getName());
47
48
        $this->valueObject = $valueObject;
49
50
        $this->resolveTypeDefinition();
51
    }
52
53
    public function set($value)
54
    {
55
        if (is_array($value)) {
56
            $value = $this->shouldBeCastToCollection($value) ? $this->castCollection($value) : $this->cast($value);
57
        }
58
59
        if (! $this->isValidType($value)) {
60
            throw DataTransferObjectError::invalidType($this, $value);
61
        }
62
63
        $this->isInitialised = true;
64
65
        $this->valueObject->{$this->getName()} = $value;
66
    }
67
68
    public function getTypes(): array
69
    {
70
        return $this->types;
71
    }
72
73
    public function getFqn(): string
74
    {
75
        return "{$this->getDeclaringClass()->getName()}::{$this->getName()}";
76
    }
77
78
    public function isNullable(): bool
79
    {
80
        return $this->isNullable;
81
    }
82
83
    protected function resolveTypeDefinition()
84
    {
85
        if (isset(self::$cache[$this->class][$this->getName()])) {
86
            $this->isNullable = self::$cache[$this->class][$this->getName()]['is_nullable'];
87
            $this->hasTypeDeclaration = (
88
                ! empty(self::$cache[$this->class][$this->getName()]['types'])
89
                || ! empty(self::$cache[$this->class][$this->getName()]['array_types'])
90
            );
91
            $this->types = self::$cache[$this->class][$this->getName()]['types'] ?? [];
92
            $this->arrayTypes = self::$cache[$this->class][$this->getName()]['array_types'] ?? [];
93
94
            return;
95
        }
96
97
        $docComment = $this->getDocComment();
98
99 View Code Duplication
        if (! $docComment) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
100
            $this->isNullable = true;
101
            self::$cache[$this->class][$this->getName()]['is_nullable'] = $this->isNullable;
102
103
            return;
104
        }
105
106
        preg_match('/\@var ((?:(?:[\w|\\\\])+(?:\[\])?)+)/', $docComment, $matches);
107
108 View Code Duplication
        if (! count($matches)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
109
            $this->isNullable = true;
110
            self::$cache[$this->class][$this->getName()]['is_nullable'] = $this->isNullable;
111
112
            return;
113
        }
114
115
        $this->hasTypeDeclaration = true;
116
117
        $varDocComment = end($matches);
118
119
        $this->types = explode('|', $varDocComment);
120
        self::$cache[$this->class][$this->getName()]['types'] = $this->types;
121
122
        $this->arrayTypes = str_replace('[]', '', $this->types);
123
        self::$cache[$this->class][$this->getName()]['array_types'] = $this->arrayTypes;
124
125
        $this->isNullable = strpos($varDocComment, 'null') !== false;
126
        self::$cache[$this->class][$this->getName()]['is_nullable'] = $this->isNullable;
127
    }
128
129
    protected function isValidType($value): bool
130
    {
131
        if (! $this->hasTypeDeclaration) {
132
            return true;
133
        }
134
135
        if ($this->isNullable && $value === null) {
136
            return true;
137
        }
138
139
        foreach ($this->types as $currentType) {
140
            $isValidType = $this->assertTypeEquals($currentType, $value);
141
142
            if ($isValidType) {
143
                return true;
144
            }
145
        }
146
147
        return false;
148
    }
149
150
    protected function cast($value)
151
    {
152
        $castTo = null;
153
154
        foreach ($this->types as $type) {
155
            if (! is_subclass_of($type, DataTransferObject::class)) {
156
                continue;
157
            }
158
159
            $castTo = $type;
160
161
            break;
162
        }
163
164
        if (! $castTo) {
165
            return $value;
166
        }
167
168
        return new $castTo($value);
169
    }
170
171
    protected function castCollection(array $values)
172
    {
173
        $castTo = null;
174
175
        foreach ($this->arrayTypes as $type) {
176
            if (! is_subclass_of($type, DataTransferObject::class)) {
177
                continue;
178
            }
179
180
            $castTo = $type;
181
182
            break;
183
        }
184
185
        if (! $castTo) {
186
            return $values;
187
        }
188
189
        $casts = [];
190
191
        foreach ($values as $value) {
192
            $casts[] = new $castTo($value);
193
        }
194
195
        return $casts;
196
    }
197
198
    protected function shouldBeCastToCollection(array $values): bool
199
    {
200
        if (empty($values)) {
201
            return false;
202
        }
203
204
        foreach ($values as $key => $value) {
205
            if (is_string($key)) {
206
                return false;
207
            }
208
209
            if (! is_array($value)) {
210
                return false;
211
            }
212
        }
213
214
        return true;
215
    }
216
217
    protected function assertTypeEquals(string $type, $value): bool
218
    {
219
        if (strpos($type, '[]') !== false) {
220
            return $this->isValidGenericCollection($type, $value);
221
        }
222
223
        if ($type === 'mixed' && $value !== null) {
224
            return true;
225
        }
226
227
        return $value instanceof $type
228
            || gettype($value) === (self::$typeMapping[$type] ?? $type);
229
    }
230
231
    protected function isValidGenericCollection(string $type, $collection): bool
232
    {
233
        if (! is_array($collection)) {
234
            return false;
235
        }
236
237
        $valueType = str_replace('[]', '', $type);
238
239
        foreach ($collection as $value) {
240
            if (! $this->assertTypeEquals($valueType, $value)) {
241
                return false;
242
            }
243
        }
244
245
        return true;
246
    }
247
}
248