@@ -1016,6 +1016,108 @@ public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $pre
1016
1016
return $ builder ->getArray ();
1017
1017
}
1018
1018
1019
+ public function spliceArray (Type $ offsetType , Type $ lengthType , Type $ replacementType ): Type
1020
+ {
1021
+ $ keyTypesCount = count ($ this ->keyTypes );
1022
+
1023
+ $ offset = $ offsetType instanceof ConstantIntegerType ? $ offsetType ->getValue () : null ;
1024
+
1025
+ if ($ lengthType instanceof ConstantIntegerType) {
1026
+ $ length = $ lengthType ->getValue ();
1027
+ } elseif ($ lengthType ->isNull ()->yes ()) {
1028
+ $ length = $ keyTypesCount ;
1029
+ } else {
1030
+ $ length = null ;
1031
+ }
1032
+
1033
+ if ($ offset === null || $ length === null ) {
1034
+ return $ this ->degradeToGeneralArray ()
1035
+ ->spliceArray ($ offsetType , $ lengthType , $ replacementType );
1036
+ }
1037
+
1038
+ if ($ keyTypesCount + $ offset <= 0 ) {
1039
+ // A negative offset cannot reach left outside the array twice
1040
+ $ offset = 0 ;
1041
+ }
1042
+
1043
+ if ($ keyTypesCount + $ length <= 0 ) {
1044
+ // A negative length cannot reach left outside the array twice
1045
+ $ length = 0 ;
1046
+ }
1047
+
1048
+ $ offsetWasNegative = false ;
1049
+ if ($ offset < 0 ) {
1050
+ $ offsetWasNegative = true ;
1051
+ $ offset = $ keyTypesCount + $ offset ;
1052
+ }
1053
+
1054
+ if ($ length < 0 ) {
1055
+ $ length = $ keyTypesCount - $ offset + $ length ;
1056
+ }
1057
+
1058
+ $ extractType = $ this ->sliceArray ($ offsetType , $ lengthType , TrinaryLogic::createYes ());
1059
+
1060
+ $ types = [];
1061
+ foreach ($ replacementType ->toArray ()->getArrays () as $ replacementArrayType ) {
1062
+ $ removeKeysCount = 0 ;
1063
+ $ optionalKeysBeforeReplacement = 0 ;
1064
+
1065
+ $ builder = ConstantArrayTypeBuilder::createEmpty ();
1066
+ for ($ i = 0 ;; $ i ++) {
1067
+ $ isOptional = $ this ->isOptionalKey ($ i );
1068
+
1069
+ if (!$ offsetWasNegative && $ i < $ offset && $ isOptional ) {
1070
+ $ optionalKeysBeforeReplacement ++;
1071
+ }
1072
+
1073
+ if ($ i === $ offset + $ optionalKeysBeforeReplacement ) {
1074
+ // When the offset is reached we have to a) put the replacement array in and b) remove $length elements
1075
+ $ removeKeysCount = $ length ;
1076
+
1077
+ if ($ replacementArrayType instanceof self) {
1078
+ $ valuesArray = $ replacementArrayType ->getValuesArray ();
1079
+ for ($ j = 0 , $ jMax = count ($ valuesArray ->keyTypes ); $ j < $ jMax ; $ j ++) {
1080
+ $ builder ->setOffsetValueType (null , $ valuesArray ->valueTypes [$ j ], $ valuesArray ->isOptionalKey ($ j ));
1081
+ }
1082
+ } else {
1083
+ $ builder ->degradeToGeneralArray ();
1084
+ $ builder ->setOffsetValueType ($ replacementArrayType ->getValuesArray ()->getIterableKeyType (), $ replacementArrayType ->getIterableValueType (), true );
1085
+ }
1086
+ }
1087
+
1088
+ if (!isset ($ this ->keyTypes [$ i ])) {
1089
+ break ;
1090
+ }
1091
+
1092
+ if ($ removeKeysCount > 0 ) {
1093
+ $ extractTypeHasOffsetValueType = $ extractType ->hasOffsetValueType ($ this ->keyTypes [$ i ]);
1094
+
1095
+ if (
1096
+ (!$ isOptional && $ extractTypeHasOffsetValueType ->yes ())
1097
+ || ($ isOptional && $ extractTypeHasOffsetValueType ->maybe ())
1098
+ ) {
1099
+ $ removeKeysCount --;
1100
+ continue ;
1101
+ }
1102
+ }
1103
+
1104
+ if (!$ isOptional && $ extractType ->hasOffsetValueType ($ this ->keyTypes [$ i ])->maybe ()) {
1105
+ $ isOptional = true ;
1106
+ }
1107
+
1108
+ $ builder ->setOffsetValueType (
1109
+ $ this ->keyTypes [$ i ]->isInteger ()->no () ? $ this ->keyTypes [$ i ] : null ,
1110
+ $ this ->valueTypes [$ i ],
1111
+ $ isOptional ,
1112
+ );
1113
+ }
1114
+
1115
+ $ types [] = $ builder ->getArray ();
1116
+ }
1117
+
1118
+ return TypeCombinator::union (...$ types );
1119
+ }
1120
+
1019
1121
public function isIterableAtLeastOnce (): TrinaryLogic
1020
1122
{
1021
1123
$ keysCount = count ($ this ->keyTypes );
0 commit comments