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 ( 23e01c...b1b0e4 )
by Tom
48:10 queued 33:10
created

HasEnums::setEnumAttribute()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 11
cts 11
cp 1
rs 8.8657
c 0
b 0
f 0
cc 6
nc 6
nop 2
crap 6
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\InvalidEnumError;
11
use Spatie\Enum\Laravel\Exceptions\NoSuchEnumField;
12
13
/**
14
 * @mixin Model
15
 */
16
trait HasEnums
17 80
{
18
    public function setAttribute($key, $value)
19 80
    {
20 80
        return $this->isEnumAttribute($key)
21 72
            ? $this->setEnumAttribute($key, $value)
22
            : parent::setAttribute($key, $value);
23
    }
24 28
25
    public function getAttribute($key)
26 28
    {
27
        $value = parent::getAttribute($key);
28 28
29 28
        return $this->isEnumAttribute($key)
30 28
            ? $this->getEnumAttribute($key, $value)
31
            : $value;
32
    }
33
34
    /**
35
     * @param \Illuminate\Database\Eloquent\Builder $builder
36
     * @param string $key
37
     * @param int|string|\Spatie\Enum\Enumerable|int[]|string[]|\Spatie\Enum\Enumerable[] $enumerables
38
     *
39
     * @see \Illuminate\Database\Eloquent\Builder::whereIn()
40 32
     */
41
    public function scopeWhereEnum(
42
        Builder $builder,
43
        string $key,
44
        $enumerables
45 32
    ): void {
46 32
        $this->buildEnumScope(
47 32
            $builder,
48 16
            'whereIn',
49 16
            $key,
50
            $enumerables
51 28
        );
52
    }
53
54
    /**
55
     * @param \Illuminate\Database\Eloquent\Builder $builder
56
     * @param string $key
57
     * @param int|string|\Spatie\Enum\Enumerable|int[]|string[]|\Spatie\Enum\Enumerable[] $enumerables
58
     *
59
     * @see \Illuminate\Database\Eloquent\Builder::orWhereIn()
60 12
     */
61
    public function scopeOrWhereEnum(
62
        Builder $builder,
63
        string $key,
64
        $enumerables
65 12
    ): void {
66 12
        $this->buildEnumScope(
67 12
            $builder,
68 6
            'orWhereIn',
69 6
            $key,
70
            $enumerables
71 8
        );
72
    }
73
74
    /**
75
     * @param \Illuminate\Database\Eloquent\Builder $builder
76
     * @param string $key
77
     * @param int|string|\Spatie\Enum\Enumerable|int[]|string[]|\Spatie\Enum\Enumerable[] $enumerables
78
     *
79
     * @see \Illuminate\Database\Eloquent\Builder::whereNotIn()
80 20
     */
81
    public function scopeWhereNotEnum(
82
        Builder $builder,
83
        string $key,
84
        $enumerables
85 20
    ): void {
86 20
        $this->buildEnumScope(
87 20
            $builder,
88 10
            'whereNotIn',
89 10
            $key,
90
            $enumerables
91 16
        );
92
    }
93
94
    /**
95
     * @param \Illuminate\Database\Eloquent\Builder $builder
96
     * @param string $key
97
     * @param int|string|\Spatie\Enum\Enumerable|int[]|string[]|\Spatie\Enum\Enumerable[] $enumerables
98
     *
99
     * @see \Illuminate\Database\Eloquent\Builder::orWhereNotIn()
100 12
     */
101
    public function scopeOrWhereNotEnum(
102
        Builder $builder,
103
        string $key,
104
        $enumerables
105 12
    ): void {
106 12
        $this->buildEnumScope(
107 12
            $builder,
108 6
            'orWhereNotIn',
109 6
            $key,
110
            $enumerables
111 8
        );
112
    }
113
114
    /**
115
     * @param string $key
116
     * @param int|string|\Spatie\Enum\Enumerable $value
117
     *
118
     * @return $this
119 80
     */
120
    protected function setEnumAttribute(string $key, $value)
121 80
    {
122
        $enumClass = $this->getEnumClass($key);
123 76
124 16
        if (is_null($value)) {
125
            if (! $this->isNullableEnum($key)) {
126
                $enumInterface = Enumerable::class;
127 76
                throw new InvalidArgumentException("{$enumInterface} {$enumClass} is not nullable");
128 4
            }
129
130
            $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...
131 72
132
            return $this;
133 72
        }
134
135
        if (is_string($value) || is_int($value)) {
136
            $value = $this->asEnum($enumClass, $value);
137
        }
138
139
        if (! is_a($value, $enumClass)) {
140
            throw InvalidEnumError::make(static::class, $key, $enumClass, get_class($value));
141
        }
142 72
143
        $this->attributes[$key] = $this->getStoredValue($key, $value);
144 72
145 4
        return $this;
146 72
    }
147
148
    /**
149
     * @param string $key
150
     * @param \Spatie\Enum\Enumerable $enum
151
     *
152
     * @return int|string
153
     */
154
    protected function getStoredValue(string $key, Enumerable $enum)
155 72
    {
156
        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...
157 72
            ? $enum->getIndex()
158
            : $enum->getValue();
159
    }
160 96
161
    /**
162 96
     * @param string $key
163
     * @param int|string|null $value
164
     *
165 80
     * @return \Spatie\Enum\Enumerable|null
166
     */
167 80
    protected function getEnumAttribute(string $key, $value): ?Enumerable
168 80
    {
169 80
        if (is_null($value) && $this->isNullableEnum($key)) {
170
            return null;
171 80
        }
172 4
173
        return $this->asEnum($this->getEnumClass($key), $value);
174
    }
175 76
176
    protected function isEnumAttribute(string $key): bool
177
    {
178
        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...
179
    }
180
181
    protected function getEnumCast(string $key): array
182
    {
183
        $enumCast = $this->enums[$key];
184 72
185
        if (! Str::contains($enumCast, ':')) {
186 72
            return [$enumCast, []];
187 36
        }
188
189
        [$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...
190 40
191 40
        $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...
192 20
193
        return [$enumClass, $options];
194
    }
195
196
    protected function isNullableEnum(string $key): bool
197
    {
198
        [, $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...
199
200
        return in_array('nullable', $options);
201
    }
202 60
203
    protected function getEnumClass(string $key): string
204
    {
205
        [$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...
206
207
        $enumInterface = Enumerable::class;
208 60
209 16
        $classImplementsEnumerable = class_implements($enumClass)[$enumInterface] ?? false;
210
211
        if (! $classImplementsEnumerable) {
212 44
            throw new InvalidArgumentException("Expected {$enumClass} to implement {$enumInterface}");
213
        }
214 44
215 44
        return $enumClass;
216
    }
217 44
218 44
    /**
219
     * @param string $class
220 44
     * @param int|string $value
221
     *
222
     * @return \Spatie\Enum\Enumerable
223
     */
224
    protected function asEnum(string $class, $value): Enumerable
225
    {
226
        if ($value instanceof Enumerable) {
227
            return $value;
228
        }
229
230
        return forward_static_call(
231
            $class.'::make',
232
            $value
233
        );
234
    }
235
236
    /**
237
     * @param \Illuminate\Database\Eloquent\Builder $builder
238
     * @param string $method
239
     * @param string $key
240
     * @param int|string|\Spatie\Enum\Enumerable|int[]|string[]|\Spatie\Enum\Enumerable[] $enumerables
241
     */
242
    protected function buildEnumScope(
243
        Builder $builder,
244
        string $method,
245
        string $key,
246
        $enumerables
247
    ): void {
248
        if (! $this->isEnumAttribute($key)) {
249
            throw NoSuchEnumField::make($key, get_class($this));
250
        }
251
252
        $enumerables = is_array($enumerables) ? $enumerables : [$enumerables];
253
254
        $builder->$method(
255
            $key,
256
            array_map(function ($value) use ($key) {
257
                return $this->getStoredValue($key, $this->getEnumAttribute($key, $value));
0 ignored issues
show
Bug introduced by
It seems like $this->getEnumAttribute($key, $value) can be null; however, getStoredValue() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
258
            }, $enumerables)
259
        );
260
    }
261
}
262