Skip to content

Commit 7be7d9b

Browse files
committed
Improve _checkTypehint for PHP8+
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.
1 parent 29daf46 commit 7be7d9b

File tree

1 file changed

+32
-26
lines changed

1 file changed

+32
-26
lines changed

src/functions.php

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -341,43 +341,49 @@ function _checkTypehint(callable $callback, $object)
341341
return true;
342342
}
343343

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

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

351352
return $expectedException->getClass()->isInstance($object);
352-
} else {
353-
$type = $parameters[0]->getType();
354-
355-
if (!$type) {
356-
return true;
357-
}
358-
359-
$types = [$type];
353+
}
360354

361-
if ($type instanceof \ReflectionUnionType) {
355+
// Extract the type of the argument and handle different possibilities
356+
$type = $expectedException->getType();
357+
$types = [];
358+
359+
switch (true) {
360+
case $type === null:
361+
break;
362+
case $type instanceof \ReflectionNamedType:
363+
$types = [$type];
364+
break;
365+
case $type instanceof \ReflectionUnionType;
362366
$types = $type->getTypes();
363-
}
364-
365-
$mismatched = false;
366-
367-
foreach ($types as $type) {
368-
if (!$type || $type->isBuiltin()) {
369-
continue;
370-
}
367+
break;
368+
default:
369+
throw new \LogicException('Unexpected return value of ReflectionParameter::getType');
370+
}
371371

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

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

378-
$mismatched = true;
383+
if ($matches) {
384+
return true;
379385
}
380-
381-
return !$mismatched;
382386
}
387+
388+
return false;
383389
}

0 commit comments

Comments
 (0)