1: | <?php declare(strict_types=1); |
2: | |
3: | namespace Salient\Core; |
4: | |
5: | use Salient\Contract\Core\ConvertibleEnumerationInterface; |
6: | use Salient\Utility\Inflect; |
7: | use Salient\Utility\Str; |
8: | use InvalidArgumentException; |
9: | use LogicException; |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: | |
21: | |
22: | abstract class AbstractReflectiveEnumeration extends AbstractEnumeration implements ConvertibleEnumerationInterface |
23: | { |
24: | |
25: | |
26: | |
27: | |
28: | |
29: | private static $NameMaps = []; |
30: | |
31: | |
32: | |
33: | |
34: | |
35: | |
36: | private static $ValueMaps = []; |
37: | |
38: | |
39: | |
40: | |
41: | final public static function fromName(string $name) |
42: | { |
43: | self::checkMaps(); |
44: | |
45: | $value = self::$ValueMaps[static::class][$name] |
46: | ?? self::$ValueMaps[static::class][Str::upper($name)] |
47: | ?? null; |
48: | |
49: | if ($value === null) { |
50: | throw new InvalidArgumentException( |
51: | sprintf('Invalid name: %s', $name) |
52: | ); |
53: | } |
54: | |
55: | |
56: | return $value; |
57: | } |
58: | |
59: | |
60: | |
61: | |
62: | final public static function fromNames(array $names): array |
63: | { |
64: | self::checkMaps(); |
65: | |
66: | $invalid = []; |
67: | foreach ($names as $name) { |
68: | $value = self::$ValueMaps[static::class][$name] |
69: | ?? self::$ValueMaps[static::class][Str::upper($name)] |
70: | ?? null; |
71: | |
72: | if ($value === null) { |
73: | $invalid[] = $name; |
74: | continue; |
75: | } |
76: | |
77: | $values[] = $value; |
78: | } |
79: | |
80: | if ($invalid) { |
81: | throw new InvalidArgumentException( |
82: | Inflect::format($invalid, 'Invalid {{#:name}}: %s', implode(',', $invalid)) |
83: | ); |
84: | } |
85: | |
86: | |
87: | return $values ?? []; |
88: | } |
89: | |
90: | |
91: | |
92: | |
93: | final public static function toName($value): string |
94: | { |
95: | self::checkMaps(); |
96: | |
97: | $name = self::$NameMaps[static::class][$value] ?? null; |
98: | |
99: | if ($name === null) { |
100: | throw new InvalidArgumentException( |
101: | sprintf('Invalid value: %s', $value) |
102: | ); |
103: | } |
104: | |
105: | return $name; |
106: | } |
107: | |
108: | |
109: | |
110: | |
111: | final public static function toNames(array $values): array |
112: | { |
113: | self::checkMaps(); |
114: | |
115: | $invalid = []; |
116: | foreach ($values as $value) { |
117: | $name = self::$NameMaps[static::class][$value] ?? null; |
118: | |
119: | if ($name === null) { |
120: | $invalid[] = $value; |
121: | continue; |
122: | } |
123: | |
124: | $names[] = $name; |
125: | } |
126: | |
127: | if ($invalid) { |
128: | throw new InvalidArgumentException( |
129: | Inflect::format($invalid, 'Invalid {{#:value}}: %s', implode(',', $invalid)) |
130: | ); |
131: | } |
132: | |
133: | return $names ?? []; |
134: | } |
135: | |
136: | |
137: | |
138: | |
139: | final public static function cases(): array |
140: | { |
141: | self::checkMaps(); |
142: | |
143: | return self::$ValueMaps[static::class]; |
144: | } |
145: | |
146: | |
147: | |
148: | |
149: | final public static function hasValue($value): bool |
150: | { |
151: | self::checkMaps(); |
152: | |
153: | return isset(self::$NameMaps[static::class][$value]); |
154: | } |
155: | |
156: | private static function checkMaps(): void |
157: | { |
158: | if (isset(self::$NameMaps[static::class])) { |
159: | return; |
160: | } |
161: | |
162: | $valueMap = []; |
163: | $nameMap = []; |
164: | |
165: | foreach (self::constants() as $name => $value) { |
166: | if (!is_int($value) && !is_string($value)) { |
167: | throw new LogicException(sprintf( |
168: | 'Public constant is not of type int|string: %s::%s', |
169: | static::class, |
170: | $name, |
171: | )); |
172: | } |
173: | |
174: | $valueMap[$name] = $value; |
175: | $nameMap[$value] = $name; |
176: | } |
177: | |
178: | if (!$valueMap) { |
179: | self::$ValueMaps[static::class] = []; |
180: | self::$NameMaps[static::class] = []; |
181: | return; |
182: | } |
183: | |
184: | if (count($valueMap) !== count($nameMap)) { |
185: | throw new LogicException(sprintf( |
186: | 'Public constants do not have unique values: %s', |
187: | static::class, |
188: | )); |
189: | } |
190: | |
191: | |
192: | $valueMap += array_change_key_case($valueMap, \CASE_UPPER); |
193: | |
194: | self::$ValueMaps[static::class] = $valueMap; |
195: | self::$NameMaps[static::class] = $nameMap; |
196: | } |
197: | } |
198: | |