diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index ac70af5ae59..aebf87f4260 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -145,6 +145,11 @@ public function getOptionalKeys(): array return $this->optionalKeys; } + public function getArrays(): array + { + return $this->getAllArrays(); + } + /** * @return self[] */ diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 1aeaf6eb513..d089303fb25 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -109,6 +109,10 @@ public function getArrays(): array { $arrays = []; foreach ($this->types as $type) { + if (!$type instanceof ArrayType) { + continue; + } + foreach ($type->getArrays() as $array) { $arrays[] = $array; } diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index 9a0477237c8..c9853c7943d 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -116,6 +116,10 @@ public function getArrays(): array { $arrays = []; foreach ($this->types as $type) { + if (!$type instanceof ArrayType) { + return []; + } + foreach ($type->getArrays() as $array) { $arrays[] = $array; } diff --git a/tests/PHPStan/Type/UnionTypeTest.php b/tests/PHPStan/Type/UnionTypeTest.php index c18374b1254..61d47685341 100644 --- a/tests/PHPStan/Type/UnionTypeTest.php +++ b/tests/PHPStan/Type/UnionTypeTest.php @@ -1410,4 +1410,77 @@ public function dataGetConstantStrings(): iterable ]; } + /** + * @dataProvider dataGetArrays + * @param list $expectedDescriptions + */ + public function testGetArrays( + Type $unionType, + array $expectedDescriptions, + ): void + { + $arrays = $unionType->getArrays(); + + $actualDescriptions = []; + foreach ($arrays as $arrayType) { + $actualDescriptions[] = $arrayType->describe(VerbosityLevel::precise()); + } + + $this->assertSame($expectedDescriptions, $actualDescriptions); + } + + public function dataGetArrays(): iterable + { + yield from [ + [ + TypeCombinator::union( + new ConstantStringType('hello'), + new ConstantStringType('world'), + ), + [], + ], + [ + TypeCombinator::union( + TypeCombinator::intersect( + new ConstantArrayType( + [new ConstantIntegerType(1), new ConstantIntegerType(2)], + [new IntegerType(), new StringType()], + 2, + [0, 1], + ), + new NonEmptyArrayType(), + ), + new ConstantArrayType( + [new ConstantIntegerType(0), new ConstantIntegerType(1)], + [new ObjectType(Foo::class), new ObjectType(stdClass::class)], + 2, + ), + ), + [ + 'array{1?: int, 2?: string}', + 'array{RecursionCallable\Foo, stdClass}', + ], + ], + [ + TypeCombinator::union( + new ArrayType(new IntegerType(), new StringType()), + new ConstantArrayType( + [new ConstantIntegerType(1), new ConstantIntegerType(2)], + [new IntegerType(), new StringType()], + 2, + [0, 1], + ), + new ConstantArrayType( + [new ConstantIntegerType(0), new ConstantIntegerType(1)], + [new ObjectType(Foo::class), new ObjectType(stdClass::class)], + 2, + ), + ), + [ + 'array', + ], + ], + ]; + } + }