@@ -1056,13 +1056,19 @@ private function turnListIntoConstantArray(FuncCall $countFuncCall, Type $type,
10561056
10571057 private function specifyTypesForConstantBinaryExpression (
10581058 Expr $ exprNode ,
1059- ConstantScalarType $ constantType ,
1059+ Type $ constantType ,
10601060 TypeSpecifierContext $ context ,
10611061 Scope $ scope ,
10621062 ?Expr $ rootExpr ,
10631063 ): ?SpecifiedTypes
10641064 {
1065- if (!$ context ->null () && $ constantType ->getValue () === false ) {
1065+ $ scalarValues = $ constantType ->getConstantScalarValues ();
1066+ if (count ($ scalarValues ) !== 1 ) {
1067+ return null ;
1068+ }
1069+ $ constValue = $ scalarValues [0 ];
1070+
1071+ if (!$ context ->null () && $ constValue === false ) {
10661072 $ types = $ this ->create ($ exprNode , $ constantType , $ context , false , $ scope , $ rootExpr );
10671073 if ($ exprNode instanceof Expr \NullsafeMethodCall || $ exprNode instanceof Expr \NullsafePropertyFetch) {
10681074 return $ types ;
@@ -1076,7 +1082,7 @@ private function specifyTypesForConstantBinaryExpression(
10761082 ));
10771083 }
10781084
1079- if (!$ context ->null () && $ constantType -> getValue () === true ) {
1085+ if (!$ context ->null () && $ constValue === true ) {
10801086 $ types = $ this ->create ($ exprNode , $ constantType , $ context , false , $ scope , $ rootExpr );
10811087 if ($ exprNode instanceof Expr \NullsafeMethodCall || $ exprNode instanceof Expr \NullsafePropertyFetch) {
10821088 return $ types ;
@@ -1090,10 +1096,6 @@ private function specifyTypesForConstantBinaryExpression(
10901096 ));
10911097 }
10921098
1093- if ($ constantType ->getValue () === null ) {
1094- return $ this ->create ($ exprNode , $ constantType , $ context , false , $ scope , $ rootExpr );
1095- }
1096-
10971099 if (
10981100 !$ context ->null ()
10991101 && $ exprNode instanceof FuncCall
@@ -1102,6 +1104,10 @@ private function specifyTypesForConstantBinaryExpression(
11021104 && in_array (strtolower ((string ) $ exprNode ->name ), ['count ' , 'sizeof ' ], true )
11031105 && $ constantType instanceof ConstantIntegerType
11041106 ) {
1107+ if ($ constantType ->getValue () < 0 ) {
1108+ return $ this ->create ($ exprNode ->getArgs ()[0 ]->value , new NeverType (), $ context , false , $ scope , $ rootExpr );
1109+ }
1110+
11051111 $ argType = $ scope ->getType ($ exprNode ->getArgs ()[0 ]->value );
11061112
11071113 if ($ argType instanceof UnionType) {
@@ -1146,6 +1152,10 @@ private function specifyTypesForConstantBinaryExpression(
11461152 && in_array (strtolower ((string ) $ exprNode ->name ), ['strlen ' , 'mb_strlen ' ], true )
11471153 && $ constantType instanceof ConstantIntegerType
11481154 ) {
1155+ if ($ constantType ->getValue () < 0 ) {
1156+ return $ this ->create ($ exprNode ->getArgs ()[0 ]->value , new NeverType (), $ context , false , $ scope , $ rootExpr );
1157+ }
1158+
11491159 if ($ context ->truthy () || $ constantType ->getValue () === 0 ) {
11501160 $ newContext = $ context ;
11511161 if ($ constantType ->getValue () === 0 ) {
@@ -1172,12 +1182,18 @@ private function specifyTypesForConstantBinaryExpression(
11721182
11731183 private function specifyTypesForConstantStringBinaryExpression (
11741184 Expr $ exprNode ,
1175- ConstantStringType $ constantType ,
1185+ Type $ constantType ,
11761186 TypeSpecifierContext $ context ,
11771187 Scope $ scope ,
11781188 ?Expr $ rootExpr ,
11791189 ): ?SpecifiedTypes
11801190 {
1191+ $ scalarValues = $ constantType ->getConstantScalarValues ();
1192+ if (count ($ scalarValues ) !== 1 || !is_string ($ scalarValues [0 ])) {
1193+ return null ;
1194+ }
1195+ $ constantStringValue = $ scalarValues [0 ];
1196+
11811197 if (
11821198 $ context ->truthy ()
11831199 && $ exprNode instanceof FuncCall
@@ -1188,12 +1204,12 @@ private function specifyTypesForConstantStringBinaryExpression(
11881204 'ucwords ' , 'mb_convert_case ' , 'mb_convert_kana ' ,
11891205 ], true )
11901206 && isset ($ exprNode ->getArgs ()[0 ])
1191- && $ constantType -> getValue () !== ''
1207+ && $ constantStringValue !== ''
11921208 ) {
11931209 $ argType = $ scope ->getType ($ exprNode ->getArgs ()[0 ]->value );
11941210
11951211 if ($ argType ->isString ()->yes ()) {
1196- if ($ constantType -> getValue () !== '0 ' ) {
1212+ if ($ constantStringValue !== '0 ' ) {
11971213 return $ this ->create (
11981214 $ exprNode ->getArgs ()[0 ]->value ,
11991215 TypeCombinator::intersect ($ argType , new AccessoryNonFalsyStringType ()),
@@ -1220,28 +1236,28 @@ private function specifyTypesForConstantStringBinaryExpression(
12201236 && isset ($ exprNode ->getArgs ()[0 ])
12211237 ) {
12221238 $ type = null ;
1223- if ($ constantType -> getValue () === 'string ' ) {
1239+ if ($ constantStringValue === 'string ' ) {
12241240 $ type = new StringType ();
12251241 }
1226- if ($ constantType -> getValue () === 'array ' ) {
1242+ if ($ constantStringValue === 'array ' ) {
12271243 $ type = new ArrayType (new MixedType (), new MixedType ());
12281244 }
1229- if ($ constantType -> getValue () === 'boolean ' ) {
1245+ if ($ constantStringValue === 'boolean ' ) {
12301246 $ type = new BooleanType ();
12311247 }
1232- if (in_array ($ constantType -> getValue () , ['resource ' , 'resource (closed) ' ], true )) {
1248+ if (in_array ($ constantStringValue , ['resource ' , 'resource (closed) ' ], true )) {
12331249 $ type = new ResourceType ();
12341250 }
1235- if ($ constantType -> getValue () === 'integer ' ) {
1251+ if ($ constantStringValue === 'integer ' ) {
12361252 $ type = new IntegerType ();
12371253 }
1238- if ($ constantType -> getValue () === 'double ' ) {
1254+ if ($ constantStringValue === 'double ' ) {
12391255 $ type = new FloatType ();
12401256 }
1241- if ($ constantType -> getValue () === 'NULL ' ) {
1257+ if ($ constantStringValue === 'NULL ' ) {
12421258 $ type = new NullType ();
12431259 }
1244- if ($ constantType -> getValue () === 'object ' ) {
1260+ if ($ constantStringValue === 'object ' ) {
12451261 $ type = new ObjectWithoutClassType ();
12461262 }
12471263
@@ -1260,7 +1276,7 @@ private function specifyTypesForConstantStringBinaryExpression(
12601276 && isset ($ exprNode ->getArgs ()[0 ])
12611277 ) {
12621278 $ argType = $ scope ->getType ($ exprNode ->getArgs ()[0 ]->value );
1263- $ objectType = new ObjectType ($ constantType -> getValue () );
1279+ $ objectType = new ObjectType ($ constantStringValue );
12641280 $ classStringType = new GenericClassStringType ($ objectType );
12651281
12661282 if ($ argType ->isString ()->yes ()) {
@@ -2149,10 +2165,14 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
21492165 }
21502166 }
21512167
2152- if (count ( $ rightType ->getConstantStrings ()) > 0 ) {
2168+ if ($ rightType ->isInteger ()-> yes () || $ rightType -> isString ()-> yes () ) {
21532169 $ types = null ;
2154- foreach ($ rightType ->getConstantStrings () as $ constantString ) {
2155- $ specifiedType = $ this ->specifyTypesForConstantStringBinaryExpression ($ unwrappedLeftExpr , $ constantString , $ context , $ scope , $ rootExpr );
2170+ foreach ($ rightType ->getFiniteTypes () as $ finiteType ) {
2171+ if ($ finiteType ->isString ()->yes ()) {
2172+ $ specifiedType = $ this ->specifyTypesForConstantStringBinaryExpression ($ unwrappedLeftExpr , $ finiteType , $ context , $ scope , $ rootExpr );
2173+ } else {
2174+ $ specifiedType = $ this ->specifyTypesForConstantBinaryExpression ($ unwrappedLeftExpr , $ finiteType , $ context , $ scope , $ rootExpr );
2175+ }
21562176 if ($ specifiedType === null ) {
21572177 continue ;
21582178 }
0 commit comments