@@ -165,9 +165,21 @@ public function getType(Type $inputType, ?Type $filterType, ?Type $flagsType): T
165165 return $ mixedType ;
166166 }
167167
168+ $ inputValueType = null ;
168169 if ($ inputIsArray ->yes () && ($ hasRequireArrayFlag ->yes () || $ hasForceArrayFlag ->yes ())) {
169170 $ inputArrayKeyType = $ inputType ->getIterableKeyType ();
170171 $ inputType = $ inputType ->getIterableValueType ();
172+ $ inputValueType = $ inputType ;
173+
174+ // When the value type is a union of scalar and array types (e.g. int|array<int>),
175+ // only use the scalar part for scalar filtering - array parts are handled separately
176+ // via recursive filtering in addNestedArrayType
177+ if ($ inputType ->isArray ()->maybe ()) {
178+ $ scalarPart = TypeCombinator::remove ($ inputType , new ArrayType ($ mixedType , $ mixedType ));
179+ if (!$ scalarPart instanceof NeverType) {
180+ $ inputType = $ scalarPart ;
181+ }
182+ }
171183 }
172184
173185 if ($ inputType ->isScalar ()->no () && $ inputType ->isNull ()->no ()) {
@@ -198,19 +210,15 @@ public function getType(Type $inputType, ?Type $filterType, ?Type $flagsType): T
198210 }
199211
200212 if ($ hasRequireArrayFlag ->yes ()) {
201- if (!$ inputIsArray ->no ()) {
202- $ type = TypeCombinator::union ($ type , new ArrayType ($ mixedType , $ mixedType ));
203- }
213+ $ type = $ this ->addNestedArrayType ($ type , $ inputValueType , $ inputIsArray , $ filterType , $ flagsType , $ mixedType );
204214 $ type = new ArrayType ($ inputArrayKeyType ?? $ mixedType , $ type );
205215 if (!$ inputIsArray ->yes ()) {
206216 $ type = TypeCombinator::union ($ type , $ defaultType );
207217 }
208218 }
209219
210220 if ($ hasRequireArrayFlag ->no () && $ hasForceArrayFlag ->yes ()) {
211- if (!$ inputIsArray ->no ()) {
212- $ type = TypeCombinator::union ($ type , new ArrayType ($ mixedType , $ mixedType ));
213- }
221+ $ type = $ this ->addNestedArrayType ($ type , $ inputValueType , $ inputIsArray , $ filterType , $ flagsType , $ mixedType );
214222 return new ArrayType ($ inputArrayKeyType ?? $ mixedType , $ type );
215223 }
216224
@@ -221,6 +229,40 @@ public function getType(Type $inputType, ?Type $filterType, ?Type $flagsType): T
221229 return $ type ;
222230 }
223231
232+ private function addNestedArrayType (Type $ type , ?Type $ inputValueType , TrinaryLogic $ inputIsArray , ?Type $ filterType , ?Type $ flagsType , MixedType $ mixedType ): Type
233+ {
234+ if ($ inputValueType !== null ) {
235+ // Input was unwrapped - check if the value type could be an array
236+ $ valueTypeIsArray = $ inputValueType ->isArray ();
237+ if ($ valueTypeIsArray ->yes ()) {
238+ // Value type is definitely an array - recursively compute precise nested type
239+ // Replace $type entirely since the scalar filtering path produces incorrect
240+ // results for array inputs (arrays are recursively filtered, not failed)
241+ return $ this ->getType ($ inputValueType , $ filterType , $ flagsType );
242+ }
243+ if ($ valueTypeIsArray ->maybe ()) {
244+ // Value type is a union of scalar and array types (e.g. int|array<int>)
245+ // Try to extract the array part for precise recursive filtering
246+ $ inputArrayPart = TypeCombinator::intersect ($ inputValueType , new ArrayType ($ mixedType , $ mixedType ));
247+ if (!$ inputArrayPart instanceof NeverType && !$ inputArrayPart ->getIterableValueType () instanceof MixedType) {
248+ $ nestedType = $ this ->getType ($ inputArrayPart , $ filterType , $ flagsType );
249+ return TypeCombinator::union ($ type , $ nestedType );
250+ }
251+ // Fall back to generic array for mixed-like value types
252+ return TypeCombinator::union ($ type , new ArrayType ($ mixedType , $ mixedType ));
253+ }
254+ // Value type is definitely not an array - don't add array to type
255+ return $ type ;
256+ }
257+
258+ // No unwrap happened (input is maybe-array, e.g. from filter_input with mixed)
259+ if (!$ inputIsArray ->no ()) {
260+ return TypeCombinator::union ($ type , new ArrayType ($ mixedType , $ mixedType ));
261+ }
262+
263+ return $ type ;
264+ }
265+
224266 /**
225267 * @return array<int, Type>
226268 */
0 commit comments