From 6c421c46822623b7a6313794cdfdd65d2fd9f140 Mon Sep 17 00:00:00 2001 From: Nicolas Hedger Date: Fri, 16 Sep 2022 16:14:30 +0200 Subject: [PATCH 1/2] feat: add support for DNF types on PHP 8.2 --- src/functions.php | 20 ++++++----- tests/FunctionCheckTypehintTest.php | 33 +++++++++++++++++++ tests/fixtures/ArrayAccessibleException.php | 22 +++++++++++++ .../fixtures/CallbackWithDNFTypehintClass.php | 21 ++++++++++++ 4 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 tests/fixtures/ArrayAccessibleException.php create mode 100644 tests/fixtures/CallbackWithDNFTypehintClass.php diff --git a/src/functions.php b/src/functions.php index 429f0e73..2177dc23 100644 --- a/src/functions.php +++ b/src/functions.php @@ -354,7 +354,7 @@ function _checkTypehint(callable $callback, $object) // Extract the type of the argument and handle different possibilities $type = $expectedException->getType(); - + $isTypeUnion = true; $types = []; @@ -379,14 +379,18 @@ function _checkTypehint(callable $callback, $object) } foreach ($types as $type) { - if (!$type instanceof \ReflectionNamedType) { - throw new \LogicException('This implementation does not support groups of intersection or union types'); - } - - // A named-type can be either a class-name or a built-in type like string, int, array, etc. - $matches = ($type->isBuiltin() && \gettype($object) === $type->getName()) - || (new \ReflectionClass($type->getName()))->isInstance($object); + if ($type instanceof \ReflectionIntersectionType) { + foreach ($type->getTypes() as $typeToMatch) { + if (!($matches = ($typeToMatch->isBuiltin() && \gettype($object) === $typeToMatch->getName()) + || (new \ReflectionClass($typeToMatch->getName()))->isInstance($object))) { + break; + } + } + } else { + $matches = ($type->isBuiltin() && \gettype($object) === $type->getName()) + || (new \ReflectionClass($type->getName()))->isInstance($object); + } // If we look for a single match (union), we can return early on match // If we look for a full match (intersection), we can return early on mismatch diff --git a/tests/FunctionCheckTypehintTest.php b/tests/FunctionCheckTypehintTest.php index b8d3fbe7..719f5e05 100644 --- a/tests/FunctionCheckTypehintTest.php +++ b/tests/FunctionCheckTypehintTest.php @@ -117,6 +117,39 @@ public function shouldAcceptStaticClassCallbackWithIntersectionTypehint() self::assertTrue(_checkTypehint(['React\Promise\CallbackWithIntersectionTypehintClass', 'testCallbackStatic'], new CountableException())); } + /** + * @test + * @requires PHP 8.2 + */ + public function shouldAcceptInvokableObjectCallbackWithDNFTypehint() + { + self::assertFalse(_checkTypehint(new CallbackWithDNFTypehintClass(), new \RuntimeException())); + self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new ArrayAccessibleException())); + self::assertTrue(_checkTypehint(new CallbackWithDNFTypehintClass(), new CountableException())); + } + + /** + * @test + * @requires PHP 8.2 + */ + public function shouldAcceptObjectMethodCallbackWithDNFTypehint() + { + self::assertFalse(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new \RuntimeException())); + self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new CountableException())); + self::assertTrue(_checkTypehint([new CallbackWithDNFTypehintClass(), 'testCallback'], new ArrayAccessibleException())); + } + + /** + * @test + * @requires PHP 8.2 + */ + public function shouldAcceptStaticClassCallbackWithDNFTypehint() + { + self::assertFalse(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new \RuntimeException())); + self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new CountableException())); + self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new ArrayAccessibleException())); + } + /** @test */ public function shouldAcceptClosureCallbackWithoutTypehint() { diff --git a/tests/fixtures/ArrayAccessibleException.php b/tests/fixtures/ArrayAccessibleException.php new file mode 100644 index 00000000..71b7ad2a --- /dev/null +++ b/tests/fixtures/ArrayAccessibleException.php @@ -0,0 +1,22 @@ + Date: Fri, 16 Sep 2022 16:33:44 +0200 Subject: [PATCH 2/2] fix: solve syntax errors with older PHP versions --- tests/FunctionCheckTypehintTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/FunctionCheckTypehintTest.php b/tests/FunctionCheckTypehintTest.php index 719f5e05..ed923d50 100644 --- a/tests/FunctionCheckTypehintTest.php +++ b/tests/FunctionCheckTypehintTest.php @@ -145,9 +145,9 @@ public function shouldAcceptObjectMethodCallbackWithDNFTypehint() */ public function shouldAcceptStaticClassCallbackWithDNFTypehint() { - self::assertFalse(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new \RuntimeException())); - self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new CountableException())); - self::assertTrue(_checkTypehint([CallbackWithDNFTypehintClass::class, 'testCallbackStatic'], new ArrayAccessibleException())); + self::assertFalse(_checkTypehint(['React\Promise\CallbackWithDNFTypehintClass', 'testCallbackStatic'], new \RuntimeException())); + self::assertTrue(_checkTypehint(['React\Promise\CallbackWithDNFTypehintClass', 'testCallbackStatic'], new CountableException())); + self::assertTrue(_checkTypehint(['React\Promise\CallbackWithDNFTypehintClass', 'testCallbackStatic'], new ArrayAccessibleException())); } /** @test */