Passed
Pull Request — master (#56)
by Brent
02:21
created

Enum::resolve()   B

Complexity

Conditions 8
Paths 49

Size

Total Lines 56
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
cc 8
eloc 30
c 8
b 0
f 0
nc 49
nop 0
dl 0
loc 56
rs 8.1954

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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