@@ -344,17 +344,10 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool
344
344
345
345
$ expectedException = $ parameters [0 ];
346
346
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
-
356
347
// Extract the type of the argument and handle different possibilities
357
348
$ type = $ expectedException ->getType ();
349
+
350
+ $ isTypeUnion = true ;
358
351
$ types = [];
359
352
360
353
switch (true ) {
@@ -363,6 +356,8 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool
363
356
case $ type instanceof \ReflectionNamedType:
364
357
$ types = [$ type ];
365
358
break ;
359
+ case $ type instanceof \ReflectionIntersectionType:
360
+ $ isTypeUnion = false ;
366
361
case $ type instanceof \ReflectionUnionType;
367
362
$ types = $ type ->getTypes ();
368
363
break ;
@@ -375,16 +370,30 @@ function _checkTypehint(callable $callback, \Throwable $reason): bool
375
370
return true ;
376
371
}
377
372
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.
380
373
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.
381
379
$ matches = ($ type ->isBuiltin () && \gettype ($ reason ) === $ type ->getName ())
382
380
|| (new \ReflectionClass ($ type ->getName ()))->isInstance ($ reason );
383
381
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
384
385
if ($ matches ) {
385
- return true ;
386
+ if ($ isTypeUnion ) {
387
+ return true ;
388
+ }
389
+ } else {
390
+ if (!$ isTypeUnion ) {
391
+ return false ;
392
+ }
386
393
}
387
394
}
388
395
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 ;
390
399
}
0 commit comments