@@ -1056,13 +1056,19 @@ private function turnListIntoConstantArray(FuncCall $countFuncCall, Type $type,
1056
1056
1057
1057
private function specifyTypesForConstantBinaryExpression (
1058
1058
Expr $ exprNode ,
1059
- ConstantScalarType $ constantType ,
1059
+ Type $ constantType ,
1060
1060
TypeSpecifierContext $ context ,
1061
1061
Scope $ scope ,
1062
1062
?Expr $ rootExpr ,
1063
1063
): ?SpecifiedTypes
1064
1064
{
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 ) {
1066
1072
$ types = $ this ->create ($ exprNode , $ constantType , $ context , false , $ scope , $ rootExpr );
1067
1073
if ($ exprNode instanceof Expr \NullsafeMethodCall || $ exprNode instanceof Expr \NullsafePropertyFetch) {
1068
1074
return $ types ;
@@ -1076,7 +1082,7 @@ private function specifyTypesForConstantBinaryExpression(
1076
1082
));
1077
1083
}
1078
1084
1079
- if (!$ context ->null () && $ constantType -> getValue () === true ) {
1085
+ if (!$ context ->null () && $ constValue === true ) {
1080
1086
$ types = $ this ->create ($ exprNode , $ constantType , $ context , false , $ scope , $ rootExpr );
1081
1087
if ($ exprNode instanceof Expr \NullsafeMethodCall || $ exprNode instanceof Expr \NullsafePropertyFetch) {
1082
1088
return $ types ;
@@ -1090,10 +1096,6 @@ private function specifyTypesForConstantBinaryExpression(
1090
1096
));
1091
1097
}
1092
1098
1093
- if ($ constantType ->getValue () === null ) {
1094
- return $ this ->create ($ exprNode , $ constantType , $ context , false , $ scope , $ rootExpr );
1095
- }
1096
-
1097
1099
if (
1098
1100
!$ context ->null ()
1099
1101
&& $ exprNode instanceof FuncCall
@@ -1102,6 +1104,10 @@ private function specifyTypesForConstantBinaryExpression(
1102
1104
&& in_array (strtolower ((string ) $ exprNode ->name ), ['count ' , 'sizeof ' ], true )
1103
1105
&& $ constantType instanceof ConstantIntegerType
1104
1106
) {
1107
+ if ($ constantType ->getValue () < 0 ) {
1108
+ return $ this ->create ($ exprNode ->getArgs ()[0 ]->value , new NeverType (), $ context , false , $ scope , $ rootExpr );
1109
+ }
1110
+
1105
1111
$ argType = $ scope ->getType ($ exprNode ->getArgs ()[0 ]->value );
1106
1112
1107
1113
if ($ argType instanceof UnionType) {
@@ -1146,6 +1152,10 @@ private function specifyTypesForConstantBinaryExpression(
1146
1152
&& in_array (strtolower ((string ) $ exprNode ->name ), ['strlen ' , 'mb_strlen ' ], true )
1147
1153
&& $ constantType instanceof ConstantIntegerType
1148
1154
) {
1155
+ if ($ constantType ->getValue () < 0 ) {
1156
+ return $ this ->create ($ exprNode ->getArgs ()[0 ]->value , new NeverType (), $ context , false , $ scope , $ rootExpr );
1157
+ }
1158
+
1149
1159
if ($ context ->truthy () || $ constantType ->getValue () === 0 ) {
1150
1160
$ newContext = $ context ;
1151
1161
if ($ constantType ->getValue () === 0 ) {
@@ -1172,12 +1182,18 @@ private function specifyTypesForConstantBinaryExpression(
1172
1182
1173
1183
private function specifyTypesForConstantStringBinaryExpression (
1174
1184
Expr $ exprNode ,
1175
- ConstantStringType $ constantType ,
1185
+ Type $ constantType ,
1176
1186
TypeSpecifierContext $ context ,
1177
1187
Scope $ scope ,
1178
1188
?Expr $ rootExpr ,
1179
1189
): ?SpecifiedTypes
1180
1190
{
1191
+ $ scalarValues = $ constantType ->getConstantScalarValues ();
1192
+ if (count ($ scalarValues ) !== 1 || !is_string ($ scalarValues [0 ])) {
1193
+ return null ;
1194
+ }
1195
+ $ constantStringValue = $ scalarValues [0 ];
1196
+
1181
1197
if (
1182
1198
$ context ->truthy ()
1183
1199
&& $ exprNode instanceof FuncCall
@@ -1188,12 +1204,12 @@ private function specifyTypesForConstantStringBinaryExpression(
1188
1204
'ucwords ' , 'mb_convert_case ' , 'mb_convert_kana ' ,
1189
1205
], true )
1190
1206
&& isset ($ exprNode ->getArgs ()[0 ])
1191
- && $ constantType -> getValue () !== ''
1207
+ && $ constantStringValue !== ''
1192
1208
) {
1193
1209
$ argType = $ scope ->getType ($ exprNode ->getArgs ()[0 ]->value );
1194
1210
1195
1211
if ($ argType ->isString ()->yes ()) {
1196
- if ($ constantType -> getValue () !== '0 ' ) {
1212
+ if ($ constantStringValue !== '0 ' ) {
1197
1213
return $ this ->create (
1198
1214
$ exprNode ->getArgs ()[0 ]->value ,
1199
1215
TypeCombinator::intersect ($ argType , new AccessoryNonFalsyStringType ()),
@@ -1220,28 +1236,28 @@ private function specifyTypesForConstantStringBinaryExpression(
1220
1236
&& isset ($ exprNode ->getArgs ()[0 ])
1221
1237
) {
1222
1238
$ type = null ;
1223
- if ($ constantType -> getValue () === 'string ' ) {
1239
+ if ($ constantStringValue === 'string ' ) {
1224
1240
$ type = new StringType ();
1225
1241
}
1226
- if ($ constantType -> getValue () === 'array ' ) {
1242
+ if ($ constantStringValue === 'array ' ) {
1227
1243
$ type = new ArrayType (new MixedType (), new MixedType ());
1228
1244
}
1229
- if ($ constantType -> getValue () === 'boolean ' ) {
1245
+ if ($ constantStringValue === 'boolean ' ) {
1230
1246
$ type = new BooleanType ();
1231
1247
}
1232
- if (in_array ($ constantType -> getValue () , ['resource ' , 'resource (closed) ' ], true )) {
1248
+ if (in_array ($ constantStringValue , ['resource ' , 'resource (closed) ' ], true )) {
1233
1249
$ type = new ResourceType ();
1234
1250
}
1235
- if ($ constantType -> getValue () === 'integer ' ) {
1251
+ if ($ constantStringValue === 'integer ' ) {
1236
1252
$ type = new IntegerType ();
1237
1253
}
1238
- if ($ constantType -> getValue () === 'double ' ) {
1254
+ if ($ constantStringValue === 'double ' ) {
1239
1255
$ type = new FloatType ();
1240
1256
}
1241
- if ($ constantType -> getValue () === 'NULL ' ) {
1257
+ if ($ constantStringValue === 'NULL ' ) {
1242
1258
$ type = new NullType ();
1243
1259
}
1244
- if ($ constantType -> getValue () === 'object ' ) {
1260
+ if ($ constantStringValue === 'object ' ) {
1245
1261
$ type = new ObjectWithoutClassType ();
1246
1262
}
1247
1263
@@ -1260,7 +1276,7 @@ private function specifyTypesForConstantStringBinaryExpression(
1260
1276
&& isset ($ exprNode ->getArgs ()[0 ])
1261
1277
) {
1262
1278
$ argType = $ scope ->getType ($ exprNode ->getArgs ()[0 ]->value );
1263
- $ objectType = new ObjectType ($ constantType -> getValue () );
1279
+ $ objectType = new ObjectType ($ constantStringValue );
1264
1280
$ classStringType = new GenericClassStringType ($ objectType );
1265
1281
1266
1282
if ($ argType ->isString ()->yes ()) {
@@ -2149,10 +2165,14 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
2149
2165
}
2150
2166
}
2151
2167
2152
- if (count ( $ rightType ->getConstantStrings ()) > 0 ) {
2168
+ if ($ rightType ->isInteger ()-> yes () || $ rightType -> isString ()-> yes () ) {
2153
2169
$ 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
+ }
2156
2176
if ($ specifiedType === null ) {
2157
2177
continue ;
2158
2178
}
0 commit comments