1- <?php
2-
3- declare (strict_types=1 );
1+ <?php declare (strict_types=1 );
42
53namespace MabeEnumPHPStan ;
64
75use MabeEnum \Enum ;
86use PhpParser \Node \Expr \MethodCall ;
7+ use PhpParser \Node \Expr \StaticCall ;
98use PHPStan \Analyser \Scope ;
109use PHPStan \Reflection \MethodReflection ;
11- use PHPStan \Reflection \ParametersAcceptorSelector ;
12- use PHPStan \ShouldNotHappenException ;
1310use PHPStan \Type \ArrayType ;
1411use PHPStan \Type \Constant \ConstantArrayType ;
1512use PHPStan \Type \ConstantTypeHelper ;
1613use PHPStan \Type \DynamicMethodReturnTypeExtension ;
14+ use PHPStan \Type \DynamicStaticMethodReturnTypeExtension ;
1715use PHPStan \Type \Type ;
1816use PHPStan \Type \TypeCombinator ;
1917
20- class EnumDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
18+ class EnumDynamicReturnTypeExtension implements DynamicStaticMethodReturnTypeExtension, DynamicMethodReturnTypeExtension
2119{
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+
2234 /**
2335 * Buffer of all types of enumeration values
2436 * @phpstan-var array<class-string<Enum>, Type[]>
@@ -31,19 +43,48 @@ class EnumDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
3143 */
3244 private $ enumOrdinalTypesBuffer = [];
3345
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+
3462 public function getClass (): string
3563 {
3664 return Enum::class;
3765 }
3866
67+ public function isStaticMethodSupported (MethodReflection $ methodReflection ): bool
68+ {
69+ $ methodLower = strtolower ($ methodReflection ->getName ());
70+ return array_key_exists ($ methodLower , $ this ->staticMethods );
71+ }
72+
3973 public function isMethodSupported (MethodReflection $ methodReflection ): bool
4074 {
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 ());
4586
46- return in_array ( strtolower ( $ methodReflection -> getName ()), $ supportedMethods , true );
87+ return $ this -> staticMethods [ $ methodLower ]( $ callClass );
4788 }
4889
4990 public function getTypeFromMethodCall (
@@ -53,24 +94,10 @@ public function getTypeFromMethodCall(
5394 ): Type {
5495 $ callType = $ scope ->getType ($ methodCall ->var );
5596 $ callClasses = $ callType ->getReferencedClasses ();
56- $ methodName = strtolower ($ methodReflection ->getName ());
97+ $ methodLower = strtolower ($ methodReflection ->getName ());
5798 $ returnTypes = [];
5899 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 );
74101 }
75102
76103 return TypeCombinator::union (...$ returnTypes );
@@ -114,7 +141,7 @@ private function enumOrdinalTypes(string $enumeration): array
114141 * Returns return type of Enum::getValue()
115142 * @phpstan-param class-string<Enum> $enumeration
116143 */
117- private function enumGetValueReturnType (string $ enumeration ): Type
144+ private function detectGetValueReturnType (string $ enumeration ): Type
118145 {
119146 return TypeCombinator::union (...$ this ->enumValueTypes ($ enumeration ));
120147 }
@@ -123,7 +150,7 @@ private function enumGetValueReturnType(string $enumeration): Type
123150 * Returns return type of Enum::getValues()
124151 * @phpstan-param class-string<Enum> $enumeration
125152 */
126- private function enumGetValuesReturnType (string $ enumeration ): ArrayType
153+ private function detectGetValuesReturnType (string $ enumeration ): ArrayType
127154 {
128155 $ keyTypes = $ this ->enumOrdinalTypes ($ enumeration );
129156 $ valueTypes = $ this ->enumValueTypes ($ enumeration );
0 commit comments