14
14
namespace Ducks \Component \SplTypes \Reflection ;
15
15
16
16
use Ducks \Component \SplTypes \SplBackedEnum ;
17
+ use Ducks \Component \SplTypes \SplEnumerable ;
17
18
use Ducks \Component \SplTypes \SplUnitEnum ;
18
19
19
20
final class SplReflectionEnumProxy
@@ -44,17 +45,41 @@ final class SplReflectionEnumProxy
44
45
/**
45
46
* Array of constants class, indexed by name, as enum cases.
46
47
*
47
- * @var array<string,\ReflectionClassConstant>
48
+ * @var \ReflectionClassConstant[]
49
+ *
50
+ * @phpstan-var array<string,\ReflectionClassConstant>
48
51
*/
49
52
private array $ constantCases = [];
50
53
51
54
/**
52
55
* Array of Reflection enum cases, indexed by name.
53
56
*
54
- * @var array<string, SplReflectionEnumUnitCase|SplReflectionEnumBackedCase>
57
+ * @var (SplReflectionEnumUnitCase|SplReflectionEnumBackedCase)[]
58
+ *
59
+ * @phpstan-var array<string, SplReflectionEnumUnitCase|SplReflectionEnumBackedCase>
55
60
*/
56
61
private array $ cases = [];
57
62
63
+ /**
64
+ * The nameof the case beeing instanciate
65
+ *
66
+ * @var string|null
67
+ */
68
+ private ?string $ running = null ;
69
+
70
+ /**
71
+ * Name of the class constant.
72
+ *
73
+ * @var string
74
+ *
75
+ * @readonly
76
+ *
77
+ * @phpstan-var class-string
78
+ *
79
+ * @psalm-readonly
80
+ *
81
+ * @phan-read-only
82
+ */
58
83
public string $ name ;
59
84
60
85
/**
@@ -65,14 +90,16 @@ final class SplReflectionEnumProxy
65
90
public function __construct (\ReflectionClass $ class )
66
91
{
67
92
$ this ->class = $ class ;
68
- $ this ->name = $ class ->name ;
93
+ $ this ->name = $ class ->getName () ;
69
94
}
70
95
71
96
/**
72
97
* Gets constants.
73
98
*
74
- * @return array<string, mixed> An array of constants,
99
+ * @return mixed[] An array of constants,
75
100
* where the keys hold the name and the values the value of the constants.
101
+ *
102
+ * @phpstan-return array<string, mixed>
76
103
*/
77
104
public function getConstants (): array
78
105
{
@@ -82,7 +109,7 @@ public function getConstants(): array
82
109
/**
83
110
* Gets a ReflectionClassConstant for a class's property
84
111
*
85
- * @param string $name ? The class constant name.
112
+ * @param string $name The class constant name.
86
113
*
87
114
* @return \ReflectionClassConstant|null
88
115
*/
@@ -115,6 +142,8 @@ private function initConstantCases(): void
115
142
* @param \ReflectionClassConstant ...$constants
116
143
*
117
144
* @return void
145
+ *
146
+ * @no-named-arguments
118
147
*/
119
148
public function addConstantCase (\ReflectionClassConstant ...$ constants ): void
120
149
{
@@ -124,7 +153,7 @@ public function addConstantCase(\ReflectionClassConstant ...$constants): void
124
153
!isset ($ this ->constantCases [$ name ])
125
154
&& $ constant ->isPublic ()
126
155
// Check consistency because of polyfilling or other bad overrides
127
- && $ constant ->getDeclaringClass ()->name === $ this ->name
156
+ && $ constant ->getDeclaringClass ()->getName () === $ this ->name
128
157
// Do not use isBacked method because of infinite loop possibility
129
158
// Add if not BackedEnum or Backed but valid type
130
159
&& (
@@ -143,7 +172,9 @@ public function addConstantCase(\ReflectionClassConstant ...$constants): void
143
172
/**
144
173
* Return an array of class constants, indexed by name, that could be use as an enum case.
145
174
*
146
- * @return array<string,\ReflectionClassConstant>
175
+ * @return \ReflectionClassConstant[]
176
+ *
177
+ * @phpstan-return array<string,\ReflectionClassConstant>
147
178
*/
148
179
public function getConstantCases (): array
149
180
{
@@ -232,32 +263,30 @@ public function addCase(\ReflectionClassConstant ...$constants): void
232
263
$ name = $ constant ->getName ();
233
264
if (
234
265
!isset ($ this ->cases [$ name ])
266
+ && \is_a ($ this ->name , SplEnumerable::class, true )
235
267
) {
236
268
// Check type
237
269
$ value = $ constant ->getValue ();
238
270
271
+ // Mandatory in order to prevent infinite loop
272
+ $ this ->running = $ name ;
273
+
274
+ if (!$ this ->isBacked () && null === $ value ) {
275
+ $ this ->cases [$ name ] = new SplReflectionEnumUnitCase ($ this ->name , $ name );
276
+ unset($ this ->running );
277
+ continue ;
278
+ }
279
+
280
+ $ backingType = $ this ->getBackingType ();
281
+
239
282
if (
240
- (
241
- // Accept nullable value for UnitEnum
242
- null === $ value && !($ this ->getBackingType () instanceof \ReflectionNamedType)
243
- ) || (
244
- // Filter acceptable value for BackedEnum
245
- $ this ->getBackingType () instanceof \ReflectionNamedType
246
- && isset ($ value )
247
- && (
248
- \is_scalar ($ value ) && \call_user_func ('is_ ' . $ this ->getBackingType (), $ value )
249
- || \is_a ($ value , (string ) $ this ->getBackingType ())
250
- )
251
- )
283
+ $ this ->isBacked ()
284
+ && $ backingType instanceof \ReflectionNamedType
285
+ && $ backingType ->getName () === \gettype ($ value )
252
286
) {
253
- // Mandatory in order to prevent infinite loop
254
- $ this ->cases [$ name ] = true ;
255
- $ case = $ this ->isBacked ()
256
- ? new SplReflectionEnumBackedCase ($ this ->name , $ name )
257
- : new SplReflectionEnumUnitCase ($ this ->name , $ name );
258
-
259
- // Now link correct class on pointer
260
- $ this ->cases [$ name ] = $ case ;
287
+ $ this ->cases [$ name ] = new SplReflectionEnumBackedCase ($ this ->name , $ name );
288
+ unset($ this ->running );
289
+ continue ;
261
290
}
262
291
}
263
292
}
@@ -266,7 +295,9 @@ public function addCase(\ReflectionClassConstant ...$constants): void
266
295
/**
267
296
* Returns a list of all cases on an Enum
268
297
*
269
- * @return array<string,SplReflectionEnumUnitCase|SplReflectionEnumBackedCase>
298
+ * @return (SplReflectionEnumUnitCase|SplReflectionEnumBackedCase)[]
299
+ *
300
+ * @phpstan-return array<string,SplReflectionEnumUnitCase|SplReflectionEnumBackedCase>
270
301
*
271
302
* @link https://www.php.net/manual/en/reflectionenum.getcases.php
272
303
*/
@@ -313,7 +344,8 @@ public function getCase(string $name): SplReflectionEnumUnitCase
313
344
*/
314
345
public function hasCase (string $ name ): bool
315
346
{
316
- if (isset ($ this ->cases [$ name ])) {
347
+ // $this->cases could be empty
348
+ if (isset ($ this ->cases [$ name ]) || $ this ->running === $ name ) {
317
349
return true ;
318
350
}
319
351
@@ -339,7 +371,7 @@ public function isBacked(): bool
339
371
$ this ->backed = false ;
340
372
} else {
341
373
$ constant = $ this ->getFirstCaseConstant ();
342
- $ this ->backed = null !== $ constant ->getValue ();
374
+ $ this ->backed = $ constant instanceof \ReflectionClassConstant && null !== $ constant ->getValue ();
343
375
}
344
376
}
345
377
@@ -364,19 +396,9 @@ public function getBackingType(): ?\ReflectionNamedType
364
396
} else {
365
397
$ constant = $ this ->getFirstCaseConstant ();
366
398
if ($ constant instanceof \ReflectionClassConstant) {
367
- switch (\gettype ($ constant ->getValue ())) {
368
- case 'string ' :
369
- $ this ->backingType = SplReflectionEnumHelper::getStringReflectionNamedType ();
370
- break ;
371
-
372
- case 'integer ' :
373
- $ this ->backingType = SplReflectionEnumHelper::getIntReflectionNamedType ();
374
- break ;
375
-
376
- default :
377
- $ this ->backingType = false ;
378
- break ;
379
- }
399
+ $ this ->backingType = SplReflectionEnumHelper::getReflectionNamedTypeFromType (
400
+ \gettype ($ constant ->getValue ())
401
+ );
380
402
}
381
403
}
382
404
} else {
0 commit comments