Enum::values()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
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
use Spatie\Enum\Exceptions\UnknownEnumMethod;
11
use Spatie\Enum\Exceptions\UnknownEnumProperty;
12
use TypeError;
13
14
/**
15
 * @property-read string|int value
16
 * @property-read string label
17
 */
18
abstract class Enum implements JsonSerializable
19
{
20
    /** @var string|int */
21
    protected $value;
22
23
    protected string $label;
24
25
    private static array $definitionCache = [];
26
27
    public static function toArray(): array
28
    {
29
        $array = [];
30
31
        foreach (static::resolveDefinition() as $definition) {
32
            $array[$definition->value] = $definition->label;
33
        }
34
35
        return $array;
36
    }
37
38
    /**
39
     * @param string|int $value
40
     *
41
     * @return static
42
     */
43
    public static function make($value): Enum
44
    {
45
        return new static($value);
46
    }
47
48
    /**
49
     * @param string|int $value
50
     *
51
     * @internal
52
     */
53
    public function __construct($value)
54
    {
55
        if (! (is_string($value) || is_int($value))) {
0 ignored issues
show
introduced by
The condition is_int($value) is always true.
Loading history...
56
            $enumClass = static::class;
57
58
            throw new TypeError("Only string and integer are allowed values for enum {$enumClass}.");
59
        }
60
61
        $definition = $this->findDefinition($value);
62
63
        if ($definition === null) {
64
            $enumClass = static::class;
65
66
            throw new BadMethodCallException("There's no value {$value} defined for enum {$enumClass}, consider adding it in the docblock definition.");
67
        }
68
69
        $this->value = $definition->value;
0 ignored issues
show
Bug introduced by
The property value is declared read-only in Spatie\Enum\Enum.
Loading history...
70
        $this->label = $definition->label;
0 ignored issues
show
Bug introduced by
The property label is declared read-only in Spatie\Enum\Enum.
Loading history...
71
    }
72
73
    /**
74
     * @param string $name
75
     *
76
     * @return int|string
77
     *
78
     * @throws UnknownEnumProperty
79
     */
80
    public function __get(string $name)
81
    {
82
        if ($name === 'label') {
83
            return $this->label;
84
        }
85
86
        if ($name === 'value') {
87
            return $this->value;
88
        }
89
90
        throw UnknownEnumProperty::new(static::class, $name);
91
    }
92
93
    /**
94
     * @param string $name
95
     * @param array $arguments
96
     *
97
     * @return static
98
     */
99
    public static function __callStatic(string $name, array $arguments)
100
    {
101
        return new static($name);
102
    }
103
104
    /**
105
     * @param string $name
106
     * @param array $arguments
107
     *
108
     * @return bool
109
     *
110
     * @throws UnknownEnumMethod
111
     */
112
    public function __call(string $name, array $arguments)
113
    {
114
        if (strpos($name, 'is') === 0) {
115
            $other = new static(substr($name, 2));
116
117
            return $this->equals($other);
118
        }
119
120
        throw UnknownEnumMethod::new(static::class, $name);
121
    }
122
123
    public function equals(Enum ...$others): bool
124
    {
125
        foreach ($others as $other) {
126
            if (
127
                get_class($this) === get_class($other)
128
                && $this->value === $other->value
129
            ) {
130
                return true;
131
            }
132
        }
133
134
        return false;
135
    }
136
137
    protected static function values(): array
138
    {
139
        return [];
140
    }
141
142
    protected static function labels(): array
143
    {
144
        return [];
145
    }
146
147
    /**
148
     * @param string|int $input
149
     *
150
     * @return \Spatie\Enum\EnumDefinition|null
151
     */
152
    private function findDefinition($input): ?EnumDefinition
153
    {
154
        foreach (static::resolveDefinition() as $definition) {
155
            if ($definition->equals($input)) {
156
                return $definition;
157
            }
158
        }
159
160
        return null;
161
    }
162
163
    /**
164
     * @return \Spatie\Enum\EnumDefinition[]
165
     */
166
    private static function resolveDefinition(): array
167
    {
168
        $className = static::class;
169
170
        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...
171
            return static::$definitionCache[$className];
172
        }
173
174
        $reflectionClass = new ReflectionClass($className);
175
176
        $docComment = $reflectionClass->getDocComment();
177
178
        preg_match_all('/@method static self ([\w_]+)\(\)/', $docComment, $matches);
179
180
        $definition = [];
181
182
        $valueMap = static::values();
183
184
        $labelMap = static::labels();
185
186
        foreach ($matches[1] as $methodName) {
187
            $value = $valueMap[$methodName] = $valueMap[$methodName] ?? $methodName;
188
189
            $label = $labelMap[$methodName] = $labelMap[$methodName] ?? $methodName;
190
191
            $definition[$methodName] = new EnumDefinition($methodName, $value, $label);
192
        }
193
194
        if (self::arrayHasDuplicates($valueMap)) {
195
            throw new DuplicateValuesException(static::class);
196
        }
197
198
        if (self::arrayHasDuplicates($labelMap)) {
199
            throw new DuplicateLabelsException(static::class);
200
        }
201
202
        return static::$definitionCache[$className] ??= $definition;
203
    }
204
205
    private static function arrayHasDuplicates(array $array): bool
206
    {
207
        return count($array) > count(array_unique($array));
208
    }
209
210
    /**
211
     * @return int|string
212
     */
213
    public function jsonSerialize()
214
    {
215
        return $this->value;
216
    }
217
218
    public function __toString(): string
219
    {
220
        return (string) $this->value;
221
    }
222
}
223