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 ( af3ff3...b6c426 )
by Tom
14s queued 12s
created

HasEnums::setAttribute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 2
1
<?php
2
3
namespace Spatie\Enum\Laravel;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Support\Str;
8
use InvalidArgumentException;
9
use Spatie\Enum\Enumerable;
10
use Spatie\Enum\Laravel\Exceptions\ExpectsArrayOfEnumsField;
11
use Spatie\Enum\Laravel\Exceptions\InvalidEnumError;
12
use Spatie\Enum\Laravel\Exceptions\NoSuchEnumField;
13
use Spatie\Enum\Laravel\Exceptions\NotNullableEnumField;
14
15
/**
16
 * @mixin Model
17 80
 */
18
trait HasEnums
19 80
{
20 80
    public function setAttribute($key, $value)
21 72
    {
22
        return $this->isEnumAttribute($key)
23
            ? $this->setEnumAttribute($key, $value)
24 28
            : parent::setAttribute($key, $value);
25
    }
26 28
27
    public function getAttribute($key)
28 28
    {
29 28
        $value = parent::getAttribute($key);
30 28
31
        return $this->isEnumAttribute($key)
32
            ? $this->getEnumAttribute($key, $value)
33
            : $value;
34
    }
35
36
    /**
37
     * @param \Illuminate\Database\Eloquent\Builder $builder
38
     * @param string $key
39
     * @param int|string|\Spatie\Enum\Enumerable|int[]|string[]|\Spatie\Enum\Enumerable[] $enumerables
40 32
     *
41
     * @see \Illuminate\Database\Eloquent\Builder::whereIn()
42
     */
43
    public function scopeWhereEnum(
44
        Builder $builder,
45 32
        string $key,
46 32
        $enumerables
47 32
    ): void {
48 16
        $this->buildEnumScope(
49 16
            $builder,
50
            'whereIn',
51 28
            $key,
52
            $enumerables
53
        );
54
    }
55
56
    /**
57
     * @param \Illuminate\Database\Eloquent\Builder $builder
58
     * @param string $key
59
     * @param int|string|\Spatie\Enum\Enumerable|int[]|string[]|\Spatie\Enum\Enumerable[] $enumerables
60 12
     *
61
     * @see \Illuminate\Database\Eloquent\Builder::orWhereIn()
62
     */
63
    public function scopeOrWhereEnum(
64
        Builder $builder,
65 12
        string $key,
66 12
        $enumerables
67 12
    ): void {
68 6
        $this->buildEnumScope(
69 6
            $builder,
70
            'orWhereIn',
71 8
            $key,
72
            $enumerables
73
        );
74
    }
75
76
    /**
77
     * @param \Illuminate\Database\Eloquent\Builder $builder
78
     * @param string $key
79
     * @param int|string|\Spatie\Enum\Enumerable|int[]|string[]|\Spatie\Enum\Enumerable[] $enumerables
80 20
     *
81
     * @see \Illuminate\Database\Eloquent\Builder::whereNotIn()
82
     */
83
    public function scopeWhereNotEnum(
84
        Builder $builder,
85 20
        string $key,
86 20
        $enumerables
87 20
    ): void {
88 10
        $this->buildEnumScope(
89 10
            $builder,
90
            'whereNotIn',
91 16
            $key,
92
            $enumerables
93
        );
94
    }
95
96
    /**
97
     * @param \Illuminate\Database\Eloquent\Builder $builder
98
     * @param string $key
99
     * @param int|string|\Spatie\Enum\Enumerable|int[]|string[]|\Spatie\Enum\Enumerable[] $enumerables
100 12
     *
101
     * @see \Illuminate\Database\Eloquent\Builder::orWhereNotIn()
102
     */
103
    public function scopeOrWhereNotEnum(
104
        Builder $builder,
105 12
        string $key,
106 12
        $enumerables
107 12
    ): void {
108 6
        $this->buildEnumScope(
109 6
            $builder,
110
            'orWhereNotIn',
111 8
            $key,
112
            $enumerables
113
        );
114
    }
115
116
    /**
117
     * @param string $key
118
     * @param int|int[]|string|string[]|\Spatie\Enum\Enumerable|\Spatie\Enum\Enumerable[] $value
119 80
     *
120
     * @return $this
121 80
     */
122
    protected function setEnumAttribute(string $key, $value)
123 76
    {
124 16
        $enumClass = $this->getEnumClass($key);
125
126
        if (is_null($value)) {
127 76
            if (! $this->isNullableEnum($key)) {
128 4
                throw NotNullableEnumField::make($key, static::class);
129
            }
130
131 72
            $this->attributes[$key] = null;
0 ignored issues
show
Bug introduced by
The property attributes does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
132
133 72
            return $this;
134
        }
135
136
        if ($this->isArrayOfEnums($key)) {
137
            if (! is_array($value)) {
138
                throw ExpectsArrayOfEnumsField::make($key, static::class, $enumClass);
139
            }
140
141
            return parent::setAttribute($key, array_map(function ($value) use ($key, $enumClass) {
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (setAttribute() instead of setEnumAttribute()). Are you sure this is correct? If so, you might want to change this to $this->setAttribute().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
142 72
                return $this->getStoredValue($key, $this->asEnum($enumClass, $value));
143
            }, $value));
144 72
        }
145 4
146 72
        if (is_string($value) || is_int($value)) {
147
            $value = $this->asEnum($enumClass, $value);
148
        }
149
150
        if (! is_a($value, $enumClass)) {
151
            throw InvalidEnumError::make(static::class, $key, $enumClass, get_class($value));
152
        }
153
154
        $this->attributes[$key] = $this->getStoredValue($key, $value);
0 ignored issues
show
Bug introduced by
It seems like $value defined by parameter $value on line 122 can also be of type array<integer,integer> or array<integer,object<Spatie\Enum\Enumerable>> or array<integer,string>; however, Spatie\Enum\Laravel\HasEnums::getStoredValue() does only seem to accept object<Spatie\Enum\Enumerable>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
155 72
156
        return $this;
157 72
    }
158
159
    /**
160 96
     * @param string $key
161
     * @param \Spatie\Enum\Enumerable $enum
162 96
     *
163
     * @return int|string
164
     */
165 80
    protected function getStoredValue(string $key, Enumerable $enum)
166
    {
167 80
        return $this->hasCast($key, ['int', 'integer'])
0 ignored issues
show
Bug introduced by
It seems like hasCast() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
168 80
            ? $enum->getIndex()
169 80
            : $enum->getValue();
170
    }
171 80
172 4
    /**
173
     * @param string $key
174
     * @param int|int[]|string|string[]|null $value
175 76
     *
176
     * @return \Spatie\Enum\Enumerable|\Spatie\Enum\Enumerable[]|null
177
     */
178
    protected function getEnumAttribute(string $key, $value)
179
    {
180
        if (is_null($value) && $this->isNullableEnum($key)) {
181
            return;
182
        }
183
184 72
        $enumClass = $this->getEnumClass($key);
185
186 72
        if ($this->isArrayOfEnums($key)) {
187 36
            if (! is_array($value)) {
188
                throw ExpectsArrayOfEnumsField::make($key, static::class, $enumClass);
189
            }
190 40
191 40
            return array_map(function ($value) use ($enumClass): Enumerable {
192 20
                return $this->asEnum($enumClass, $value);
193
            }, $value);
194
        }
195
196
        return $this->asEnum($enumClass, $value);
197
    }
198
199
    protected function isEnumAttribute(string $key): bool
200
    {
201
        return isset($this->enums[$key]);
0 ignored issues
show
Bug introduced by
The property enums does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
202 60
    }
203
204
    protected function getEnumCast(string $key): array
205
    {
206
        $enumCast = $this->enums[$key];
207
208 60
        if (! Str::contains($enumCast, ':')) {
209 16
            return [$enumCast, []];
210
        }
211
212 44
        [$enumClass, $options] = explode(':', $enumCast);
0 ignored issues
show
Bug introduced by
The variable $enumClass does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $options seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
213
214 44
        $options = explode(',', $options);
0 ignored issues
show
Bug introduced by
The variable $options seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
215 44
216
        return [$enumClass, $options];
217 44
    }
218 44
219
    protected function isNullableEnum(string $key): bool
220 44
    {
221
        [, $options] = $this->getEnumCast($key);
0 ignored issues
show
Bug introduced by
The variable $options does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
222
223
        return in_array('nullable', $options);
224
    }
225
226
    protected function isArrayOfEnums(string $key): bool
227
    {
228
        [, $options] = $this->getEnumCast($key);
0 ignored issues
show
Bug introduced by
The variable $options does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
229
230
        return in_array('array', $options);
231
    }
232
233
    protected function getEnumClass(string $key): string
234
    {
235
        [$enumClass] = $this->getEnumCast($key);
0 ignored issues
show
Bug introduced by
The variable $enumClass does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
236
237
        $enumInterface = Enumerable::class;
238
239
        $classImplementsEnumerable = class_implements($enumClass)[$enumInterface] ?? false;
240
241
        if (! $classImplementsEnumerable) {
242
            throw new InvalidArgumentException("Expected {$enumClass} to implement {$enumInterface}");
243
        }
244
245
        return $enumClass;
246
    }
247
248
    /**
249
     * @param string $class
250
     * @param int|string $value
251
     *
252
     * @return \Spatie\Enum\Enumerable
253
     */
254
    protected function asEnum(string $class, $value): Enumerable
255
    {
256
        if ($value instanceof Enumerable) {
257
            return $value;
258
        }
259
260
        return forward_static_call(
261
            $class.'::make',
262
            $value
263
        );
264
    }
265
266
    /**
267
     * @param \Illuminate\Database\Eloquent\Builder $builder
268
     * @param string $method
269
     * @param string $key
270
     * @param int|string|\Spatie\Enum\Enumerable|int[]|string[]|\Spatie\Enum\Enumerable[] $enumerables
271
     */
272
    protected function buildEnumScope(
273
        Builder $builder,
274
        string $method,
275
        string $key,
276
        $enumerables
277
    ): void {
278
        if (! $this->isEnumAttribute($key)) {
279
            throw NoSuchEnumField::make($key, static::class);
280
        }
281
282
        $enumerables = is_array($enumerables) ? $enumerables : [$enumerables];
283
284
        $builder->$method(
285
            $key,
286
            array_map(function ($value) use ($key) {
287
                return $this->getStoredValue($key, $this->getEnumAttribute($key, $value));
0 ignored issues
show
Bug introduced by
It seems like $this->getEnumAttribute($key, $value) targeting Spatie\Enum\Laravel\HasEnums::getEnumAttribute() can also be of type array or null; however, Spatie\Enum\Laravel\HasEnums::getStoredValue() does only seem to accept object<Spatie\Enum\Enumerable>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
288
            }, $enumerables)
289
        );
290
    }
291
}
292