17
17
*
18
18
* @psalm-template T
19
19
* @psalm-immutable
20
+ * @psalm-consistent-constructor
20
21
*/
21
- abstract class Enum implements \JsonSerializable
22
+ abstract class Enum implements \JsonSerializable, \Stringable
22
23
{
23
24
/**
24
25
* Enum value
@@ -28,6 +29,13 @@ abstract class Enum implements \JsonSerializable
28
29
*/
29
30
protected $ value ;
30
31
32
+ /**
33
+ * Enum key, the constant name
34
+ *
35
+ * @var string
36
+ */
37
+ private $ key ;
38
+
31
39
/**
32
40
* Store existing constants in a static cache per object.
33
41
*
@@ -51,7 +59,7 @@ abstract class Enum implements \JsonSerializable
51
59
* @psalm-pure
52
60
* @param mixed $value
53
61
*
54
- * @psalm-param static<T>| T $value
62
+ * @psalm-param T $value
55
63
* @throws \UnexpectedValueException if incompatible type is given.
56
64
*/
57
65
public function __construct ($ value )
@@ -61,15 +69,40 @@ public function __construct($value)
61
69
$ value = $ value ->getValue ();
62
70
}
63
71
64
- if (!$ this ->isValid ($ value )) {
65
- /** @psalm-suppress InvalidCast */
66
- throw new \UnexpectedValueException ("Value ' $ value' is not part of the enum " . static ::class);
67
- }
72
+ /** @psalm-suppress ImplicitToStringCast assertValidValueReturningKey returns always a string but psalm has currently an issue here */
73
+ $ this ->key = static ::assertValidValueReturningKey ($ value );
68
74
69
75
/** @psalm-var T */
70
76
$ this ->value = $ value ;
71
77
}
72
78
79
+ /**
80
+ * This method exists only for the compatibility reason when deserializing a previously serialized version
81
+ * that didn't had the key property
82
+ */
83
+ public function __wakeup ()
84
+ {
85
+ /** @psalm-suppress DocblockTypeContradiction key can be null when deserializing an enum without the key */
86
+ if ($ this ->key === null ) {
87
+ /**
88
+ * @psalm-suppress InaccessibleProperty key is not readonly as marked by psalm
89
+ * @psalm-suppress PossiblyFalsePropertyAssignmentValue deserializing a case that was removed
90
+ */
91
+ $ this ->key = static ::search ($ this ->value );
92
+ }
93
+ }
94
+
95
+ /**
96
+ * @param mixed $value
97
+ * @return static
98
+ */
99
+ public static function from ($ value ): self
100
+ {
101
+ $ key = static ::assertValidValueReturningKey ($ value );
102
+
103
+ return self ::__callStatic ($ key , []);
104
+ }
105
+
73
106
/**
74
107
* @psalm-pure
75
108
* @return mixed
@@ -84,11 +117,11 @@ public function getValue()
84
117
* Returns the enum key (i.e. the constant name).
85
118
*
86
119
* @psalm-pure
87
- * @return mixed
120
+ * @return string
88
121
*/
89
122
public function getKey ()
90
123
{
91
- return static :: search ( $ this ->value ) ;
124
+ return $ this ->key ;
92
125
}
93
126
94
127
/**
@@ -163,7 +196,9 @@ public static function toArray()
163
196
$ class = static ::class;
164
197
165
198
if (!isset (static ::$ cache [$ class ])) {
199
+ /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
166
200
$ reflection = new \ReflectionClass ($ class );
201
+ /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
167
202
static ::$ cache [$ class ] = $ reflection ->getConstants ();
168
203
}
169
204
@@ -176,13 +211,43 @@ public static function toArray()
176
211
* @param $value
177
212
* @psalm-param mixed $value
178
213
* @psalm-pure
214
+ * @psalm-assert-if-true T $value
179
215
* @return bool
180
216
*/
181
217
public static function isValid ($ value )
182
218
{
183
219
return \in_array ($ value , static ::toArray (), true );
184
220
}
185
221
222
+ /**
223
+ * Asserts valid enum value
224
+ *
225
+ * @psalm-pure
226
+ * @psalm-assert T $value
227
+ * @param mixed $value
228
+ */
229
+ public static function assertValidValue ($ value ): void
230
+ {
231
+ self ::assertValidValueReturningKey ($ value );
232
+ }
233
+
234
+ /**
235
+ * Asserts valid enum value
236
+ *
237
+ * @psalm-pure
238
+ * @psalm-assert T $value
239
+ * @param mixed $value
240
+ * @return string
241
+ */
242
+ private static function assertValidValueReturningKey ($ value ): string
243
+ {
244
+ if (false === ($ key = static ::search ($ value ))) {
245
+ throw new \UnexpectedValueException ("Value ' $ value' is not part of the enum " . static ::class);
246
+ }
247
+
248
+ return $ key ;
249
+ }
250
+
186
251
/**
187
252
* Check if is valid enum key
188
253
*
@@ -201,11 +266,11 @@ public static function isValidKey($key)
201
266
/**
202
267
* Return key for value
203
268
*
204
- * @param $value
269
+ * @param mixed $value
205
270
*
206
271
* @psalm-param mixed $value
207
272
* @psalm-pure
208
- * @return mixed
273
+ * @return string|false
209
274
*/
210
275
public static function search ($ value )
211
276
{
@@ -220,6 +285,8 @@ public static function search($value)
220
285
*
221
286
* @return static
222
287
* @throws \BadMethodCallException
288
+ *
289
+ * @psalm-pure
223
290
*/
224
291
public static function __callStatic ($ name , $ arguments )
225
292
{
@@ -243,6 +310,7 @@ public static function __callStatic($name, $arguments)
243
310
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
244
311
* @psalm-pure
245
312
*/
313
+ #[\ReturnTypeWillChange]
246
314
public function jsonSerialize ()
247
315
{
248
316
return $ this ->getValue ();
0 commit comments