Skip to content

Commit b233f5f

Browse files
authored
Merge pull request #198 from SimonFrings/types
Support union types and address deprecation of `ReflectionType::getClass()` (PHP 8+ / Promise v2)
2 parents a9752a8 + 68b0670 commit b233f5f

File tree

6 files changed

+149
-50
lines changed

6 files changed

+149
-50
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ jobs:
1111
strategy:
1212
matrix:
1313
php:
14+
- 8.0
1415
- 7.4
1516
- 7.3
1617
- 7.2

src/functions.php

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

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

346-
if (!$expectedException->getClass()) {
347-
return true;
348-
}
347+
if (!$expectedException->getClass()) {
348+
return true;
349+
}
350+
351+
return $expectedException->getClass()->isInstance($object);
352+
} else {
353+
$type = $parameters[0]->getType();
349354

350-
return $expectedException->getClass()->isInstance($object);
355+
if (!$type) {
356+
return true;
357+
}
358+
359+
$types = [$type];
360+
361+
if ($type instanceof \ReflectionUnionType) {
362+
$types = $type->getTypes();
363+
}
364+
365+
$mismatched = false;
366+
367+
foreach ($types as $type) {
368+
if (!$type || $type->isBuiltin()) {
369+
continue;
370+
}
371+
372+
$expectedClass = $type->getName();
373+
374+
if ($object instanceof $expectedClass) {
375+
return true;
376+
}
377+
378+
$mismatched = true;
379+
}
380+
381+
return !$mismatched;
382+
}
351383
}

tests/FunctionCheckTypehintTest.php

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,65 @@ public function shouldAcceptFunctionStringCallbackWithTypehint()
2323
/** @test */
2424
public function shouldAcceptInvokableObjectCallbackWithTypehint()
2525
{
26-
$this->assertTrue(_checkTypehint(new TestCallbackWithTypehintClass(), new \InvalidArgumentException()));
27-
$this->assertfalse(_checkTypehint(new TestCallbackWithTypehintClass(), new \Exception()));
26+
$this->assertTrue(_checkTypehint(new CallbackWithTypehintClass(), new \InvalidArgumentException()));
27+
$this->assertfalse(_checkTypehint(new CallbackWithTypehintClass(), new \Exception()));
2828
}
2929

3030
/** @test */
3131
public function shouldAcceptObjectMethodCallbackWithTypehint()
3232
{
33-
$this->assertTrue(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new \InvalidArgumentException()));
34-
$this->assertfalse(_checkTypehint([new TestCallbackWithTypehintClass(), 'testCallback'], new \Exception()));
33+
$this->assertTrue(_checkTypehint([new CallbackWithTypehintClass(), 'testCallback'], new \InvalidArgumentException()));
34+
$this->assertfalse(_checkTypehint([new CallbackWithTypehintClass(), 'testCallback'], new \Exception()));
3535
}
3636

