From 2ed085dcf86b3a8151eff6c5b5f100d6975e3ad2 Mon Sep 17 00:00:00 2001 From: cdosoftei Date: Tue, 11 Aug 2020 15:27:22 -0400 Subject: [PATCH 1/7] php8: address ReflectionParameter::getClass deprecation --- src/functions.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/functions.php b/src/functions.php index 31e7db7b..ad0c9e40 100644 --- a/src/functions.php +++ b/src/functions.php @@ -342,7 +342,17 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool return true; } - $expectedClass = $parameters[0]->getClass(); + $expectedClass = null; + if (method_exists($parameters[0], 'getType')) { + $type = $parameters[0]->getType(); + if (!$type || $type->isBuiltin()) { + return true; + } + + $expectedClass = new \ReflectionClass(method_exists($type, 'getName') ? $type->getName() : (string) $type); + } else { + $expectedClass = $parameters[0]->getClass(); + } if (!$expectedClass) { return true; From 8a8150d48a6c5ad2444440316787dd914e9cc52f Mon Sep 17 00:00:00 2001 From: cdosoftei Date: Wed, 12 Aug 2020 23:56:00 -0400 Subject: [PATCH 2/7] Adjustments for 7.1, support for union types, test suite updates --- phpunit.xml.dist | 30 +++++++++--------- src/functions.php | 38 ++++++++++++++++------- tests/ErrorCollector.php | 4 +-- tests/FunctionCheckTypehintTest.php | 47 ++++++++++++++++++++++++++++- 4 files changed, 89 insertions(+), 30 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 0b97c746..4b82339d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,18 +1,16 @@ - - - - - ./tests/ - - - - - - ./src/ - - ./src/functions_include.php - - - + + + + ./src/ + + + ./src/functions_include.php + + + + + ./tests/ + + diff --git a/src/functions.php b/src/functions.php index ad0c9e40..f80f7804 100644 --- a/src/functions.php +++ b/src/functions.php @@ -342,21 +342,37 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool return true; } - $expectedClass = null; - if (method_exists($parameters[0], 'getType')) { - $type = $parameters[0]->getType(); + $type = $parameters[0]->getType(); + + if (!$type) { + return true; + } + + $types = [$type]; + + if (\method_exists($type, 'getTypes')) { + $types = $type->getTypes(); + } + + $mismatched = false; + + foreach ($types as $type) { if (!$type || $type->isBuiltin()) { - return true; + continue; } - $expectedClass = new \ReflectionClass(method_exists($type, 'getName') ? $type->getName() : (string) $type); - } else { - $expectedClass = $parameters[0]->getClass(); - } + try { + $expectedClass = new \ReflectionClass($type->getName()); - if (!$expectedClass) { - return true; + if ($expectedClass->isInstance($reason)) { + return true; + } else { + $mismatched = true; + } + } catch (\ReflectionException $exception) { + $mismatched = true; + } } - return $expectedClass->isInstance($reason); + return !$mismatched; } diff --git a/tests/ErrorCollector.php b/tests/ErrorCollector.php index 970f8628..0ad3fffd 100644 --- a/tests/ErrorCollector.php +++ b/tests/ErrorCollector.php @@ -10,8 +10,8 @@ public function start() { $errors = []; - set_error_handler(function ($errno, $errstr, $errfile, $errline, $errcontext) use (&$errors) { - $errors[] = compact('errno', 'errstr', 'errfile', 'errline', 'errcontext'); + set_error_handler(function ($errno, $errstr, $errfile, $errline) use (&$errors) { + $errors[] = compact('errno', 'errstr', 'errfile', 'errline'); }); $this->errors = &$errors; diff --git a/tests/FunctionCheckTypehintTest.php b/tests/FunctionCheckTypehintTest.php index bebda2c7..ee651289 100644 --- a/tests/FunctionCheckTypehintTest.php +++ b/tests/FunctionCheckTypehintTest.php @@ -42,6 +42,33 @@ public function shouldAcceptStaticClassCallbackWithTypehint() self::assertFalse(_checkTypehint([TestCallbackWithTypehintClass::class, 'testCallbackStatic'], new Exception())); } + // Requires PHP 8 + /* + public function shouldAcceptClosureCallbackWithUnionTypehint() + { + self::assertTrue(_checkTypehint(function (RuntimeException|InvalidArgumentException $e) {}, new InvalidArgumentException())); + self::assertFalse(_checkTypehint(function (RuntimeException|InvalidArgumentException $e) {}, new Exception())); + } + + public function shouldAcceptInvokableObjectCallbackWithUnionTypehint() + { + self::assertTrue(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new InvalidArgumentException())); + self::assertFalse(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new Exception())); + } + + public function shouldAcceptObjectMethodCallbackWithUnionTypehint() + { + self::assertTrue(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new Exception())); + } + + public function shouldAcceptStaticClassCallbackWithUnionTypehint() + { + self::assertTrue(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new Exception())); + } + */ + /** @test */ public function shouldAcceptClosureCallbackWithoutTypehint() { @@ -52,7 +79,7 @@ public function shouldAcceptClosureCallbackWithoutTypehint() /** @test */ public function shouldAcceptFunctionStringCallbackWithoutTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new InvalidArgumentException())); + self::assertTrue(_checkTypehint(new TestCallbackWithoutTypehintClass(), new InvalidArgumentException())); } /** @test */ @@ -97,6 +124,24 @@ public static function testCallbackStatic(InvalidArgumentException $e) } } +// Requires PHP 8 +/* +class TestCallbackWithUnionTypehintClass +{ + public function __invoke(RuntimeException|InvalidArgumentException $e) + { + } + + public function testCallback(RuntimeException|InvalidArgumentException $e) + { + } + + public static function testCallbackStatic(RuntimeException|InvalidArgumentException $e) + { + } +} +*/ + class TestCallbackWithoutTypehintClass { public function __invoke() From fa620fadb982f4f917e007afb2489ed12756c1c1 Mon Sep 17 00:00:00 2001 From: cdosoftei Date: Tue, 18 Aug 2020 08:59:12 -0400 Subject: [PATCH 3/7] Reinstated previous PHPUnit XML config (dupe of #177) --- phpunit.xml.dist | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4b82339d..0b97c746 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,16 +1,18 @@ - - - - ./src/ - - - ./src/functions_include.php - - - - - ./tests/ - - + + + + + ./tests/ + + + + + + ./src/ + + ./src/functions_include.php + + + From 0f27f37e2ed1c8e9120630850bb5aefffb6213a0 Mon Sep 17 00:00:00 2001 From: cdosoftei Date: Tue, 18 Aug 2020 10:13:04 -0400 Subject: [PATCH 4/7] Code tweaks and test adjustments --- src/functions.php | 16 +++----- tests/FunctionCheckTypehintTest.php | 59 ++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/functions.php b/src/functions.php index f80f7804..df50eb33 100644 --- a/src/functions.php +++ b/src/functions.php @@ -350,7 +350,7 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool $types = [$type]; - if (\method_exists($type, 'getTypes')) { + if ($type instanceof \ReflectionUnionType) { $types = $type->getTypes(); } @@ -361,17 +361,13 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool continue; } - try { - $expectedClass = new \ReflectionClass($type->getName()); + $expectedClass = $type->getName(); - if ($expectedClass->isInstance($reason)) { - return true; - } else { - $mismatched = true; - } - } catch (\ReflectionException $exception) { - $mismatched = true; + if ($reason instanceof $expectedClass) { + return true; } + + $mismatched = true; } return !$mismatched; diff --git a/tests/FunctionCheckTypehintTest.php b/tests/FunctionCheckTypehintTest.php index ee651289..2fd17295 100644 --- a/tests/FunctionCheckTypehintTest.php +++ b/tests/FunctionCheckTypehintTest.php @@ -5,6 +5,8 @@ use Exception; use InvalidArgumentException; +define('UNION_TYPE_TESTS_ENABLED', defined('PHP_MAJOR_VERSION') && (PHP_MAJOR_VERSION >= 8)); + class FunctionCheckTypehintTest extends TestCase { /** @test */ @@ -42,32 +44,52 @@ public function shouldAcceptStaticClassCallbackWithTypehint() self::assertFalse(_checkTypehint([TestCallbackWithTypehintClass::class, 'testCallbackStatic'], new Exception())); } - // Requires PHP 8 - /* + /** @test */ public function shouldAcceptClosureCallbackWithUnionTypehint() { - self::assertTrue(_checkTypehint(function (RuntimeException|InvalidArgumentException $e) {}, new InvalidArgumentException())); - self::assertFalse(_checkTypehint(function (RuntimeException|InvalidArgumentException $e) {}, new Exception())); + if (UNION_TYPE_TESTS_ENABLED) { + eval( + 'namespace React\Promise;' . + 'self::assertTrue(_checkTypehint(function (\RuntimeException|\InvalidArgumentException $e) {}, new \InvalidArgumentException()));' . + 'self::assertFalse(_checkTypehint(function (\RuntimeException|\InvalidArgumentException $e) {}, new \Exception()));' + ); + } else { + self::expectNotToPerformAssertions(); + } } + /** @test */ public function shouldAcceptInvokableObjectCallbackWithUnionTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new InvalidArgumentException())); - self::assertFalse(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new Exception())); + if (UNION_TYPE_TESTS_ENABLED) { + self::assertTrue(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new InvalidArgumentException())); + self::assertFalse(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new Exception())); + } else { + self::expectNotToPerformAssertions(); + } } + /** @test */ public function shouldAcceptObjectMethodCallbackWithUnionTypehint() { - self::assertTrue(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new InvalidArgumentException())); - self::assertFalse(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new Exception())); + if (UNION_TYPE_TESTS_ENABLED) { + self::assertTrue(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new Exception())); + } else { + self::expectNotToPerformAssertions(); + } } + /** @test */ public function shouldAcceptStaticClassCallbackWithUnionTypehint() { - self::assertTrue(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); - self::assertFalse(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new Exception())); + if (UNION_TYPE_TESTS_ENABLED) { + self::assertTrue(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new Exception())); + } else { + self::expectNotToPerformAssertions(); + } } - */ /** @test */ public function shouldAcceptClosureCallbackWithoutTypehint() @@ -124,23 +146,26 @@ public static function testCallbackStatic(InvalidArgumentException $e) } } -// Requires PHP 8 -/* +if (UNION_TYPE_TESTS_ENABLED) { + eval(<< Date: Wed, 19 Aug 2020 16:29:30 -0400 Subject: [PATCH 5/7] Test tweaks --- tests/FunctionCheckTypehintTest.php | 62 +++++++++++++---------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/tests/FunctionCheckTypehintTest.php b/tests/FunctionCheckTypehintTest.php index 2fd17295..171c9b71 100644 --- a/tests/FunctionCheckTypehintTest.php +++ b/tests/FunctionCheckTypehintTest.php @@ -5,8 +5,6 @@ use Exception; use InvalidArgumentException; -define('UNION_TYPE_TESTS_ENABLED', defined('PHP_MAJOR_VERSION') && (PHP_MAJOR_VERSION >= 8)); - class FunctionCheckTypehintTest extends TestCase { /** @test */ @@ -44,51 +42,47 @@ public function shouldAcceptStaticClassCallbackWithTypehint() self::assertFalse(_checkTypehint([TestCallbackWithTypehintClass::class, 'testCallbackStatic'], new Exception())); } - /** @test */ + /** + * @test + * @requires PHP 8 + */ public function shouldAcceptClosureCallbackWithUnionTypehint() { - if (UNION_TYPE_TESTS_ENABLED) { - eval( - 'namespace React\Promise;' . - 'self::assertTrue(_checkTypehint(function (\RuntimeException|\InvalidArgumentException $e) {}, new \InvalidArgumentException()));' . - 'self::assertFalse(_checkTypehint(function (\RuntimeException|\InvalidArgumentException $e) {}, new \Exception()));' - ); - } else { - self::expectNotToPerformAssertions(); - } + eval( + 'namespace React\Promise;' . + 'self::assertTrue(_checkTypehint(function (\RuntimeException|\InvalidArgumentException $e) {}, new \InvalidArgumentException()));' . + 'self::assertFalse(_checkTypehint(function (\RuntimeException|\InvalidArgumentException $e) {}, new \Exception()));' + ); } - /** @test */ + /** + * @test + * @requires PHP 8 + */ public function shouldAcceptInvokableObjectCallbackWithUnionTypehint() { - if (UNION_TYPE_TESTS_ENABLED) { - self::assertTrue(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new InvalidArgumentException())); - self::assertFalse(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new Exception())); - } else { - self::expectNotToPerformAssertions(); - } + self::assertTrue(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new InvalidArgumentException())); + self::assertFalse(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new Exception())); } - /** @test */ + /** + * @test + * @requires PHP 8 + */ public function shouldAcceptObjectMethodCallbackWithUnionTypehint() { - if (UNION_TYPE_TESTS_ENABLED) { - self::assertTrue(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new InvalidArgumentException())); - self::assertFalse(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new Exception())); - } else { - self::expectNotToPerformAssertions(); - } + self::assertTrue(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new Exception())); } - /** @test */ + /** + * @test + * @requires PHP 8 + */ public function shouldAcceptStaticClassCallbackWithUnionTypehint() { - if (UNION_TYPE_TESTS_ENABLED) { - self::assertTrue(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); - self::assertFalse(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new Exception())); - } else { - self::expectNotToPerformAssertions(); - } + self::assertTrue(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new Exception())); } /** @test */ @@ -146,7 +140,7 @@ public static function testCallbackStatic(InvalidArgumentException $e) } } -if (UNION_TYPE_TESTS_ENABLED) { +if (defined('PHP_MAJOR_VERSION') && (PHP_MAJOR_VERSION >= 8)) { eval(<< Date: Thu, 20 Aug 2020 10:26:13 -0400 Subject: [PATCH 6/7] _checkType tests cleanup --- tests/FunctionCheckTypehintTest.php | 87 ++++--------------- tests/fixtures/CallbackWithTypehintClass.php | 20 +++++ .../CallbackWithUnionTypehintClass.php | 21 +++++ .../fixtures/CallbackWithoutTypehintClass.php | 18 ++++ 4 files changed, 77 insertions(+), 69 deletions(-) create mode 100644 tests/fixtures/CallbackWithTypehintClass.php create mode 100644 tests/fixtures/CallbackWithUnionTypehintClass.php create mode 100644 tests/fixtures/CallbackWithoutTypehintClass.php diff --git a/tests/FunctionCheckTypehintTest.php b/tests/FunctionCheckTypehintTest.php index 171c9b71..37a438d7 100644 --- a/tests/FunctionCheckTypehintTest.php +++ b/tests/FunctionCheckTypehintTest.php @@ -17,29 +17,29 @@ public function shouldAcceptClosureCallbackWithTypehint() /** @test */ public function shouldAcceptFunctionStringCallbackWithTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new InvalidArgumentException())); - self::assertFalse(_checkTypehint(new TestCallbackWithTypehintClass(), new Exception())); + self::assertTrue(_checkTypehint(new CallbackWithTypehintClass(), new InvalidArgumentException())); + self::assertFalse(_checkTypehint(new CallbackWithTypehintClass(), new Exception())); } /** @test */ public function shouldAcceptInvokableObjectCallbackWithTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new InvalidArgumentException())); - self::assertFalse(_checkTypehint(new TestCallbackWithTypehintClass(), new Exception())); + self::assertTrue(_checkTypehint(new CallbackWithTypehintClass(), new InvalidArgumentException())); + self::assertFalse(_checkTypehint(new CallbackWithTypehintClass(), new Exception())); } /** @test */ public function shouldAcceptObjectMethodCallbackWithTypehint() { - self::assertTrue(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new InvalidArgumentException())); - self::assertFalse(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new Exception())); + self::assertTrue(_checkTypehint([new CallbackWithTypehintClass(), 'testCallback'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([new CallbackWithTypehintClass(), 'testCallback'], new Exception())); } /** @test */ public function shouldAcceptStaticClassCallbackWithTypehint() { - self::assertTrue(_checkTypehint([TestCallbackWithTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); - self::assertFalse(_checkTypehint([TestCallbackWithTypehintClass::class, 'testCallbackStatic'], new Exception())); + self::assertTrue(_checkTypehint([CallbackWithTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([CallbackWithTypehintClass::class, 'testCallbackStatic'], new Exception())); } /** @@ -61,8 +61,8 @@ public function shouldAcceptClosureCallbackWithUnionTypehint() */ public function shouldAcceptInvokableObjectCallbackWithUnionTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new InvalidArgumentException())); - self::assertFalse(_checkTypehint(new TestCallbackWithUnionTypehintClass(), new Exception())); + self::assertTrue(_checkTypehint(new CallbackWithUnionTypehintClass(), new InvalidArgumentException())); + self::assertFalse(_checkTypehint(new CallbackWithUnionTypehintClass(), new Exception())); } /** @@ -71,8 +71,8 @@ public function shouldAcceptInvokableObjectCallbackWithUnionTypehint() */ public function shouldAcceptObjectMethodCallbackWithUnionTypehint() { - self::assertTrue(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new InvalidArgumentException())); - self::assertFalse(_checkTypehint([new TestCallbackWithUnionTypehintClass(), 'testCallback'], new Exception())); + self::assertTrue(_checkTypehint([new CallbackWithUnionTypehintClass(), 'testCallback'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([new CallbackWithUnionTypehintClass(), 'testCallback'], new Exception())); } /** @@ -81,8 +81,8 @@ public function shouldAcceptObjectMethodCallbackWithUnionTypehint() */ public function shouldAcceptStaticClassCallbackWithUnionTypehint() { - self::assertTrue(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); - self::assertFalse(_checkTypehint([TestCallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new Exception())); + self::assertTrue(_checkTypehint([CallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([CallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new Exception())); } /** @test */ @@ -95,25 +95,25 @@ public function shouldAcceptClosureCallbackWithoutTypehint() /** @test */ public function shouldAcceptFunctionStringCallbackWithoutTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithoutTypehintClass(), new InvalidArgumentException())); + self::assertTrue(_checkTypehint(new CallbackWithoutTypehintClass(), new InvalidArgumentException())); } /** @test */ public function shouldAcceptInvokableObjectCallbackWithoutTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithoutTypehintClass(), new InvalidArgumentException())); + self::assertTrue(_checkTypehint(new CallbackWithoutTypehintClass(), new InvalidArgumentException())); } /** @test */ public function shouldAcceptObjectMethodCallbackWithoutTypehint() { - self::assertTrue(_checkTypehint([new TestCallbackWithoutTypehintClass(), 'testCallback'], new InvalidArgumentException())); + self::assertTrue(_checkTypehint([new CallbackWithoutTypehintClass(), 'testCallback'], new InvalidArgumentException())); } /** @test */ public function shouldAcceptStaticClassCallbackWithoutTypehint() { - self::assertTrue(_checkTypehint([TestCallbackWithoutTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); + self::assertTrue(_checkTypehint([CallbackWithoutTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); } } @@ -124,54 +124,3 @@ function testCallbackWithTypehint(InvalidArgumentException $e) function testCallbackWithoutTypehint() { } - -class TestCallbackWithTypehintClass -{ - public function __invoke(InvalidArgumentException $e) - { - } - - public function testCallback(InvalidArgumentException $e) - { - } - - public static function testCallbackStatic(InvalidArgumentException $e) - { - } -} - -if (defined('PHP_MAJOR_VERSION') && (PHP_MAJOR_VERSION >= 8)) { - eval(<< Date: Tue, 11 Aug 2020 15:27:22 -0400 Subject: [PATCH 7/7] php8: address ReflectionParameter::getClass deprecation Adjustments for 7.1, support for union types, test suite updates Reinstated previous PHPUnit XML config (dupe of #177) Code tweaks and test adjustments Test tweaks _checkType tests cleanup --- src/functions.php | 28 +++++- tests/ErrorCollector.php | 4 +- tests/FunctionCheckTypehintTest.php | 97 +++++++++++-------- tests/fixtures/CallbackWithTypehintClass.php | 20 ++++ .../CallbackWithUnionTypehintClass.php | 21 ++++ .../fixtures/CallbackWithoutTypehintClass.php | 18 ++++ 6 files changed, 141 insertions(+), 47 deletions(-) create mode 100644 tests/fixtures/CallbackWithTypehintClass.php create mode 100644 tests/fixtures/CallbackWithUnionTypehintClass.php create mode 100644 tests/fixtures/CallbackWithoutTypehintClass.php diff --git a/src/functions.php b/src/functions.php index 31e7db7b..df50eb33 100644 --- a/src/functions.php +++ b/src/functions.php @@ -342,11 +342,33 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool return true; } - $expectedClass = $parameters[0]->getClass(); + $type = $parameters[0]->getType(); - if (!$expectedClass) { + if (!$type) { return true; } - return $expectedClass->isInstance($reason); + $types = [$type]; + + if ($type instanceof \ReflectionUnionType) { + $types = $type->getTypes(); + } + + $mismatched = false; + + foreach ($types as $type) { + if (!$type || $type->isBuiltin()) { + continue; + } + + $expectedClass = $type->getName(); + + if ($reason instanceof $expectedClass) { + return true; + } + + $mismatched = true; + } + + return !$mismatched; } diff --git a/tests/ErrorCollector.php b/tests/ErrorCollector.php index 970f8628..0ad3fffd 100644 --- a/tests/ErrorCollector.php +++ b/tests/ErrorCollector.php @@ -10,8 +10,8 @@ public function start() { $errors = []; - set_error_handler(function ($errno, $errstr, $errfile, $errline, $errcontext) use (&$errors) { - $errors[] = compact('errno', 'errstr', 'errfile', 'errline', 'errcontext'); + set_error_handler(function ($errno, $errstr, $errfile, $errline) use (&$errors) { + $errors[] = compact('errno', 'errstr', 'errfile', 'errline'); }); $this->errors = &$errors; diff --git a/tests/FunctionCheckTypehintTest.php b/tests/FunctionCheckTypehintTest.php index bebda2c7..37a438d7 100644 --- a/tests/FunctionCheckTypehintTest.php +++ b/tests/FunctionCheckTypehintTest.php @@ -17,29 +17,72 @@ public function shouldAcceptClosureCallbackWithTypehint() /** @test */ public function shouldAcceptFunctionStringCallbackWithTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new InvalidArgumentException())); - self::assertFalse(_checkTypehint(new TestCallbackWithTypehintClass(), new Exception())); + self::assertTrue(_checkTypehint(new CallbackWithTypehintClass(), new InvalidArgumentException())); + self::assertFalse(_checkTypehint(new CallbackWithTypehintClass(), new Exception())); } /** @test */ public function shouldAcceptInvokableObjectCallbackWithTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new InvalidArgumentException())); - self::assertFalse(_checkTypehint(new TestCallbackWithTypehintClass(), new Exception())); + self::assertTrue(_checkTypehint(new CallbackWithTypehintClass(), new InvalidArgumentException())); + self::assertFalse(_checkTypehint(new CallbackWithTypehintClass(), new Exception())); } /** @test */ public function shouldAcceptObjectMethodCallbackWithTypehint() { - self::assertTrue(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new InvalidArgumentException())); - self::assertFalse(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new Exception())); + self::assertTrue(_checkTypehint([new CallbackWithTypehintClass(), 'testCallback'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([new CallbackWithTypehintClass(), 'testCallback'], new Exception())); } /** @test */ public function shouldAcceptStaticClassCallbackWithTypehint() { - self::assertTrue(_checkTypehint([TestCallbackWithTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); - self::assertFalse(_checkTypehint([TestCallbackWithTypehintClass::class, 'testCallbackStatic'], new Exception())); + self::assertTrue(_checkTypehint([CallbackWithTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([CallbackWithTypehintClass::class, 'testCallbackStatic'], new Exception())); + } + + /** + * @test + * @requires PHP 8 + */ + public function shouldAcceptClosureCallbackWithUnionTypehint() + { + eval( + 'namespace React\Promise;' . + 'self::assertTrue(_checkTypehint(function (\RuntimeException|\InvalidArgumentException $e) {}, new \InvalidArgumentException()));' . + 'self::assertFalse(_checkTypehint(function (\RuntimeException|\InvalidArgumentException $e) {}, new \Exception()));' + ); + } + + /** + * @test + * @requires PHP 8 + */ + public function shouldAcceptInvokableObjectCallbackWithUnionTypehint() + { + self::assertTrue(_checkTypehint(new CallbackWithUnionTypehintClass(), new InvalidArgumentException())); + self::assertFalse(_checkTypehint(new CallbackWithUnionTypehintClass(), new Exception())); + } + + /** + * @test + * @requires PHP 8 + */ + public function shouldAcceptObjectMethodCallbackWithUnionTypehint() + { + self::assertTrue(_checkTypehint([new CallbackWithUnionTypehintClass(), 'testCallback'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([new CallbackWithUnionTypehintClass(), 'testCallback'], new Exception())); + } + + /** + * @test + * @requires PHP 8 + */ + public function shouldAcceptStaticClassCallbackWithUnionTypehint() + { + self::assertTrue(_checkTypehint([CallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); + self::assertFalse(_checkTypehint([CallbackWithUnionTypehintClass::class, 'testCallbackStatic'], new Exception())); } /** @test */ @@ -52,25 +95,25 @@ public function shouldAcceptClosureCallbackWithoutTypehint() /** @test */ public function shouldAcceptFunctionStringCallbackWithoutTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new InvalidArgumentException())); + self::assertTrue(_checkTypehint(new CallbackWithoutTypehintClass(), new InvalidArgumentException())); } /** @test */ public function shouldAcceptInvokableObjectCallbackWithoutTypehint() { - self::assertTrue(_checkTypehint(new TestCallbackWithoutTypehintClass(), new InvalidArgumentException())); + self::assertTrue(_checkTypehint(new CallbackWithoutTypehintClass(), new InvalidArgumentException())); } /** @test */ public function shouldAcceptObjectMethodCallbackWithoutTypehint() { - self::assertTrue(_checkTypehint([new TestCallbackWithoutTypehintClass(), 'testCallback'], new InvalidArgumentException())); + self::assertTrue(_checkTypehint([new CallbackWithoutTypehintClass(), 'testCallback'], new InvalidArgumentException())); } /** @test */ public function shouldAcceptStaticClassCallbackWithoutTypehint() { - self::assertTrue(_checkTypehint([TestCallbackWithoutTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); + self::assertTrue(_checkTypehint([CallbackWithoutTypehintClass::class, 'testCallbackStatic'], new InvalidArgumentException())); } } @@ -81,33 +124,3 @@ function testCallbackWithTypehint(InvalidArgumentException $e) function testCallbackWithoutTypehint() { } - -class TestCallbackWithTypehintClass -{ - public function __invoke(InvalidArgumentException $e) - { - } - - public function testCallback(InvalidArgumentException $e) - { - } - - public static function testCallbackStatic(InvalidArgumentException $e) - { - } -} - -class TestCallbackWithoutTypehintClass -{ - public function __invoke() - { - } - - public function testCallback() - { - } - - public static function testCallbackStatic() - { - } -} diff --git a/tests/fixtures/CallbackWithTypehintClass.php b/tests/fixtures/CallbackWithTypehintClass.php new file mode 100644 index 00000000..12dd0da4 --- /dev/null +++ b/tests/fixtures/CallbackWithTypehintClass.php @@ -0,0 +1,20 @@ +