@@ -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,108 @@ 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
+ $ keyTypesCount = count ($ this ->keyTypes );
1012
+
1013
+ $ offset = $ offsetType instanceof ConstantIntegerType ? $ offsetType ->getValue () : null ;
1014
+
1015
+ if ($ lengthType instanceof ConstantIntegerType) {
1016
+ $ length = $ lengthType ->getValue ();
1017
+ } elseif ($ lengthType ->isNull ()->yes ()) {
1018
+ $ length = $ keyTypesCount ;
1019
+ } else {
1020
+ $ length = null ;
1021
+ }
1022
+
1023
+ if ($ keyTypesCount + $ offset <= 0 ) {
1024
+ // A negative offset cannot reach left outside the array twice
1025
+ $ offset = 0 ;
1026
+ }
1027
+
1028
+ if ($ keyTypesCount + $ length <= 0 ) {
1029
+ // A negative length cannot reach left outside the array twice
1030
+ $ length = 0 ;
1031
+ }
1032
+
1033
+ if (
1034
+ $ offset === null || $ length === null
1035
+ || count ($ this ->optionalKeys ) > 0 && $ length < 0 // Negative lengths with optional keys are not supported yet
1036
+ ) {
1037
+ return $ this ->degradeToGeneralArray ()
1038
+ ->spliceArray ($ offsetType , $ lengthType , $ replacementType );
1039
+ }
1040
+
1041
+ $ offsetWasNegative = false ;
1042
+ if ($ offset < 0 ) {
1043
+ $ offsetWasNegative = true ;
1044
+ $ offset = $ keyTypesCount + $ offset ;
1045
+ }
1046
+
1047
+ if ($ length < 0 ) {
1048
+ $ length = $ keyTypesCount - $ offset + $ length ;
1049
+ }
1050
+
1051
+ $ removeKeysCount = 0 ;
1052
+ $ optionalKeysIgnored = 0 ;
1053
+ $ optionalKeysBeforeReplacement = 0 ;
1054
+ $ builder = ConstantArrayTypeBuilder::createEmpty ();
1055
+ for ($ i = 0 ;; $ i ++) {
1056
+ $ isOptional = $ this ->isOptionalKey ($ i );
1057
+
1058
+ if (!$ offsetWasNegative && $ i < $ offset && $ isOptional ) {
1059
+ $ optionalKeysIgnored ++;
1060
+ $ optionalKeysBeforeReplacement ++;
1061
+ }
1062
+
1063
+ if ($ i === $ offset + $ optionalKeysBeforeReplacement ) {
1064
+ // When the offset is reached we have to a) put the replacement array in and b) remove $length elements
1065
+ $ removeKeysCount = $ length ;
1066
+ $ optionalKeysIgnored += $ optionalKeysBeforeReplacement ;
1067
+
1068
+ $ replacementArrayType = $ replacementType ->toArray ();
1069
+ $ constantArrays = $ replacementArrayType ->getConstantArrays ();
1070
+ if (count ($ constantArrays ) === 1 ) {
1071
+ $ valuesArray = $ constantArrays [0 ]->getValuesArray ();
1072
+ for ($ j = 0 , $ jMax = count ($ valuesArray ->keyTypes ); $ j < $ jMax ; $ j ++) {
1073
+ $ builder ->setOffsetValueType (null , $ valuesArray ->valueTypes [$ j ], $ valuesArray ->isOptionalKey ($ j ));
1074
+ }
1075
+ } else {
1076
+ $ builder ->degradeToGeneralArray ();
1077
+ $ builder ->setOffsetValueType (null , $ replacementArrayType ->getIterableValueType (), true );
1078
+ }
1079
+ }
1080
+
1081
+ if (!isset ($ this ->keyTypes [$ i ])) {
1082
+ break ;
1083
+ }
1084
+
1085
+ if ($ removeKeysCount > 0 ) {
1086
+ $ removeKeysCount --;
1087
+
1088
+ if ($ optionalKeysIgnored === 0 ) {
1089
+ if ($ isOptional && $ removeKeysCount === 0 ) {
1090
+ $ optionalKeysIgnored ++;
1091
+ }
1092
+ continue ;
1093
+ }
1094
+ }
1095
+
1096
+ if (!$ isOptional && $ optionalKeysIgnored > 0 ) {
1097
+ $ optionalKeysIgnored --;
1098
+ $ isOptional = true ;
1099
+ }
1100
+
1101
+ $ builder ->setOffsetValueType (
1102
+ $ this ->keyTypes [$ i ]->isInteger ()->no () ? $ this ->keyTypes [$ i ] : null ,
1103
+ $ this ->valueTypes [$ i ],
1104
+ $ isOptional ,
1105
+ );
1106
+ }
1107
+
1108
+ return $ builder ->getArray ();
1109
+ }
1110
+
1009
1111
public function isIterableAtLeastOnce (): TrinaryLogic
1010
1112
{
1011
1113
$ keysCount = count ($ this ->keyTypes );
0 commit comments