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