1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Paillechat\Enum; |
4
|
|
|
|
5
|
|
|
use Paillechat\Enum\Exception\EnumException; |
6
|
|
|
|
7
|
|
|
abstract class Enum |
8
|
|
|
{ |
9
|
|
|
/** @var Enum[] */ |
10
|
|
|
private static $instances = []; |
11
|
|
|
/** @var \ReflectionClassConstant[] */ |
12
|
|
|
private static $constReflections = []; |
13
|
|
|
/** @var \ReflectionClass[] */ |
14
|
|
|
private static $reflections = []; |
15
|
|
|
/** @var string */ |
16
|
|
|
private $name; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @param string $name |
20
|
|
|
*/ |
21
|
3 |
|
final private function __construct(string $name) |
22
|
|
|
{ |
23
|
3 |
|
$this->name = $name; |
24
|
3 |
|
} |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Creates enum instance by name |
28
|
|
|
* |
29
|
|
|
* @param string $name |
30
|
|
|
* |
31
|
|
|
* @return static |
32
|
|
|
* |
33
|
|
|
* @throws EnumException |
34
|
|
|
*/ |
35
|
8 |
|
final public static function createByName(string $name) |
36
|
|
|
{ |
37
|
8 |
|
$canonicalName = strtoupper($name); |
38
|
8 |
|
if ($canonicalName !== $name) { |
39
|
1 |
|
$name = $canonicalName; |
40
|
1 |
|
trigger_error('PSR-1 requires constant to be declared in upper case.', E_USER_NOTICE); |
41
|
|
|
} |
42
|
|
|
|
43
|
7 |
|
$const = static::getConstList(); |
44
|
|
|
|
45
|
7 |
|
if (!\in_array($name, $const, true)) { |
46
|
1 |
|
throw EnumException::becauseUnknownMember(static::class, $name); |
47
|
|
|
} |
48
|
|
|
|
49
|
6 |
|
return static::createNamedInstance($name); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Creates enum instance with short static constructor |
54
|
|
|
* |
55
|
|
|
* @param string $name |
56
|
|
|
* @param array $arguments |
57
|
|
|
* |
58
|
|
|
* @return static |
59
|
|
|
* |
60
|
|
|
* @throws EnumException |
61
|
|
|
*/ |
62
|
7 |
|
final public static function __callStatic(string $name, array $arguments) |
63
|
|
|
{ |
64
|
7 |
|
return static::createByName($name); |
65
|
|
|
} |
66
|
|
|
|
67
|
8 |
|
public static function getConstList(): array |
68
|
|
|
{ |
69
|
8 |
|
return array_keys(self::getEnumReflection(static::class)->getConstants()); |
70
|
|
|
} |
71
|
|
|
|
72
|
6 |
View Code Duplication |
private static function getConstantReflection(string $class, string $name): \ReflectionClassConstant |
|
|
|
|
73
|
|
|
{ |
74
|
6 |
|
$key = self::getConstKey($class, $name); |
75
|
6 |
|
if (!array_key_exists($key, self::$constReflections)) { |
76
|
4 |
|
$refl = self::getEnumReflection(static::class); |
77
|
|
|
|
78
|
4 |
|
self::$constReflections[$key] = $refl->getReflectionConstant($name); |
79
|
|
|
} |
80
|
|
|
|
81
|
6 |
|
return self::$constReflections[$key]; |
82
|
|
|
} |
83
|
|
|
|
84
|
6 |
|
private static function getConstKey(string $class, string $name): string |
85
|
|
|
{ |
86
|
6 |
|
return $class . '::' . $name; |
87
|
|
|
} |
88
|
|
|
|
89
|
6 |
|
private static function findParentClassForConst(string $name): string |
90
|
|
|
{ |
91
|
6 |
|
return self::getConstantReflection(static::class, $name)->getDeclaringClass()->getName(); |
92
|
|
|
} |
93
|
|
|
|
94
|
8 |
|
private static function getEnumReflection(string $class): \ReflectionClass |
95
|
|
|
{ |
96
|
8 |
|
if (!array_key_exists($class, self::$reflections)) { |
97
|
|
|
try { |
98
|
3 |
|
self::$reflections[$class] = new \ReflectionClass($class); |
99
|
|
|
// @codeCoverageIgnoreStart |
100
|
|
|
} catch (\ReflectionException $e) { |
101
|
|
|
throw new \LogicException('Class should be valid FQCN. Fix internal calls.'); |
102
|
|
|
// @codeCoverageIgnoreEnd |
103
|
|
|
} |
104
|
|
|
} |
105
|
|
|
|
106
|
8 |
|
return self::$reflections[$class]; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Create named enum instance |
111
|
|
|
* |
112
|
|
|
* @param string $name |
113
|
|
|
* |
114
|
|
|
* @return static |
115
|
|
|
*/ |
116
|
6 |
View Code Duplication |
private static function createNamedInstance(string $name) |
|
|
|
|
117
|
|
|
{ |
118
|
6 |
|
$class = self::findParentClassForConst($name); |
119
|
|
|
|
120
|
6 |
|
$key = self::getConstKey($class, $name); |
121
|
|
|
|
122
|
6 |
|
if (!array_key_exists($key, self::$instances)) { |
123
|
3 |
|
self::$instances[$key] = new static($name); |
124
|
|
|
} |
125
|
|
|
|
126
|
6 |
|
return self::$instances[$key]; |
127
|
|
|
} |
128
|
|
|
|
129
|
1 |
|
final public function getName(): string |
130
|
|
|
{ |
131
|
1 |
|
return $this->name; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* {@inheritdoc} |
136
|
|
|
*/ |
137
|
1 |
|
final public function __toString(): string |
138
|
|
|
{ |
139
|
1 |
|
return $this->getName(); |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.