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

Enum::isValidName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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