3737
/** @test */
3838
public function shouldAcceptStaticClassCallbackWithTypehint()
3939
{
40-
$this->assertTrue(_checkTypehint(['React\Promise\TestCallbackWithTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException()));
41-
$this->assertfalse(_checkTypehint(['React\Promise\TestCallbackWithTypehintClass', 'testCallbackStatic'], new \Exception()));
40+
$this->assertTrue(_checkTypehint([new CallbackWithTypehintClass(), 'testCallbackStatic'], new \InvalidArgumentException()));
41+
$this->assertfalse(_checkTypehint([new CallbackWithTypehintClass(), 'testCallbackStatic'], new \Exception()));
42+
}
43+
44+
/**
45+
* @test
46+
* @requires PHP 8
47+
*/
48+
public function shouldAcceptClosureCallbackWithUnionTypehint()
49+
{
50+
eval(
51+
'namespace React\Promise;' .
52+
'self::assertTrue(_checkTypehint(function (\RuntimeException|\InvalidArgumentException $e) {}, new \InvalidArgumentException()));' .
53+
'self::assertFalse(_checkTypehint(function (\RuntimeException|\InvalidArgumentException $e) {}, new \Exception()));'
54+
);
55+
}
56+
57+
/**
58+
* @test
59+
* @requires PHP 8
60+
*/
61+
public function shouldAcceptInvokableObjectCallbackWithUnionTypehint()
62+
{
63+
self::assertTrue(_checkTypehint(new CallbackWithUnionTypehintClass(), new \InvalidArgumentException()));
64+
self::assertFalse(_checkTypehint(new CallbackWithUnionTypehintClass(), new \Exception()));
65+
}
66+
67+
/**
68+
* @test
69+
* @requires PHP 8
70+
*/
71+
public function shouldAcceptObjectMethodCallbackWithUnionTypehint()
72+
{
73+
self::assertTrue(_checkTypehint([new CallbackWithUnionTypehintClass(), 'testCallback'], new \InvalidArgumentException()));
74+
self::assertFalse(_checkTypehint([new CallbackWithUnionTypehintClass(), 'testCallback'], new \Exception()));
75+
}
76+
77+
/**
78+
* @test
79+
* @requires PHP 8
80+
*/
81+
public function shouldAcceptStaticClassCallbackWithUnionTypehint()
82+
{
83+
self::assertTrue(_checkTypehint(['React\Promise\CallbackWithUnionTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException()));
84+
self::assertFalse(_checkTypehint(['React\Promise\CallbackWithUnionTypehintClass', 'testCallbackStatic'], new \Exception()));
4285
}
4386

4487
/** @test */
@@ -57,19 +100,19 @@ public function shouldAcceptFunctionStringCallbackWithoutTypehint()
57100
/** @test */
58101
public function shouldAcceptInvokableObjectCallbackWithoutTypehint()
59102
{
60-
$this->assertTrue(_checkTypehint(new TestCallbackWithoutTypehintClass(), new \InvalidArgumentException()));
103+
$this->assertTrue(_checkTypehint(new CallbackWithoutTypehintClass(), new \InvalidArgumentException()));
61104
}
62105

63106
/** @test */
64107
public function shouldAcceptObjectMethodCallbackWithoutTypehint()
65108
{
66-
$this->assertTrue(_checkTypehint([new TestCallbackWithoutTypehintClass(), 'testCallback'], new \InvalidArgumentException()));
109+
$this->assertTrue(_checkTypehint([new CallbackWithoutTypehintClass(), 'testCallback'], new \InvalidArgumentException()));
67110
}
68111

69112
/** @test */
70113
public function shouldAcceptStaticClassCallbackWithoutTypehint()
71114
{
72-
$this->assertTrue(_checkTypehint(['React\Promise\TestCallbackWithoutTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException()));
115+
$this->assertTrue(_checkTypehint(['React\Promise\CallbackWithoutTypehintClass', 'testCallbackStatic'], new \InvalidArgumentException()));
73116
}
74117
}
75118

@@ -80,39 +123,3 @@ function testCallbackWithTypehint(\InvalidArgumentException $e)
80123
function testCallbackWithoutTypehint()
81124
{
82125
}
83-
84-
class TestCallbackWithTypehintClass
85-
{
86-
public function __invoke(\InvalidArgumentException $e)
87-
{
88-
89-
}
90-
91-
public function testCallback(\InvalidArgumentException $e)
92-
{
93-
94-
}
95-
96-
public static function testCallbackStatic(\InvalidArgumentException $e)
97-
{
98-
99-
}
100-
}
101-
102-
class TestCallbackWithoutTypehintClass
103-
{
104-
public function __invoke()
105-
{
106-
107-
}
108-
109-
public function testCallback()
110-
{
111-
112-
}
113-
114-
public static function testCallbackStatic()
115-
{
116-
117-
}
118-
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace React\Promise;
4+
5+
use InvalidArgumentException;
6+
7+
class CallbackWithTypehintClass
8+
{
9+
public function __invoke(InvalidArgumentException $e)
10+
{
11+
}
12+
13+
public function testCallback(InvalidArgumentException $e)
14+
{
15+
}
16+
17+
public static function testCallbackStatic(InvalidArgumentException $e)
18+
{
19+
}
20+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace React\Promise;
4+
5+
use InvalidArgumentException;
6+
use RuntimeException;
7+
8+
class CallbackWithUnionTypehintClass
9+
{
10+
public function __invoke(RuntimeException|InvalidArgumentException $e)
11+
{
12+
}
13+
14+
public function testCallback(RuntimeException|InvalidArgumentException $e)
15+
{
16+
}
17+
18+
public static function testCallbackStatic(RuntimeException|InvalidArgumentException $e)
19+
{
20+
}
21+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace React\Promise;
4+
5+
class CallbackWithoutTypehintClass
6+
{
7+
public function __invoke()
8+
{
9+
}
10+
11+
public function testCallback()
12+
{
13+
}
14+
15+
public static function testCallbackStatic()
16+
{
17+
}
18+
}

0 commit comments

Comments
 (0)