@@ -30,16 +30,14 @@ public function resolve(): Type
3030 return new UnknownType ;
3131 }
3232
33- if ($ this ->type instanceof ArrayType) {
34- return $ this ->type ; // ??
35- }
36-
3733 $ path = $ this ->normalizePath ($ this ->offset );
3834 if (! $ path ) {
3935 return new UnknownType ;
4036 }
4137
42- return $ this ->applyPath ($ this ->type ->clone (), $ path , $ this ->value );
38+ $ result = $ this ->type ->clone ();
39+
40+ return $ this ->applyPath ($ result , $ path , $ this ->value );
4341 }
4442
4543 public function acceptedBy (Type $ otherType ): bool
@@ -68,78 +66,74 @@ public function toString(): string
6866 }
6967
7068 /**
71- * @param array<int, int|string|null> $path
69+ * @param list< int|string|null> $path
7270 */
73- private function applyPath (KeyedArrayType $ target , array $ path , Type $ value ): KeyedArrayType
71+ private function applyPath (ArrayType | KeyedArrayType & $ target , array $ path , Type $ value ): ArrayType | KeyedArrayType
7472 {
75- $ modifyingType = $ target ;
76-
77- foreach ($ path as $ i => $ pathItem ) {
78- $ isLast = $ i === array_key_last ($ path );
79-
80- $ modifyingType = $ isLast
81- ? $ this ->applyLeafAssignment ($ modifyingType , $ pathItem , $ value )
82- : $ this ->applyIntermediateStep ($ modifyingType , $ pathItem );
83-
84- if ($ modifyingType === null ) {
85- return $ target ;
86- }
73+ if (count ($ path ) === 0 ) {
74+ return $ target ;
8775 }
8876
89- return $ target ;
90- }
77+ $ pathItem = array_shift ($ path );
9178
92- private function applyIntermediateStep (KeyedArrayType $ modifyingType , string |int |null $ pathItem ): ?KeyedArrayType
93- {
94- $ targetItems = $ modifyingType ->items ;
79+ if ($ pathItem === null && $ target instanceof KeyedArrayType && count ($ target ->items ) === 0 ) {
80+ $ target = (new ArrayType )->mergeAttributes ($ target ->attributes ());
9581
96- $ targetItem = Arr::first (
97- $ targetItems ,
98- fn (ArrayItemType_ $ t ) => $ t ->key === $ pathItem ,
99- );
82+ if ($ path ) {
83+ $ target ->value = new KeyedArrayType ;
10084
101- if ( $ targetItem ) {
102- if (! $ targetItem -> value instanceof KeyedArrayType) {
103- return null ;
85+ $ this -> applyPath ( $ target -> value , $ path , $ value );
86+ } else {
87+ $ target -> value = $ value ;
10488 }
105- $ newModifyingType = $ targetItem ->value ;
106- } else {
107- $ targetItem = new ArrayItemType_ (
108- key: $ pathItem ,
109- value: $ newModifyingType = new KeyedArrayType ,
110- );
111- $ targetItems [] = $ targetItem ;
89+
90+ return $ target ;
11291 }
11392
114- $ modifyingType -> items = $ targetItems ;
115- $ modifyingType -> isList = KeyedArrayType:: checkIsList ( $ targetItems );
93+ if ( $ target instanceof ArrayType) {
94+ $ target -> value = Union:: wrap ([ $ target -> value , $ value ] );
11695
117- return $ newModifyingType ;
118- }
96+ $ this ->applyPath ($ target , $ path , $ value );
11997
120- private function applyLeafAssignment (KeyedArrayType $ modifyingType , string |int |null $ pathItem , Type $ value ): KeyedArrayType
121- {
122- $ targetItems = $ modifyingType ->items ;
98+ return $ target ;
99+ }
123100
124- $ targetItem = $ pathItem !== null ? Arr::first (
125- $ targetItems ,
101+ $ targetItem = Arr::first (
102+ $ target -> items ,
126103 fn (ArrayItemType_ $ t ) => $ t ->key === $ pathItem ,
127- ) : null ;
104+ );
128105
129106 if ($ targetItem ) {
130- $ targetItem ->value = $ value ;
107+ if ($ path ) {
108+ if (! $ targetItem ->value instanceof KeyedArrayType && ! $ targetItem ->value instanceof ArrayType) {
109+ return $ target ;
110+ }
111+
112+ $ this ->applyPath ($ targetItem ->value , $ path , $ value );
113+ } else {
114+ $ targetItem ->value = $ value ;
115+ }
131116 } else {
132- $ targetItems [] = $ targetItem = new ArrayItemType_ (key: $ pathItem , value: $ value );
117+ if ($ path ) {
118+ $ targetItem = new ArrayItemType_ (
119+ key: $ pathItem ,
120+ value: new KeyedArrayType ,
121+ );
122+ $ target ->items [] = $ targetItem ;
123+
124+ $ this ->applyPath ($ targetItem ->value , $ path , $ value ); // @phpstan-ignore argument.type
125+ } else {
126+ $ target ->items [] = new ArrayItemType_ (key: $ pathItem , value: $ value );
127+ }
133128 }
134129
135- $ modifyingType ->items = $ targetItems ;
136- $ modifyingType ->isList = KeyedArrayType::checkIsList ($ targetItems );
130+ $ target ->isList = KeyedArrayType::checkIsList ($ target ->items );
137131
138- return $ modifyingType ;
132+ return $ target ;
139133 }
140134
141135 /**
142- * @return null|list<string|int|null>
136+ * @return null|non-empty- list<string|int|null>
143137 */
144138 private function normalizePath (KeyedArrayType $ path ): ?array
145139 {
@@ -152,6 +146,7 @@ private function normalizePath(KeyedArrayType $path): ?array
152146
153147 continue ;
154148 }
149+
155150 if ($ pathItemType instanceof LiteralString || $ pathItemType instanceof LiteralIntegerType) {
156151 $ normalizedPath [] = $ pathItemType ->getValue ();
157152
@@ -161,6 +156,10 @@ private function normalizePath(KeyedArrayType $path): ?array
161156 return null ;
162157 }
163158
159+ if (! count ($ normalizedPath )) {
160+ return null ;
161+ }
162+
164163 return $ normalizedPath ;
165164 }
166165}
0 commit comments