@@ -1016,6 +1016,108 @@ public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $pre
10161016 return $ builder ->getArray ();
10171017 }
10181018
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+
10191121 public function isIterableAtLeastOnce (): TrinaryLogic
10201122 {
10211123 $ keysCount = count ($ this ->keyTypes );
0 commit comments