Skip to content

Commit

Permalink
Improve _checkTypehint for PHP8+
Browse files Browse the repository at this point in the history
PHP8's reflection API changed to make room for handling of union-types. As one of the consequences `ReflectionParameter::getClass` got marked as deprecated.
While the bug got already fixed in the meantime, the proposed code clears up the code-path for future features like PHP8.1's intersection-types.
  • Loading branch information
bzikarsky committed Feb 4, 2022
1 parent 29daf46 commit 7be7d9b
Showing 1 changed file with 32 additions and 26 deletions.
58 changes: 32 additions & 26 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -341,43 +341,49 @@ function _checkTypehint(callable $callback, $object)
return true;
}

if (\PHP_VERSION_ID < 70100 || \defined('HHVM_VERSION')) {
$expectedException = $parameters[0];
$expectedException = $parameters[0];

// PHP before v8 used an easy API:
if (\PHP_VERSION_ID < 70100 || \defined('HHVM_VERSION')) {
if (!$expectedException->getClass()) {
return true;
}

return $expectedException->getClass()->isInstance($object);
} else {
$type = $parameters[0]->getType();

if (!$type) {
return true;
}

$types = [$type];
}

if ($type instanceof \ReflectionUnionType) {
// Extract the type of the argument and handle different possibilities
$type = $expectedException->getType();
$types = [];

switch (true) {
case $type === null:
break;
case $type instanceof \ReflectionNamedType:
$types = [$type];
break;
case $type instanceof \ReflectionUnionType;
$types = $type->getTypes();
}

$mismatched = false;

foreach ($types as $type) {
if (!$type || $type->isBuiltin()) {
continue;
}
break;
default:
throw new \LogicException('Unexpected return value of ReflectionParameter::getType');
}

$expectedClass = $type->getName();
// If there is no type restriction, it matches
if (empty($types)) {
return true;
}

if ($object instanceof $expectedClass) {
return true;
}
// Search for one matching named-type for success, otherwise return false
// A named-type can be either a class-name or a built-in type like string, int, array, etc.
foreach ($types as $type) {
$matches = ($type->isBuiltin() && \gettype($object) === $type->getName())
|| (new \ReflectionClass($type->getName()))->isInstance($object);

$mismatched = true;
if ($matches) {
return true;
}

return !$mismatched;
}

return false;
}

0 comments on commit 7be7d9b

Please sign in to comment.