3535use PHPStan \Analyser \TypeSpecifierAwareExtension ;
3636use PHPStan \Analyser \TypeSpecifierContext ;
3737use PHPStan \Reflection \MethodReflection ;
38+ use PHPStan \Reflection \ReflectionProvider ;
3839use PHPStan \ShouldNotHappenException ;
3940use PHPStan \Type \ArrayType ;
40- use PHPStan \Type \Constant \ConstantArrayType ;
4141use PHPStan \Type \Constant \ConstantArrayTypeBuilder ;
4242use PHPStan \Type \Constant \ConstantBooleanType ;
43- use PHPStan \Type \Constant \ConstantStringType ;
4443use PHPStan \Type \IterableType ;
4544use PHPStan \Type \MixedType ;
4645use PHPStan \Type \NeverType ;
47- use PHPStan \Type \ObjectType ;
4846use PHPStan \Type \StaticMethodTypeSpecifyingExtension ;
4947use PHPStan \Type \StringType ;
5048use PHPStan \Type \Type ;
5149use PHPStan \Type \TypeCombinator ;
52- use PHPStan \Type \TypeWithClassName ;
5350use ReflectionObject ;
5451use Traversable ;
5552use function array_filter ;
@@ -66,11 +63,19 @@ class AssertTypeSpecifyingExtension implements StaticMethodTypeSpecifyingExtensi
6663{
6764
6865 /** @var Closure[] */
69- private static $ resolvers ;
66+ private $ resolvers ;
67+
68+ /** @var ReflectionProvider */
69+ private $ reflectionProvider ;
7070
7171 /** @var TypeSpecifier */
7272 private $ typeSpecifier ;
7373
74+ public function __construct (ReflectionProvider $ reflectionProvider )
75+ {
76+ $ this ->reflectionProvider = $ reflectionProvider ;
77+ }
78+
7479 public function setTypeSpecifier (TypeSpecifier $ typeSpecifier ): void
7580 {
7681 $ this ->typeSpecifier = $ typeSpecifier ;
@@ -98,7 +103,7 @@ public function isStaticMethodSupported(
98103 }
99104
100105 $ trimmedName = self ::trimName ($ staticMethodReflection ->getName ());
101- $ resolvers = self :: getExpressionResolvers ();
106+ $ resolvers = $ this -> getExpressionResolvers ();
102107
103108 if (!array_key_exists ($ trimmedName , $ resolvers )) {
104109 return false ;
@@ -176,14 +181,14 @@ static function (Type $type) {
176181 * @param Arg[] $args
177182 * @return array{?Expr, ?Expr}
178183 */
179- private static function createExpression (
184+ private function createExpression (
180185 Scope $ scope ,
181186 string $ name ,
182187 array $ args
183188 ): array
184189 {
185190 $ trimmedName = self ::trimName ($ name );
186- $ resolvers = self :: getExpressionResolvers ();
191+ $ resolvers = $ this -> getExpressionResolvers ();
187192 $ resolver = $ resolvers [$ trimmedName ];
188193
189194 $ resolverResult = $ resolver ($ scope , ...$ args );
@@ -214,10 +219,10 @@ private static function createExpression(
214219 /**
215220 * @return array<string, callable(Scope, Arg...): (Expr|array{?Expr, ?Expr}|null)>
216221 */
217- private static function getExpressionResolvers (): array
222+ private function getExpressionResolvers (): array
218223 {
219- if (self :: $ resolvers === null ) {
220- self :: $ resolvers = [
224+ if ($ this -> resolvers === null ) {
225+ $ this -> resolvers = [
221226 'integer ' => static function (Scope $ scope , Arg $ value ): Expr {
222227 return new FuncCall (
223228 new Name ('is_int ' ),
@@ -328,8 +333,8 @@ private static function getExpressionResolvers(): array
328333 [$ value ]
329334 );
330335 },
331- 'isTraversable ' => static function (Scope $ scope , Arg $ value ): Expr {
332- return self :: $ resolvers ['isIterable ' ]($ scope , $ value );
336+ 'isTraversable ' => function (Scope $ scope , Arg $ value ): Expr {
337+ return $ this -> resolvers ['isIterable ' ]($ scope , $ value );
333338 },
334339 'isIterable ' => static function (Scope $ scope , Arg $ expr ): Expr {
335340 return new BooleanOr (
@@ -358,9 +363,9 @@ private static function getExpressionResolvers(): array
358363 )
359364 );
360365 },
361- 'isNonEmptyList ' => static function (Scope $ scope , Arg $ expr ): Expr {
366+ 'isNonEmptyList ' => function (Scope $ scope , Arg $ expr ): Expr {
362367 return new BooleanAnd (
363- self :: $ resolvers ['isList ' ]($ scope , $ expr ),
368+ $ this -> resolvers ['isList ' ]($ scope , $ expr ),
364369 new NotIdentical (
365370 $ expr ->value ,
366371 new Array_ ()
@@ -382,9 +387,9 @@ private static function getExpressionResolvers(): array
382387 )
383388 );
384389 },
385- 'isNonEmptyMap ' => static function (Scope $ scope , Arg $ expr ): Expr {
390+ 'isNonEmptyMap ' => function (Scope $ scope , Arg $ expr ): Expr {
386391 return new BooleanAnd (
387- self :: $ resolvers ['isMap ' ]($ scope , $ expr ),
392+ $ this -> resolvers ['isMap ' ]($ scope , $ expr ),
388393 new NotIdentical (
389394 $ expr ->value ,
390395 new Array_ ()
@@ -405,24 +410,22 @@ private static function getExpressionResolvers(): array
405410 },
406411 'isInstanceOf ' => static function (Scope $ scope , Arg $ expr , Arg $ class ): ?Expr {
407412 $ classType = $ scope ->getType ($ class ->value );
408- if ($ classType instanceof ConstantStringType) {
409- $ className = new Name ($ classType ->getValue ());
410- } elseif ($ classType instanceof TypeWithClassName) {
411- $ className = new Name ($ classType ->getClassName ());
412- } else {
413+ $ classNameType = $ classType ->getObjectTypeOrClassStringObjectType ();
414+ $ classNames = $ classNameType ->getObjectClassNames ();
415+ if (count ($ classNames ) !== 1 ) {
413416 return null ;
414417 }
415418
416419 return new Instanceof_ (
417420 $ expr ->value ,
418- $ className
421+ new Name ( $ classNames [ 0 ])
419422 );
420423 },
421- 'isInstanceOfAny ' => static function (Scope $ scope , Arg $ expr , Arg $ classes ): ?Expr {
422- return self ::buildAnyOfExpr ($ scope , $ expr , $ classes , self :: $ resolvers ['isInstanceOf ' ]);
424+ 'isInstanceOfAny ' => function (Scope $ scope , Arg $ expr , Arg $ classes ): ?Expr {
425+ return self ::buildAnyOfExpr ($ scope , $ expr , $ classes , $ this -> resolvers ['isInstanceOf ' ]);
423426 },
424- 'notInstanceOf ' => static function (Scope $ scope , Arg $ expr , Arg $ class ): ?Expr {
425- $ expr = self :: $ resolvers ['isInstanceOf ' ]($ scope , $ expr , $ class );
427+ 'notInstanceOf ' => function (Scope $ scope , Arg $ expr , Arg $ class ): ?Expr {
428+ $ expr = $ this -> resolvers ['isInstanceOf ' ]($ scope , $ expr , $ class );
426429 if ($ expr === null ) {
427430 return null ;
428431 }
@@ -438,37 +441,39 @@ private static function getExpressionResolvers(): array
438441 [$ expr , $ class , new Arg (new ConstFetch (new Name ($ allowString ? 'true ' : 'false ' )))]
439442 );
440443 },
441- 'isAnyOf ' => static function (Scope $ scope , Arg $ value , Arg $ classes ): ?Expr {
442- return self ::buildAnyOfExpr ($ scope , $ value , $ classes , self :: $ resolvers ['isAOf ' ]);
444+ 'isAnyOf ' => function (Scope $ scope , Arg $ value , Arg $ classes ): ?Expr {
445+ return self ::buildAnyOfExpr ($ scope , $ value , $ classes , $ this -> resolvers ['isAOf ' ]);
443446 },
444- 'isNotA ' => static function (Scope $ scope , Arg $ value , Arg $ class ): Expr {
445- return new BooleanNot (self :: $ resolvers ['isAOf ' ]($ scope , $ value , $ class ));
447+ 'isNotA ' => function (Scope $ scope , Arg $ value , Arg $ class ): Expr {
448+ return new BooleanNot ($ this -> resolvers ['isAOf ' ]($ scope , $ value , $ class ));
446449 },
447- 'implementsInterface ' => static function (Scope $ scope , Arg $ expr , Arg $ class ): ?Expr {
448- $ classType = $ scope ->getType ($ class ->value );
449- if (!$ classType instanceof ConstantStringType) {
450+ 'implementsInterface ' => function (Scope $ scope , Arg $ expr , Arg $ class ): ?Expr {
451+ $ classType = $ scope ->getType ($ class ->value )->getClassStringObjectType ();
452+ $ classNames = $ classType ->getObjectClassNames ();
453+
454+ if (count ($ classNames ) !== 1 ) {
450455 return null ;
451456 }
452457
453- $ classReflection = (new ObjectType ($ classType ->getValue ()))->getClassReflection ();
454- if ($ classReflection === null ) {
458+ if (!$ this ->reflectionProvider ->hasClass ($ classNames [0 ])) {
455459 return null ;
456460 }
457461
462+ $ classReflection = $ this ->reflectionProvider ->getClass ($ classNames [0 ]);
458463 if (!$ classReflection ->isInterface ()) {
459464 return new ConstFetch (new Name ('false ' ));
460465 }
461466
462- return self :: $ resolvers ['subclassOf ' ]($ scope , $ expr , $ class );
467+ return $ this -> resolvers ['subclassOf ' ]($ scope , $ expr , $ class );
463468 },
464469 'keyExists ' => static function (Scope $ scope , Arg $ array , Arg $ key ): Expr {
465470 return new FuncCall (
466471 new Name ('array_key_exists ' ),
467472 [$ key , $ array ]
468473 );
469474 },
470- 'keyNotExists ' => static function (Scope $ scope , Arg $ array , Arg $ key ): Expr {
471- return new BooleanNot (self :: $ resolvers ['keyExists ' ]($ scope , $ array , $ key ));
475+ 'keyNotExists ' => function (Scope $ scope , Arg $ array , Arg $ key ): Expr {
476+ return new BooleanNot ($ this -> resolvers ['keyExists ' ]($ scope , $ array , $ key ));
472477 },
473478 'validArrayKey ' => static function (Scope $ scope , Arg $ value ): Expr {
474479 return new BooleanOr (
@@ -518,8 +523,8 @@ private static function getExpressionResolvers(): array
518523 $ value2 ->value
519524 );
520525 },
521- 'notEq ' => static function (Scope $ scope , Arg $ value , Arg $ value2 ): Expr {
522- return new BooleanNot (self :: $ resolvers ['eq ' ]($ scope , $ value , $ value2 ));
526+ 'notEq ' => function (Scope $ scope , Arg $ value , Arg $ value2 ): Expr {
527+ return new BooleanNot ($ this -> resolvers ['eq ' ]($ scope , $ value , $ value2 ));
523528 },
524529 'same ' => static function (Scope $ scope , Arg $ value1 , Arg $ value2 ): Expr {
525530 return new Identical (
@@ -714,8 +719,8 @@ private static function getExpressionResolvers(): array
714719 ]
715720 );
716721 },
717- 'oneOf ' => static function (Scope $ scope , Arg $ needle , Arg $ array ): Expr {
718- return self :: $ resolvers ['inArray ' ]($ scope , $ needle , $ array );
722+ 'oneOf ' => function (Scope $ scope , Arg $ needle , Arg $ array ): Expr {
723+ return $ this -> resolvers ['inArray ' ]($ scope , $ needle , $ array );
719724 },
720725 'methodExists ' => static function (Scope $ scope , Arg $ object , Arg $ method ): Expr {
721726 return new FuncCall (
@@ -744,12 +749,12 @@ private static function getExpressionResolvers(): array
744749 ];
745750
746751 foreach (['contains ' , 'startsWith ' , 'endsWith ' ] as $ name ) {
747- self :: $ resolvers [$ name ] = static function (Scope $ scope , Arg $ value , Arg $ subString ) use ($ name ): array {
752+ $ this -> resolvers [$ name ] = function (Scope $ scope , Arg $ value , Arg $ subString ) use ($ name ): array {
748753 if ($ scope ->getType ($ subString ->value )->isNonEmptyString ()->yes ()) {
749754 return self ::createIsNonEmptyStringAndSomethingExprPair ($ name , [$ value , $ subString ]);
750755 }
751756
752- return [self :: $ resolvers ['string ' ]($ scope , $ value ), null ];
757+ return [$ this -> resolvers ['string ' ]($ scope , $ value ), null ];
753758 };
754759 }
755760
@@ -769,14 +774,14 @@ private static function getExpressionResolvers(): array
769774 'notWhitespaceOnly ' ,
770775 ];
771776 foreach ($ assertionsResultingAtLeastInNonEmptyString as $ name ) {
772- self :: $ resolvers [$ name ] = static function (Scope $ scope , Arg $ value ) use ($ name ): array {
777+ $ this -> resolvers [$ name ] = static function (Scope $ scope , Arg $ value ) use ($ name ): array {
773778 return self ::createIsNonEmptyStringAndSomethingExprPair ($ name , [$ value ]);
774779 };
775780 }
776781
777782 }
778783
779- return self :: $ resolvers ;
784+ return $ this -> resolvers ;
780785 }
781786
782787 private function handleAllNot (
@@ -797,20 +802,17 @@ static function (Type $type): Type {
797802
798803 if ($ methodName === 'allNotInstanceOf ' ) {
799804 $ classType = $ scope ->getType ($ node ->getArgs ()[1 ]->value );
800-
801- if ($ classType instanceof ConstantStringType) {
802- $ objectType = new ObjectType ($ classType ->getValue ());
803- } elseif ($ classType instanceof TypeWithClassName) {
804- $ objectType = new ObjectType ($ classType ->getClassName ());
805- } else {
805+ $ classNameType = $ classType ->getObjectTypeOrClassStringObjectType ();
806+ $ classNames = $ classNameType ->getObjectClassNames ();
807+ if (count ($ classNames ) !== 1 ) {
806808 return new SpecifiedTypes ([], []);
807809 }
808810
809811 return $ this ->allArrayOrIterable (
810812 $ scope ,
811813 $ node ->getArgs ()[0 ]->value ,
812- static function (Type $ type ) use ($ objectType ): Type {
813- return TypeCombinator::remove ($ type , $ objectType );
814+ static function (Type $ type ) use ($ classNameType ): Type {
815+ return TypeCombinator::remove ($ type , $ classNameType );
814816 }
815817 );
816818 }
@@ -889,14 +891,15 @@ private function allArrayOrIterable(
889891 if (count ($ arrayTypes ) > 0 ) {
890892 $ newArrayTypes = [];
891893 foreach ($ arrayTypes as $ arrayType ) {
892- if ($ arrayType instanceof ConstantArrayType) {
894+ $ constantArrays = $ arrayType ->getConstantArrays ();
895+ if (count ($ constantArrays ) === 1 ) {
893896 $ builder = ConstantArrayTypeBuilder::createEmpty ();
894- foreach ($ arrayType ->getKeyTypes () as $ i => $ keyType ) {
895- $ valueType = $ typeCallback ($ arrayType ->getValueTypes ()[$ i ]);
897+ foreach ($ constantArrays [ 0 ] ->getKeyTypes () as $ i => $ keyType ) {
898+ $ valueType = $ typeCallback ($ constantArrays [ 0 ] ->getValueTypes ()[$ i ]);
896899 if ($ valueType instanceof NeverType) {
897900 continue 2 ;
898901 }
899- $ builder ->setOffsetValueType ($ keyType , $ valueType , $ arrayType ->isOptionalKey ($ i ));
902+ $ builder ->setOffsetValueType ($ keyType , $ valueType , $ constantArrays [ 0 ] ->isOptionalKey ($ i ));
900903 }
901904 $ newArrayTypes [] = $ builder ->getArray ();
902905 } else {
0 commit comments