diff --git a/src/Assert/Filter.php b/src/Assert/Filter.php index 4f765f9..2e6a79b 100644 --- a/src/Assert/Filter.php +++ b/src/Assert/Filter.php @@ -42,13 +42,26 @@ public static function boolean(callable $filter): void $separator = $returnType instanceof ReflectionUnionType ? '|' : '&'; $types = $returnType->getTypes(); - Assert::allIsInstanceOf($types, ReflectionNamedType::class); + Assert::allIsInstanceOfAny($types, [ReflectionNamedType::class, ReflectionIntersectionType::class]); throw new InvalidArgumentException( sprintf( self::MESSAGE, implode($separator, array_map( - static fn (ReflectionNamedType $reflectionNamedType): string => $reflectionNamedType->getName(), + function (ReflectionNamedType|ReflectionIntersectionType $reflectionType): string { + if ($reflectionType instanceof ReflectionNamedType) { + return $reflectionType->getName(); + } + + $subTypes = $reflectionType->getTypes(); + Assert::allIsInstanceOf($subTypes, ReflectionNamedType::class); + + return '(' . implode('&', array_map( + static fn (ReflectionNamedType $reflectionNamedType): string + => $reflectionNamedType->getName(), + $subTypes + )) . ')'; + }, $types )) ) diff --git a/tests/FilterTest.php b/tests/FilterTest.php index 3256e9d..b9ecad0 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -133,4 +133,21 @@ public function __invoke(int $datum): A&B AtLeast::once($data, $filter); } + + public function testWithUnionedIntersectionTypeCallable(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + 'Expected a bool return type on callable filter, (ArrayLookup\Tests\A&ArrayLookup\Tests\B)|string given' + ); + + $data = [1, 2, 3]; + $filter = new class { + public function __invoke(int $datum): (A&B)|string + { + } + }; + + AtLeast::once($data, $filter); + } }