Passed
Pull Request — master (#56)
by Brent
01:50
created

Enum::resolveByIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 6
rs 10
1
<?php
2
3
namespace Spatie\Enum;
4
5
use BadMethodCallException;
6
use JsonSerializable;
7
use ReflectionClass;
8
use Spatie\Enum\Exceptions\DuplicateLabelsException;
9
use Spatie\Enum\Exceptions\DuplicateValuesException;
10
11
/**
12
 * @property-read string value
13
 * @property-read string label
14
 */
15
abstract class Enum implements JsonSerializable
16
{
17
    /** @var string|int */
18
    protected $value;
19
20
    protected string $label;
21
22
    private static array $definitionCache = [];
23
24
    public static function toArray(): array
25
    {
26
        $array = [];
27
28
        foreach (static::resolveDefinition() as $definition) {
29
            $array[$definition->value] = $definition->label;
30
        }
31
32
        return $array;
33
    }
34
35
    /**
36
     * @param string|int $value
37
     */
38
    public static function make($value): Enum
39
    {
40
        return new static($value);
41
    }
42
43
    /**
44
     * @param string|int $value
45
     */
46
    public function __construct($value)
47
    {
48
        $definition = $this->findDefinition($value);
49
50
        if ($definition === null) {
51
            $enumClass = static::class;
52
53
            throw new BadMethodCallException("There's no value {$value} defined for enum {$enumClass}, consider adding it in the docblock definition.");
54
        }
55
56
        $this->value = $definition->value;
0 ignored issues
show
Bug introduced by
The property value is declared read-only in Spatie\Enum\Enum.
Loading history...
57
        $this->label = $definition->label;
0 ignored issues
show
Bug introduced by
The property label is declared read-only in Spatie\Enum\Enum.
Loading history...
58
    }
59
60
    public function __get($name)
61
    {
62
        if ($name === 'label') {
63
            return $this->label;
64
        }
65
66
        if ($name === 'value') {
67
            return $this->value;
68
        }
69
    }
70
71
    public static function __callStatic(string $name, array $arguments)
72
    {
73
        return new static($name);
74
    }
75
76
    public function __call($name, $arguments)
77
    {
78
        if (strpos($name, 'is') === 0) {
79
            $other = new static(str_replace('is', '', $name));
80
81
            return $this->equals($other);
82
        }
83
    }
84
85
    public function equals(Enum ...$others): bool
86
    {
87
        foreach ($others as $other) {
88
            if (
89
                get_class($this) === get_class($other)
90
                && $this->value === $other->value
91
            ) {
92
                return true;
93
            }
94
        }
95
96
        return false;
97
    }
98
99
    protected static function values(): array
100
    {
101
        return [];
102
    }
103
104
    protected static function labels(): array
105
    {
106
        return [];
107
    }
108
109
    private function findDefinition($input): ?EnumDefinition
110
    {
111
        foreach (static::resolveDefinition() as $definition) {
112
            if ($definition->equals($input)) {
113
                return $definition;
114
            }
115
        }
116
117
        return null;
118
    }
119
120
    /**
121
     * @return \Spatie\Enum\EnumDefinition[]|null
122
     */
123
    private static function resolveDefinition(): array
124
    {
125
        $className = static::class;
126
127
        if (static::$definitionCache[$className] ?? null) {
0 ignored issues
show
Bug introduced by
Since $definitionCache is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $definitionCache to at least protected.
Loading history...
128
            return static::$definitionCache[$className];
129
        }
130
131
        $reflectionClass = new ReflectionClass($className);
132
133
        $docComment = $reflectionClass->getDocComment();
134
135
        preg_match_all('/@method static self ([\w_]+)\(\)/', $docComment, $matches);
136
137
        $definition = [];
138
139
        $valueMap = static::values();
140
141
        if (self::arrayHasDuplicates($valueMap)) {
142
            throw new DuplicateValuesException(static::class);
143
        }
144
145
        $labelMap = static::labels();
146
147
        if (self::arrayHasDuplicates($labelMap)) {
148
            throw new DuplicateLabelsException(static::class);
149
        }
150
151
        foreach ($matches[1] as $methodName) {
152
            $value = $valueMap[$methodName] ?? $methodName;
153
154
            $label = $labelMap[$methodName] ?? $methodName;
155
156
            $definition[$methodName] = new EnumDefinition($methodName, $value, $label);
157
        }
158
159
        return static::$definitionCache[$className] ??= $definition;
160
    }
161
162
    private static function arrayHasDuplicates(array $array): bool
163
    {
164
        return count($array) > count(array_unique($array));
165
    }
166
167
    public function jsonSerialize(): string
168
    {
169
        return (string) $this->value;
170
    }
171
}
172