Completed
Push — master ( 173ef8...6b591a )
by Julián
41:13
created

AbstractEnum::assertFinal()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 8
rs 10
cc 3
nc 3
nop 0
1
<?php
2
3
/*
4
 * enum (https://github.com/phpgears/enum).
5
 * Enum object for PHP.
6
 *
7
 * @license MIT
8
 * @link https://github.com/phpgears/enum
9
 * @author Julián Gutiérrez <[email protected]>
10
 */
11
12
declare(strict_types=1);
13
14
namespace Gears\Enum;
15
16
use Gears\Enum\Exception\EnumException;
17
use Gears\Enum\Exception\InvalidEnumNameException;
18
use Gears\Enum\Exception\InvalidEnumValueException;
19
use Gears\Immutability\ImmutabilityBehaviour;
20
21
/**
22
 * Base immutable enum class.
23
 */
24
abstract class AbstractEnum implements Enum
25
{
26
    use ImmutabilityBehaviour;
27
28
    /**
29
     * Class is final check.
30
     *
31
     * @var bool
32
     */
33
    protected static $finalAlreadyChecked = false;
34
35
    /**
36
     * Enum class constants map.
37
     *
38
     * @var array
39
     */
40
    protected static $enumCacheMap = [];
41
42
    /**
43
     * Enum value.
44
     *
45
     * @var mixed
46
     */
47
    private $value;
48
49
    /**
50
     * AbstractEnum constructor.
51
     *
52
     * @param mixed $value
53
     *
54
     * @throws EnumException
55
     * @throws InvalidEnumValueException
56
     */
57
    final public function __construct($value)
58
    {
59
        $this->assertImmutable();
60
        $this->assertFinal();
61
62
        $this->checkValue($value);
63
64
        $this->value = $value;
65
    }
66
67
    /**
68
     * Assert enum is final.
69
     *
70
     * @throws EnumException
71
     */
72
    private function assertFinal(): void
73
    {
74
        if (!static::$finalAlreadyChecked) {
75
            if (!(new \ReflectionClass(static::class))->isFinal()) {
76
                throw new EnumException(\sprintf('Enum class "%s" should be final', static::class));
77
            }
78
79
            static::$finalAlreadyChecked = true;
80
        }
81
    }
82
83
    /**
84
     * Value based static constructor.
85
     *
86
     * @param string  $name
87
     * @param mixed[] $params
88
     *
89
     * @return self
90
     */
91
    final public static function __callStatic(string $name, array $params)
92
    {
93
        if (\count($params) !== 0) {
94
            throw new EnumException('Enum static constructor must be called with no parameters');
95
        }
96
97
        $enumerator = static::normalizeName($name);
98
        $enumerators = static::getEnumerators();
99
100
        if (!\array_key_exists($enumerator, $enumerators)) {
101
            throw new InvalidEnumNameException(\sprintf(
102
                '"%s" is not a valid enumerator for enum "%s"',
103
                $name,
104
                static::class
105
            ));
106
        }
107
108
        return new static($enumerators[$enumerator]);
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114
    final public function isEqualTo(Enum $enum): bool
115
    {
116
        return \get_class($enum) === static::class && $enum->getValue() === $this->value;
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122
    final public function isAnyOf(array $enums): bool
123
    {
124
        foreach ($enums as $enum) {
125
            if ($this->isEqualTo($enum)) {
126
                return true;
127
            }
128
        }
129
130
        return false;
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    final public function getValue()
137
    {
138
        return $this->value;
139
    }
140
141
    /**
142
     * @return mixed[]
143
     */
144
    final public function __sleep(): array
145
    {
146
        throw new EnumException(\sprintf('Enum "%s" cannot be serialized', static::class));
147
    }
148
149
    final public function __wakeup(): void
150
    {
151
        throw new EnumException(\sprintf('Enum "%s" cannot be unserialized', static::class));
152
    }
153
154
    /**
155
     * @return mixed[]
156
     *
157
     * @SuppressWarnings(PHPMD.CamelCaseMethodName)
158
     */
159
    final public function __serialize(): array
160
    {
161
        throw new EnumException(\sprintf('Enum "%s" cannot be serialized', static::class));
162
    }
163
164
    /**
165
     * @SuppressWarnings(PHPMD.CamelCaseMethodName)
166
     */
167
    final public function __unserialize(): void
168
    {
169
        throw new EnumException(\sprintf('Enum "%s" cannot be unserialized', static::class));
170
    }
171
172
    final public function __clone()
173
    {
174
    }
175
176
    /**
177
     * Check enum value validity.
178
     *
179
     * @param mixed $value
180
     *
181
     * @throws InvalidEnumValueException
182
     */
183
    private function checkValue($value): void
184
    {
185
        if (!\in_array($value, static::getEnumerators(), true)) {
186
            throw new InvalidEnumValueException(\sprintf(
187
                '"%s" is not a valid value for enum "%s"',
188
                $value,
189
                static::class
190
            ));
191
        }
192
    }
193
194
    /**
195
     * Get list of enumerators.
196
     *
197
     * @return array<string, mixed>
198
     */
199
    private static function getEnumerators(): array
200
    {
201
        $class = static::class;
202
203
        if (!isset(static::$enumCacheMap[$class])) {
204
            $constants = [];
205
            foreach ((new \ReflectionClass($class))->getReflectionConstants() as $reflectionConstant) {
206
                if ($reflectionConstant->isPublic()) {
207
                    $constants[static::normalizeName($reflectionConstant->getName())] = $reflectionConstant->getValue();
208
                }
209
            }
210
211
            static::$enumCacheMap[$class] = $constants;
212
        }
213
214
        return static::$enumCacheMap[$class];
215
    }
216
217
    /**
218
     * Normalize name.
219
     *
220
     * @param string $name
221
     *
222
     * @return string
223
     */
224
    private static function normalizeName(string $name): string
225
    {
226
        return \strtoupper($name);
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     *
232
     * @return string[]
233
     */
234
    final protected function getAllowedInterfaces(): array
235
    {
236
        return [Enum::class];
237
    }
238
}
239