Enum   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 244
Duplicated Lines 0 %

Test Coverage

Coverage 94.74%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 45
c 2
b 0
f 0
dl 0
loc 244
ccs 54
cts 57
cp 0.9474
rs 10
wmc 22

15 Methods

Rating   Name   Duplication   Size   Complexity  
A name() 0 3 1
A values() 0 3 1
A initValue() 0 2 1
A valueOf() 0 14 3
A containsValue() 0 3 1
A __callStatic() 0 3 1
A __set() 0 2 1
A containsKey() 0 3 1
A fromValue() 0 11 2
A value() 0 3 1
A __construct() 0 5 1
A __clone() 0 3 1
A ordinal() 0 7 2
A getEnumConstants() 0 12 3
A __toString() 0 6 2
1
<?php
2
/** @noinspection MagicMethodsValidityInspection */
3
declare(strict_types=1);
4
5
/**
6
 * @author   Ne-Lexa
7
 * @license  MIT
8
 * @link     https://github.com/Ne-Lexa/enum
9
 */
10
11
namespace Nelexa;
12
13
/**
14
 * Based functional of Enum type.
15
 */
16
abstract class Enum
17
{
18
    /**
19
     * Contains cache already created by enum.
20
     *
21
     * @var self[][]
22
     * @internal
23
     */
24
    private static $instances = [];
25
26
    /** @var string Constant name. */
27
    private $name;
28
29
    /** @var string|int|float|bool|array|null Constant value */
30
    private $value;
31
32
    /**
33
     * Enum constructor.
34
     *
35
     * @param string $name Constant name.
36
     * @param string|int|float|bool|array|null $value Constant value.
37
     */
38 21
    final private function __construct(string $name, $value)
39
    {
40 21
        $this->name = $name;
41 21
        $this->value = $value;
42 21
        $this->initValue($value);
43 21
    }
44
45
    /**
46
     * In this method, you can initialize additional variables based on the
47
     * value of the constant. The method is called after the constructor.
48
     *
49
     * @param string|int|float|bool|array|null $value Constant value.
50
     */
51 20
    protected function initValue($value): void
52
    {
53 20
    }
54
55
    /**
56
     * Returns the enum of the specified constant name.
57
     *
58
     * The name must match exactly an identifier used to declare an enum constant
59
     * in this type. (Extraneous whitespace characters are not permitted.)
60
     *
61
     * @param string $name Constant name.
62
     * @param array $arguments Arguments (currently not used).
63
     *
64
     * @return static Object of subtype Enum.
65
     *
66
     * @internal
67
     */
68 28
    final public static function __callStatic(string $name, $arguments): self
69
    {
70 28
        return self::valueOf($name);
71
    }
72
73
    /**
74
     * Returns the enum of the specified constant name.
75
     *
76
     * The name must match exactly an identifier used to declare an enum constant
77
     * in this type. (Extraneous whitespace characters are not permitted.)
78
     *
79
     * @param string $name the name of the constant
80
     *
81
     * @return static the enum constant of the specified enum type with the specified name
82
     */
83 46
    final public static function valueOf(string $name): self
84
    {
85 46
        if (isset(self::$instances[static::class][$name])) {
86 43
            return self::$instances[static::class][$name];
87
        }
88 23
        $constants = self::getEnumConstants();
89 23
        if (!array_key_exists($name, $constants)) {
90 2
            throw new \InvalidArgumentException(sprintf(
91 2
                'Constant named "%s" is not defined in the %s class.',
92 2
                $name,
93 2
                static::class
94
            ));
95
        }
96 21
        return self::$instances[static::class][$name] = new static($name, $constants[$name]);
97
    }
98
99
    /**
100
     * Returns an array with class constants.
101
     *
102
     * @return array Array of constants.
103
     */
104 42
    protected static function getEnumConstants(): array
105
    {
106 42
        static $enumConstants = [];
107 42
        if (!isset($enumConstants[static::class])) {
108
            try {
109 5
                $reflectionClass = new \ReflectionClass(static::class);
110 5
                $enumConstants[static::class] = $reflectionClass->getConstants();
111
            } catch (\ReflectionException $e) {
112
                throw new \RuntimeException($e->getMessage(), $e->getCode(), $e);
113
            }
114
        }
115 42
        return $enumConstants[static::class];
116
    }
117
118
    /**
119
     * Returns the name of this enum constant.
120
     *
121
     * @return string Constant name.
122
     */
123 22
    final public function name(): string
124
    {
125 22
        return $this->name;
126
    }
127
128
    /**
129
     * Returns the scalar value of this enum constant.
130
     *
131
     * @return string|int|float|bool|array|null Constant value.
132
     */
133 19
    final public function value()
134
    {
135 19
        return $this->value;
136
    }
137
138
    /**
139
     * Returns an array containing the constants of this enum type, in the order they're declared.
140
     *
141
     * This method may be used to iterate over the constants as follows:
142
     *
143
     * ```php
144
     * foreach(EnumClass::values() as $enum) {
145
     *     echo $enum->name() . ' => ' . $enum->value() . PHP_EOL;
146
     * }
147
     * ```
148
     *
149
     * @return static[] An array of constants of this type enum in the order they are declared.
150
     */
151 4
    final public static function values(): array
152
    {
153 4
        return array_map('self::valueOf', array_keys(self::getEnumConstants()));
154
    }
155
156
    /**
157
     * Checks whether the constant name is present in the enum.
158
     *
159
     * @param string $name Constant name.
160
     *
161
     * @return bool Returns true if the name is defined in one of the constants.
162
     */
163 2
    final public static function containsKey(string $name): bool
164
    {
165 2
        return isset(self::getEnumConstants()[$name]);
166
    }
167
168
    /**
169
     * Checks if enum contains a passed value.
170
     *
171
     * @param string|int|float|bool|array|null $value Checked value.
172
     * @param bool $strict Strict check.
173
     *
174
     * @return bool Returns true if the value is defined in one of the constants.
175
     */
176 1
    final public static function containsValue($value, bool $strict = true): bool
177
    {
178 1
        return in_array($value, self::getEnumConstants(), $strict);
179
    }
180
181
    /**
182
     * Returns first enum of the specified constant value.
183
     *
184
     * @param string|int|float|bool|array|null $value Checked value.
185
     *
186
     * @return static the enum constant of the specified constant value.
187
     */
188 17
    final public static function fromValue($value): self
189
    {
190 17
        $key = array_search($value, self::getEnumConstants(), true);
191 17
        if ($key === false) {
192 1
            throw new \InvalidArgumentException(sprintf(
193 1
                'Constant value "%s" is not defined in the %s class.',
194 1
                $value,
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type array; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

194
                /** @scrutinizer ignore-type */ $value,
Loading history...
195 1
                static::class
196
            ));
197
        }
198 16
        return self::valueOf($key);
199
    }
200
201
    /**
202
     * Returns the ordinal of this enum constant.
203
     *
204
     * The first constant is assigned a sequence number of zero.
205
     *
206
     * @return int Ordinal of this enumeration constant.
207
     */
208 17
    final public function ordinal(): int
209
    {
210 17
        $key = array_search($this->name, array_keys(self::getEnumConstants()), true);
211 17
        if ($key === false) {
212
            throw new \RuntimeException(sprintf('Not found the ordinal number of the constant %s', $this->name));
213
        }
214 17
        return $key;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $key could return the type string which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
215
    }
216
217
    /**
218
     * Returns the value of this enum constant, as contained in the declaration.
219
     *
220
     * This method may be overridden, though it typically isn't necessary or desirable.
221
     * An enum type should override this method when a more "programmer-friendly"
222
     * string form exists.
223
     *
224
     * @return string Value of this enum constant (the array will be serialized in json).
225
     */
226 17
    public function __toString()
227
    {
228 17
        if (is_array($this->value)) {
229 2
            return (string)json_encode($this->value);
230
        }
231 15
        return (string)$this->value;
232
    }
233
234
    /**
235
     * Enum cloning.
236
     *
237
     * This method guarantees that enums are never cloned,
238
     * which is necessary to preserve their "singleton" status.
239
     *
240
     * @throws \LogicException Always throw an exception.
241
     *
242
     * @internal
243
     */
244 1
    final public function __clone()
245
    {
246 1
        throw new \LogicException('Enums are not cloneable');
247
    }
248
249
    /**
250
     * Protects the object from mutability and prevents the setting
251
     * of new properties for the object.
252
     *
253
     * @param mixed $name Name
254
     * @param mixed $value Value
255
     *
256
     * @internal
257
     */
258 1
    final public function __set($name, $value)
259
    {
260 1
    }
261
}
262