@@ -945,7 +945,7 @@ public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $pre
945
945
}
946
946
947
947
if ($ keyTypesCount + $ offset <= 0 ) {
948
- // A negative offset cannot reach left outside the array
948
+ // A negative offset cannot reach left outside the array twice
949
949
$ offset = 0 ;
950
950
}
951
951
@@ -1006,6 +1006,97 @@ public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $pre
1006
1006
return $ builder ->getArray ();
1007
1007
}
1008
1008
1009
+ public function spliceArray (Type $ offsetType , Type $ lengthType , Type $ replacementType ): Type
1010
+ {
1011
+ $ allArrays = $ this ->getAllArrays ();
1012
+ if (count ($ allArrays ) > InitializerExprTypeResolver::CALCULATE_SCALARS_LIMIT ) {
1013
+ return $ this ->degradeToGeneralArray ()
1014
+ ->spliceArray ($ offsetType , $ lengthType , $ replacementType );
1015
+ }
1016
+
1017
+ $ types = [];
1018
+ foreach ($ this ->getAllArrays () as $ array ) {
1019
+ $ types [] = $ array ->spliceArrayWithoutOptionalKeys ($ offsetType , $ lengthType , $ replacementType );
1020
+ }
1021
+
1022
+ return TypeCombinator::union (...$ types );
1023
+ }
1024
+
1025
+ private function spliceArrayWithoutOptionalKeys (Type $ offsetType , Type $ lengthType , Type $ replacementType ): Type
1026
+ {
1027
+ $ keyTypesCount = count ($ this ->keyTypes );
1028
+
1029
+ $ offset = $ offsetType instanceof ConstantIntegerType ? $ offsetType ->getValue () : null ;
1030
+
1031
+ if ($ lengthType instanceof ConstantIntegerType) {
1032
+ $ length = $ lengthType ->getValue ();
1033
+ } elseif ($ lengthType ->isNull ()->yes ()) {
1034
+ $ length = $ keyTypesCount ;
1035
+ } else {
1036
+ $ length = null ;
1037
+ }
1038
+
1039
+ if ($ offset === null || $ length === null ) {
1040
+ return $ this ->degradeToGeneralArray ()
1041
+ ->spliceArray ($ offsetType , $ lengthType , $ replacementType );
1042
+ }
1043
+
1044
+ if ($ keyTypesCount + $ offset <= 0 ) {
1045
+ // A negative offset cannot reach left outside the array twice
1046
+ $ offset = 0 ;
1047
+ }
1048
+
1049
+ if ($ keyTypesCount + $ length <= 0 ) {
1050
+ // A negative length cannot reach left outside the array twice
1051
+ $ length = 0 ;
1052
+ }
1053
+
1054
+ if ($ offset < 0 ) {
1055
+ $ offset = $ keyTypesCount + $ offset ;
1056
+ }
1057
+
1058
+ if ($ length < 0 ) {
1059
+ $ length = $ keyTypesCount - $ offset + $ length ;
1060
+ }
1061
+
1062
+ $ removeKeysCount = 0 ;
1063
+ $ builder = ConstantArrayTypeBuilder::createEmpty ();
1064
+ for ($ i = 0 ;; $ i ++) {
1065
+ if ($ i === $ offset ) {
1066
+ // When the offset is reached we have to a) put the replacement array in and b) remove $length elements
1067
+ $ removeKeysCount = $ length ;
1068
+
1069
+ $ replacementArrayType = $ replacementType ->toArray ();
1070
+ $ constantArrays = $ replacementArrayType ->getConstantArrays ();
1071
+ if (count ($ constantArrays ) === 1 ) {
1072
+ $ valuesArray = $ constantArrays [0 ]->getValuesArray ();
1073
+ for ($ j = 0 , $ jMax = count ($ valuesArray ->keyTypes ); $ j < $ jMax ; $ j ++) {
1074
+ $ builder ->setOffsetValueType (null , $ valuesArray ->valueTypes [$ j ], $ valuesArray ->isOptionalKey ($ j ));
1075
+ }
1076
+ } else {
1077
+ $ builder ->degradeToGeneralArray ();
1078
+ $ builder ->setOffsetValueType ($ replacementArrayType ->getValuesArray ()->getIterableKeyType (), $ replacementArrayType ->getIterableValueType (), true );
1079
+ }
1080
+ }
1081
+
1082
+ if (!isset ($ this ->keyTypes [$ i ])) {
1083
+ break ;
1084
+ }
1085
+
1086
+ if ($ removeKeysCount > 0 ) {
1087
+ $ removeKeysCount --;
1088
+ continue ;
1089
+ }
1090
+
1091
+ $ builder ->setOffsetValueType (
1092
+ $ this ->keyTypes [$ i ]->isInteger ()->no () ? $ this ->keyTypes [$ i ] : null ,
1093
+ $ this ->valueTypes [$ i ],
1094
+ );
1095
+ }
1096
+
1097
+ return $ builder ->getArray ();
1098
+ }
1099
+
1009
1100
public function isIterableAtLeastOnce (): TrinaryLogic
1010
1101
{
1011
1102
$ keysCount = count ($ this ->keyTypes );
0 commit comments