1
- <?php
2
-
3
- declare (strict_types=1 );
1
+ <?php declare (strict_types=1 );
4
2
5
3
namespace MabeEnumPHPStan ;
6
4
7
5
use MabeEnum \Enum ;
8
6
use PhpParser \Node \Expr \MethodCall ;
7
+ use PhpParser \Node \Expr \StaticCall ;
9
8
use PHPStan \Analyser \Scope ;
10
9
use PHPStan \Reflection \MethodReflection ;
11
- use PHPStan \Reflection \ParametersAcceptorSelector ;
12
- use PHPStan \ShouldNotHappenException ;
13
10
use PHPStan \Type \ArrayType ;
14
11
use PHPStan \Type \Constant \ConstantArrayType ;
15
12
use PHPStan \Type \ConstantTypeHelper ;
16
13
use PHPStan \Type \DynamicMethodReturnTypeExtension ;
14
+ use PHPStan \Type \DynamicStaticMethodReturnTypeExtension ;
17
15
use PHPStan \Type \Type ;
18
16
use PHPStan \Type \TypeCombinator ;
19
17
20
- class EnumDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
18
+ class EnumDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension, DynamicMethodReturnTypeExtension
21
19
{
20
+ /**
21
+ * Map supported object method to a callable function detecting return type
22
+ *
23
+ * @var array<string, callable>
24
+ */
25
+ private $ objectMethods = [];
26
+
27
+ /**
28
+ * Map supported static method to a callable function detecting return type
29
+ *
30
+ * @var array<string, callable>
31
+ */
32
+ private $ staticMethods = [];
33
+
22
34
/**
23
35
* Buffer of all types of enumeration values
24
36
* @phpstan-var array<class-string<Enum>, Type[]>
@@ -31,19 +43,48 @@ class EnumDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
31
43
*/
32
44
private $ enumOrdinalTypesBuffer = [];
33
45
46
+ public function __construct ()
47
+ {
48
+ $ this ->objectMethods ['getvalue ' ] = function (string $ class ) {
49
+ return $ this ->detectGetValueReturnType ($ class );
50
+ };
51
+
52
+ if (method_exists (Enum::class, 'getvalues ' )) {
53
+ $ this ->staticMethods ['getvalues ' ] = function (string $ class ) {
54
+ return $ this ->detectGetValuesReturnType ($ class );
55
+ };
56
+ }
57
+
58
+ // static methods cann be called like object methods
59
+ $ this ->objectMethods = array_merge ($ this ->objectMethods , $ this ->staticMethods );
60
+ }
61
+
34
62
public function getClass (): string
35
63
{
36
64
return Enum::class;
37
65
}
38
66
67
+ public function isStaticMethodSupported (MethodReflection $ methodReflection ): bool
68
+ {
69
+ $ methodLower = strtolower ($ methodReflection ->getName ());
70
+ return array_key_exists ($ methodLower , $ this ->staticMethods );
71
+ }
72
+
39
73
public function isMethodSupported (MethodReflection $ methodReflection ): bool
40
74
{
41
- $ supportedMethods = ['getvalue ' ];
42
- if (method_exists (Enum::class, 'getValues ' )) {
43
- array_push ($ supportedMethods , 'getvalues ' );
44
- }
75
+ $ methodLower = strtolower ($ methodReflection ->getName ());
76
+ return array_key_exists ($ methodLower , $ this ->objectMethods );
77
+ }
78
+
79
+ public function getTypeFromStaticMethodCall (
80
+ MethodReflection $ methodReflection ,
81
+ StaticCall $ staticCall ,
82
+ Scope $ scope
83
+ ): Type {
84
+ $ callClass = $ staticCall ->class ->toString ();
85
+ $ methodLower = strtolower ($ methodReflection ->getName ());
45
86
46
- return in_array ( strtolower ( $ methodReflection -> getName ()), $ supportedMethods , true );
87
+ return $ this -> staticMethods [ $ methodLower ]( $ callClass );
47
88
}
48
89
49
90
public function getTypeFromMethodCall (
@@ -53,24 +94,10 @@ public function getTypeFromMethodCall(
53
94
): Type {
54
95
$ callType = $ scope ->getType ($ methodCall ->var );
55
96
$ callClasses = $ callType ->getReferencedClasses ();
56
- $ methodName = strtolower ($ methodReflection ->getName ());
97
+ $ methodLower = strtolower ($ methodReflection ->getName ());
57
98
$ returnTypes = [];
58
99
foreach ($ callClasses as $ callClass ) {
59
- if (!is_subclass_of ($ callClass , Enum::class, true )) {
60
- $ returnTypes [] = ParametersAcceptorSelector::selectSingle ($ methodReflection ->getVariants ())
61
- ->getReturnType ();
62
- } else {
63
- switch ($ methodName ) {
64
- case 'getvalue ' :
65
- $ returnTypes [] = $ this ->enumGetValueReturnType ($ callClass );
66
- break ;
67
- case 'getvalues ' :
68
- $ returnTypes [] = $ this ->enumGetValuesReturnType ($ callClass );
69
- break ;
70
- default :
71
- throw new ShouldNotHappenException ("Method {$ methodName } is not supported " );
72
- }
73
- }
100
+ $ returnTypes [] = $ this ->objectMethods [$ methodLower ]($ callClass );
74
101
}
75
102
76
103
return TypeCombinator::union (...$ returnTypes );
@@ -114,7 +141,7 @@ private function enumOrdinalTypes(string $enumeration): array
114
141
* Returns return type of Enum::getValue()
115
142
* @phpstan-param class-string<Enum> $enumeration
116
143
*/
117
- private function enumGetValueReturnType (string $ enumeration ): Type
144
+ private function detectGetValueReturnType (string $ enumeration ): Type
118
145
{
119
146
return TypeCombinator::union (...$ this ->enumValueTypes ($ enumeration ));
120
147
}
@@ -123,7 +150,7 @@ private function enumGetValueReturnType(string $enumeration): Type
123
150
* Returns return type of Enum::getValues()
124
151
* @phpstan-param class-string<Enum> $enumeration
125
152
*/
126
- private function enumGetValuesReturnType (string $ enumeration ): ArrayType
153
+ private function detectGetValuesReturnType (string $ enumeration ): ArrayType
127
154
{
128
155
$ keyTypes = $ this ->enumOrdinalTypes ($ enumeration );
129
156
$ valueTypes = $ this ->enumValueTypes ($ enumeration );
0 commit comments