Passed
Push — develop ( 9b8c4e...79e7ae )
by Andrea Marco
03:34 queued 14s
created

CasesCollectionCast::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
nc 2
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 2
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cerbero\LaravelEnum;
6
7
use BackedEnum;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Cerbero\LaravelEnum\BackedEnum. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
Bug introduced by
The type BackedEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Cerbero\LaravelEnum\Contracts\Bitwise;
9
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
10
use Illuminate\Database\Eloquent\Model;
11
use InvalidArgumentException;
12
use UnitEnum;
0 ignored issues
show
Bug introduced by
The type UnitEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
14
/**
15
 * The cases collection cast.
16
 *
17
 * @template TEnum of UnitEnum
18
 *
19
 * @implements CastsAttributes<?CasesCollection<TEnum>, mixed>
20
 */
21
class CasesCollectionCast implements CastsAttributes
22
{
23
    /**
24
     * Disable object caching.
25
     */
26
    public bool $withoutObjectCaching = true;
27
28
    /**
29
     * Instantiate the class.
30
     */
31 19
    public function __construct(private readonly string $enum)
32
    {
33 19
        if (! is_subclass_of($enum, UnitEnum::class)) {
34 1
            throw new InvalidArgumentException('The cast argument must be a valid enum');
35
        }
36
    }
37
38
    /**
39
     * Transform the attribute from the underlying model values.
40
     *
41
     * @param string|int|null $value
42
     * @param array<string, mixed> $attributes
43
     * @return ?CasesCollection<TEnum>
44
     * @throws \ValueError
45
     */
46 18
    public function get(Model $model, string $key, mixed $value, array $attributes): ?CasesCollection
47
    {
48
        /** @var ?CasesCollection<TEnum> */
49
        return match (true) {
50 18
            is_string($value) => $this->getByJson($value), /** @phpstan-ignore-next-line binaryOp.invalid */
51 7
            is_int($value) => $this->enum::filter(fn(BackedEnum $case) => ($value & $case->value) == $case->value),
52 17
            default => null,
53
        };
54
    }
55
56
    /**
57
     * Transform the given JSON into a cases collection.
58
     *
59
     * @return CasesCollection<TEnum>
60
     * @throws \ValueError
61
     */
62 11
    protected function getByJson(string $json): CasesCollection
63
    {
64
        /** @var list<string|int> $rawCases */
65 11
        $rawCases = array_unique((array) json_decode($json, true));
66
        /** @var TEnum[] $cases */
67 11
        $cases = array_map(fn(string|int $value) => $this->enum::from($value), $rawCases);
0 ignored issues
show
Bug introduced by
$rawCases of type Cerbero\LaravelEnum\list is incompatible with the type array expected by parameter $array of array_map(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

67
        $cases = array_map(fn(string|int $value) => $this->enum::from($value), /** @scrutinizer ignore-type */ $rawCases);
Loading history...
68
69
        /** @var CasesCollection<TEnum> */
70 10
        return new CasesCollection($cases);
71
    }
72
73
    /**
74
     * Transform the attribute to its underlying model values.
75
     *
76
     * @param array<string, mixed> $attributes
77
     */
78 17
    public function set(Model $model, string $key, mixed $value, array $attributes): string|int|null
79
    {
80 17
        $this->withoutObjectCaching = ! $value instanceof CasesCollection;
81
82
        return match (true) {
83 17
            $value instanceof CasesCollection => $value->toJson() ?: null,
84 15
            is_array($value) => $this->setByArray($value),
85 4
            is_int($value) => $value,
86 17
            default => null,
87
        };
88
    }
89
90
    /**
91
     * Transform the given array into a serializable string.
92
     *
93
     * @param array<array-key, mixed> $array
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<array-key, mixed> at position 2 could not be parsed: Unknown type name 'array-key' at position 2 in array<array-key, mixed>.
Loading history...
94
     */
95 11
    protected function setByArray(array $array): string|int|null
96
    {
97 11
        if (is_subclass_of($this->enum, Bitwise::class)) {
98 2
            return array_reduce($array, function (?int $carry, mixed $item): int {
99
                /** @phpstan-ignore-next-line binaryOp.invalid */
100 2
                return $carry |= $item instanceof BackedEnum ? $item->value : $item;
101 2
            });
102
        }
103
104 9
        $values = reset($array) instanceof UnitEnum
105 4
            ? $this->enum::only(...array_column($array, 'name'))
106 5
            : array_values(array_unique($array));
107
108 9
        return json_encode($values) ?: null;
109
    }
110
}
111