@@ -344,17 +344,10 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool
344344
345345 $ expectedException = $ parameters [0 ];
346346
347- // PHP before v8 used an easy API:
348- if (\PHP_VERSION_ID < 70100 || \defined ('HHVM_VERSION ' )) {
349- if (!$ expectedException ->getClass ()) {
350- return true ;
351- }
352-
353- return $ expectedException ->getClass ()->isInstance ($ reason );
354- }
355-
356347 // Extract the type of the argument and handle different possibilities
357348 $ type = $ expectedException ->getType ();
349+
350+ $ isTypeUnion = true ;
358351 $ types = [];
359352
360353 switch (true ) {
@@ -363,6 +356,8 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool
363356 case $ type instanceof \ReflectionNamedType:
364357 $ types = [$ type ];
365358 break ;
359+ case $ type instanceof \ReflectionIntersectionType:
360+ $ isTypeUnion = false ;
366361 case $ type instanceof \ReflectionUnionType;
367362 $ types = $ type ->getTypes ();
368363 break ;
@@ -375,16 +370,30 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool
375370 return true ;
376371 }
377372
378- // Search for one matching named-type for success, otherwise return false
379- // A named-type can be either a class-name or a built-in type like string, int, array, etc.
380373 foreach ($ types as $ type ) {
374+ if (!$ type instanceof \ReflectionNamedType) {
375+ throw new \LogicException ('This implementation does not support groups of intersection or union types ' );
376+ }
377+
378+ // A named-type can be either a class-name or a built-in type like string, int, array, etc.
381379 $ matches = ($ type ->isBuiltin () && \gettype ($ reason ) === $ type ->getName ())
382380 || (new \ReflectionClass ($ type ->getName ()))->isInstance ($ reason );
383381
382+
383+ // If we look for a single match (union), we can return early on match
384+ // If we look for a full match (intersection), we can return early on mismatch
384385 if ($ matches ) {
385- return true ;
386+ if ($ isTypeUnion ) {
387+ return true ;
388+ }
389+ } else {
390+ if (!$ isTypeUnion ) {
391+ return false ;
392+ }
386393 }
387394 }
388395
389- return false ;
396+ // If we look for a single match (union) and did not return early, we matched no type and are false
397+ // If we look for a full match (intersection) and did not return early, we matched all types and are true
398+ return $ isTypeUnion ? false : true ;
390399}
0 commit comments