@@ -341,43 +341,67 @@ 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 ();
353+ }
354354
355- if (!$ type ) {
356- return true ;
357- }
355+ // Extract the type of the argument and handle different possibilities
356+ $ type = $ expectedException ->getType ();
357+
358+ $ isTypeUnion = true ;
359+ $ types = [];
360+
361+ switch (true ) {
362+ case $ type === null :
363+ break ;
364+ case $ type instanceof \ReflectionNamedType:
365+ $ types = [$ type ];
366+ break ;
367+ case $ type instanceof \ReflectionIntersectionType:
368+ $ isTypeUnion = false ;
369+ case $ type instanceof \ReflectionUnionType;
370+ $ types = $ type ->getTypes ();
371+ break ;
372+ default :
373+ throw new \LogicException ('Unexpected return value of ReflectionParameter::getType ' );
374+ }
358375
359- $ types = [$ type ];
376+ // If there is no type restriction, it matches
377+ if (empty ($ types )) {
378+ return true ;
379+ }
360380
361- if ($ type instanceof \ReflectionUnionType) {
362- $ types = $ type ->getTypes ();
381+ foreach ($ types as $ type ) {
382+ if (!$ type instanceof \ReflectionNamedType) {
383+ throw new \LogicException ('This implementation does not support groups of intersection or union types ' );
363384 }
364385
365- $ mismatched = false ;
366-
367- foreach ($ types as $ type ) {
368- if (!$ type || $ type ->isBuiltin ()) {
369- continue ;
370- }
386+ // A named-type can be either a class-name or a built-in type like string, int, array, etc.
387+ $ matches = ($ type ->isBuiltin () && \gettype ($ object ) === $ type ->getName ())
388+ || (new \ReflectionClass ($ type ->getName ()))->isInstance ($ object );
371389
372- $ expectedClass = $ type ->getName ();
373390
374- if ($ object instanceof $ expectedClass ) {
391+ // If we look for a single match (union), we can return early on match
392+ // If we look for a full match (intersection), we can return early on mismatch
393+ if ($ matches ) {
394+ if ($ isTypeUnion ) {
375395 return true ;
376396 }
377-
378- $ mismatched = true ;
397+ } else {
398+ if (!$ isTypeUnion ) {
399+ return false ;
400+ }
379401 }
380-
381- return !$ mismatched ;
382402 }
403+
404+ // If we look for a single match (union) and did not return early, we matched no type and are false
405+ // If we look for a full match (intersection) and did not return early, we matched all types and are true
406+ return $ isTypeUnion ? false : true ;
383407}
0 commit comments