22
33namespace PHPStan \Type \Php ;
44
5+ use PhpParser \Node \Expr \ClassConstFetch ;
56use PhpParser \Node \Expr \MethodCall ;
7+ use PhpParser \Node \Identifier ;
8+ use PhpParser \Node \Name \FullyQualified ;
69use PHPStan \Analyser \Scope ;
710use PHPStan \DependencyInjection \AutowiredService ;
811use PHPStan \Reflection \ClassReflection ;
1114use PHPStan \Type \Constant \ConstantBooleanType ;
1215use PHPStan \Type \Constant \ConstantStringType ;
1316use PHPStan \Type \DynamicMethodReturnTypeExtension ;
14- use PHPStan \Type \Enum \EnumCaseObjectType ;
1517use PHPStan \Type \ObjectWithoutClassType ;
1618use PHPStan \Type \Type ;
1719use PHPStan \Type \TypeCombinator ;
@@ -56,7 +58,16 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
5658 ? $ scope ->getType ($ methodCall ->getArgs ()[0 ]->value )
5759 : null ;
5860
59- return $ this ->resolveGetConstants ($ classReflections , $ filterType );
61+ return $ this ->resolveGetConstants ($ scope , $ classReflections , $ filterType );
62+ }
63+
64+ /** @param non-empty-string $name */
65+ private function getConstantType (Scope $ scope , ClassReflection $ classReflection , string $ name ): Type
66+ {
67+ return $ scope ->getType (new ClassConstFetch (
68+ new FullyQualified ($ classReflection ->getName ()),
69+ new Identifier ($ name ),
70+ ));
6071 }
6172
6273 /**
@@ -76,10 +87,11 @@ private function resolveGetConstant(MethodCall $methodCall, Scope $scope, array
7687 foreach ($ classReflections as $ classReflection ) {
7788 foreach ($ constantNames as $ constantName ) {
7889 $ name = $ constantName ->getValue ();
79- if ($ classReflection ->isEnum () && $ classReflection ->hasEnumCase ($ name )) {
80- $ types [] = new EnumCaseObjectType ($ classReflection ->getName (), $ name );
81- } elseif ($ classReflection ->hasConstant ($ name )) {
82- $ types [] = $ classReflection ->getConstant ($ name )->getValueType ();
90+ if ($ name === '' ) {
91+ continue ;
92+ }
93+ if ($ classReflection ->hasConstant ($ name )) {
94+ $ types [] = $ this ->getConstantType ($ scope , $ classReflection , $ name );
8395 } else {
8496 $ types [] = new ConstantBooleanType (false );
8597 }
@@ -95,8 +107,8 @@ private function resolveGetConstant(MethodCall $methodCall, Scope $scope, array
95107
96108 $ allConstantTypes = [];
97109 foreach ($ classReflections as $ classReflection ) {
98- foreach ($ this ->getClassConstants ($ classReflection ) as [ $ name, $ valueType ] ) {
99- $ allConstantTypes [] = $ valueType ;
110+ foreach ($ this ->getConstantNames ($ classReflection ) as $ name ) {
111+ $ allConstantTypes [] = $ this -> getConstantType ( $ scope , $ classReflection , $ name ) ;
100112 }
101113 }
102114
@@ -112,61 +124,61 @@ private function resolveGetConstant(MethodCall $methodCall, Scope $scope, array
112124 /**
113125 * @param list<ClassReflection> $classReflections
114126 */
115- private function resolveGetConstants (array $ classReflections , ?Type $ filterType ): ?Type
127+ private function resolveGetConstants (Scope $ scope , array $ classReflections , ?Type $ filterType ): ?Type
116128 {
117- $ filter = null ;
118- $ filterIsUncertain = false ;
119- if ($ filterType !== null ) {
120- $ filterScalars = $ filterType ->getConstantScalarValues ();
121- $ intFilters = [];
122- foreach ($ filterScalars as $ scalar ) {
123- if (!is_int ($ scalar )) {
124- $ intFilters = null ;
125- break ;
126- }
127- $ intFilters [] = $ scalar ;
128- }
129+ if ($ filterType === null ) {
130+ return $ this ->buildConstantsArray ($ scope , $ classReflections , null , false );
131+ }
129132
130- if ( $ intFilters !== null && count ( $ intFilters ) === 1 ) {
131- $ filter = $ intFilters [ 0 ];
132- } elseif ( $ intFilters !== null && count ( $ intFilters ) > 1 ) {
133- return $ this -> resolveGetConstantsForMultipleFilters ( $ classReflections , $ intFilters );
134- } else {
135- $ filterIsUncertain = true ;
133+ $ filterScalars = $ filterType -> getConstantScalarValues ();
134+ $ intFilters = [ ];
135+ foreach ( $ filterScalars as $ scalar ) {
136+ if (! is_int ( $ scalar )) {
137+ $ intFilters = null ;
138+ break ;
136139 }
140+ $ intFilters [] = $ scalar ;
137141 }
138142
139- $ types = [];
140- foreach ($ classReflections as $ classReflection ) {
141- $ builder = ConstantArrayTypeBuilder::createEmpty ();
142- foreach ($ this ->getClassConstants ($ classReflection , $ filter ) as [$ name , $ valueType ]) {
143- $ builder ->setOffsetValueType (new ConstantStringType ($ name ), $ valueType , $ filterIsUncertain );
144- }
145- $ types [] = $ builder ->getArray ();
143+ if ($ intFilters !== null && count ($ intFilters ) === 1 ) {
144+ return $ this ->buildConstantsArray ($ scope , $ classReflections , $ intFilters [0 ], false );
146145 }
147146
148- if (count ($ types ) === 0 ) {
149- return null ;
147+ if ($ intFilters !== null && count ($ intFilters ) > 1 ) {
148+ $ types = [];
149+ foreach ($ intFilters as $ filter ) {
150+ $ result = $ this ->buildConstantsArray ($ scope , $ classReflections , $ filter , false );
151+ if ($ result !== null ) {
152+ $ types [] = $ result ;
153+ }
154+ }
155+
156+ if (count ($ types ) === 0 ) {
157+ return null ;
158+ }
159+
160+ return TypeCombinator::union (...$ types );
150161 }
151162
152- return TypeCombinator:: union (... $ types );
163+ return $ this -> buildConstantsArray ( $ scope , $ classReflections , null , true );
153164 }
154165
155166 /**
156167 * @param list<ClassReflection> $classReflections
157- * @param list<int> $filters
158168 */
159- private function resolveGetConstantsForMultipleFilters ( array $ classReflections , array $ filters ): ?Type
169+ private function buildConstantsArray ( Scope $ scope , array $ classReflections , ? int $ filter , bool $ optional ): ?Type
160170 {
161171 $ types = [];
162- foreach ($ filters as $ filter ) {
163- foreach ($ classReflections as $ classReflection ) {
164- $ builder = ConstantArrayTypeBuilder::createEmpty ();
165- foreach ($ this ->getClassConstants ($ classReflection , $ filter ) as [$ name , $ valueType ]) {
166- $ builder ->setOffsetValueType (new ConstantStringType ($ name ), $ valueType );
167- }
168- $ types [] = $ builder ->getArray ();
172+ foreach ($ classReflections as $ classReflection ) {
173+ $ builder = ConstantArrayTypeBuilder::createEmpty ();
174+ foreach ($ this ->getConstantNames ($ classReflection , $ filter ) as $ name ) {
175+ $ builder ->setOffsetValueType (
176+ new ConstantStringType ($ name ),
177+ $ this ->getConstantType ($ scope , $ classReflection , $ name ),
178+ $ optional ,
179+ );
169180 }
181+ $ types [] = $ builder ->getArray ();
170182 }
171183
172184 if (count ($ types ) === 0 ) {
@@ -177,31 +189,25 @@ private function resolveGetConstantsForMultipleFilters(array $classReflections,
177189 }
178190
179191 /**
180- * @return list<array{ string, Type} >
192+ * @return list<non-empty- string>
181193 */
182- private function getClassConstants (ClassReflection $ classReflection , ?int $ filter = null ): array
194+ private function getConstantNames (ClassReflection $ classReflection , ?int $ filter = null ): array
183195 {
184- $ constants = [];
196+ $ names = [];
185197 foreach ($ classReflection ->getNativeReflection ()->getReflectionConstants () as $ reflectionConstant ) {
186- $ constantName = $ reflectionConstant ->getName ();
187-
188198 if ($ filter !== null && ($ reflectionConstant ->getModifiers () & $ filter ) === 0 ) {
189199 continue ;
190200 }
191201
192- if ($ classReflection ->isEnum () && $ classReflection ->hasEnumCase ($ constantName )) {
193- $ constants [] = [$ constantName , new EnumCaseObjectType ($ classReflection ->getName (), $ constantName )];
194- continue ;
195- }
196-
197- if (!$ classReflection ->hasConstant ($ constantName )) {
202+ $ name = $ reflectionConstant ->getName ();
203+ if ($ name === '' ) {
198204 continue ;
199205 }
200206
201- $ constants [] = [ $ constantName , $ classReflection -> getConstant ( $ constantName )-> getValueType ()] ;
207+ $ names [] = $ name ;
202208 }
203209
204- return $ constants ;
210+ return $ names ;
205211 }
206212
207213}
0 commit comments