Skip to content

Commit c9bad9b

Browse files
committed
[1.x] Improve PHP 8.4+ support by avoiding implicitly nullable types
This changeset backports reactphp#223 from `3.x` to `1.x` to improve PHP 8.4+ support by avoiding implicitly nullable types as discussed in reactphp/promise#260. The same idea applies, but v1 requires manual type checks to support legacy PHP versions as the nullable type syntax requires PHP 7.1+ otherwise. Builds on top of reactphp#223, reactphp#204 and reactphp#218
1 parent c134600 commit c9bad9b

8 files changed

+82
-5
lines changed

src/Query/TcpTransportExecutor.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ class TcpTransportExecutor implements ExecutorInterface
134134
* @param string $nameserver
135135
* @param ?LoopInterface $loop
136136
*/
137-
public function __construct($nameserver, LoopInterface $loop = null)
137+
public function __construct($nameserver, $loop = null)
138138
{
139139
if (\strpos($nameserver, '[') === false && \substr_count($nameserver, ':') >= 2 && \strpos($nameserver, '://') === false) {
140140
// several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets
@@ -146,6 +146,10 @@ public function __construct($nameserver, LoopInterface $loop = null)
146146
throw new \InvalidArgumentException('Invalid nameserver address given');
147147
}
148148

149+
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
150+
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
151+
}
152+
149153
$this->nameserver = 'tcp://' . $parts['host'] . ':' . (isset($parts['port']) ? $parts['port'] : 53);
150154
$this->loop = $loop ?: Loop::get();
151155
$this->parser = new Parser();

src/Query/TimeoutExecutor.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,15 @@ final class TimeoutExecutor implements ExecutorInterface
1212
private $loop;
1313
private $timeout;
1414

15-
public function __construct(ExecutorInterface $executor, $timeout, LoopInterface $loop = null)
15+
/**
16+
* @param ?LoopInterface $loop
17+
*/
18+
public function __construct(ExecutorInterface $executor, $timeout, $loop = null)
1619
{
20+
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
21+
throw new \InvalidArgumentException('Argument #3 ($loop) expected null|React\EventLoop\LoopInterface');
22+
}
23+
1724
$this->executor = $executor;
1825
$this->loop = $loop ?: Loop::get();
1926
$this->timeout = $timeout;

src/Query/UdpTransportExecutor.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ final class UdpTransportExecutor implements ExecutorInterface
9898
* @param string $nameserver
9999
* @param ?LoopInterface $loop
100100
*/
101-
public function __construct($nameserver, LoopInterface $loop = null)
101+
public function __construct($nameserver, $loop = null)
102102
{
103103
if (\strpos($nameserver, '[') === false && \substr_count($nameserver, ':') >= 2 && \strpos($nameserver, '://') === false) {
104104
// several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets
@@ -110,6 +110,10 @@ public function __construct($nameserver, LoopInterface $loop = null)
110110
throw new \InvalidArgumentException('Invalid nameserver address given');
111111
}
112112

113+
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
114+
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
115+
}
116+
113117
$this->nameserver = 'udp://' . $parts['host'] . ':' . (isset($parts['port']) ? $parts['port'] : 53);
114118
$this->loop = $loop ?: Loop::get();
115119
$this->parser = new Parser();

src/Resolver/Factory.php

+14-2
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ final class Factory
3636
* @throws \InvalidArgumentException for invalid DNS server address
3737
* @throws \UnderflowException when given DNS Config object has an empty list of nameservers
3838
*/
39-
public function create($config, LoopInterface $loop = null)
39+
public function create($config, $loop = null)
4040
{
41+
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
42+
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
43+
}
44+
4145
$executor = $this->decorateHostsFileExecutor($this->createExecutor($config, $loop ?: Loop::get()));
4246

4347
return new Resolver($executor);
@@ -59,8 +63,16 @@ public function create($config, LoopInterface $loop = null)
5963
* @throws \InvalidArgumentException for invalid DNS server address
6064
* @throws \UnderflowException when given DNS Config object has an empty list of nameservers
6165
*/
62-
public function createCached($config, LoopInterface $loop = null, CacheInterface $cache = null)
66+
public function createCached($config, $loop = null, $cache = null)
6367
{
68+
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
69+
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
70+
}
71+
72+
if ($cache !== null && !$cache instanceof CacheInterface) { // manual type check to support legacy PHP < 7.1
73+
throw new \InvalidArgumentException('Argument #3 ($cache) expected null|React\Cache\CacheInterface');
74+
}
75+
6476
// default to keeping maximum of 256 responses in cache unless explicitly given
6577
if (!($cache instanceof CacheInterface)) {
6678
$cache = new ArrayCache(256);

tests/Query/TcpTransportExecutorTest.php

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use React\Dns\Protocol\Parser;
88
use React\Dns\Query\Query;
99
use React\Dns\Query\TcpTransportExecutor;
10+
use React\Dns\Query\UdpTransportExecutor;
1011
use React\EventLoop\Loop;
1112
use React\Tests\Dns\TestCase;
1213

@@ -943,4 +944,11 @@ public function testQueryAgainAfterPreviousQueryResolvedWillReuseSocketAndCancel
943944
// trigger second query
944945
$executor->query($query);
945946
}
947+
948+
/** @test */
949+
public function constructorThrowsExceptionForInvalidLoop()
950+
{
951+
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
952+
new TcpTransportExecutor('127.0.0.1:0', 'loop');
953+
}
946954
}

tests/Query/TimeoutExecutorTest.php

+8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use React\Dns\Model\Message;
66
use React\Dns\Query\CancellationException;
77
use React\Dns\Query\Query;
8+
use React\Dns\Query\TcpTransportExecutor;
89
use React\Dns\Query\TimeoutException;
910
use React\Dns\Query\TimeoutExecutor;
1011
use React\Promise;
@@ -185,4 +186,11 @@ public function testRejectsPromiseAndCancelsPendingQueryWhenTimeoutTriggers()
185186
$this->assertInstanceOf('React\Dns\Query\TimeoutException', $exception);
186187
$this->assertEquals('DNS query for igor.io (A) timed out' , $exception->getMessage());
187188
}
189+
190+
/** @test */
191+
public function constructorThrowsExceptionForInvalidLoop()
192+
{
193+
$this->setExpectedException('InvalidArgumentException', 'Argument #3 ($loop) expected null|React\EventLoop\LoopInterface');
194+
new TimeoutExecutor($this->executor, 5.0, 'loop');
195+
}
188196
}

tests/Query/UdpTransportExecutorTest.php

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use React\Dns\Protocol\Parser;
88
use React\Dns\Query\Query;
99
use React\Dns\Query\UdpTransportExecutor;
10+
use React\Dns\Resolver\Factory;
1011
use React\EventLoop\Loop;
1112
use React\Tests\Dns\TestCase;
1213

@@ -375,4 +376,11 @@ public function testQueryResolvesIfServerSendsValidResponse()
375376

376377
$this->assertInstanceOf('React\Dns\Model\Message', $response);
377378
}
379+
380+
/** @test */
381+
public function constructorThrowsExceptionForInvalidLoop()
382+
{
383+
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
384+
new UdpTransportExecutor('127.0.0.1:0', 'loop');
385+
}
378386
}

tests/Resolver/FactoryTest.php

+26
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,32 @@ public function createCachedShouldCreateResolverWithCachingExecutorWithCustomCac
399399
$this->assertSame($cache, $cacheProperty);
400400
}
401401

402+
/** @test */
403+
public function createThrowsExceptionForInvalidLoop()
404+
{
405+
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
406+
$factory = new Factory();
407+
$factory->create('config', 'loop');
408+
}
409+
410+
/** @test */
411+
public function createCachedThrowsExceptionForInvalidLoop()
412+
{
413+
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
414+
$factory = new Factory();
415+
$factory->createCached('config', 'loop');
416+
}
417+
418+
/** @test */
419+
public function createCachedThrowsExceptionForInvalidCache()
420+
{
421+
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
422+
423+
$this->setExpectedException('InvalidArgumentException', 'Argument #3 ($cache) expected null|React\Cache\CacheInterface');
424+
$factory = new Factory();
425+
$factory->createCached('config', $loop, 'cache');
426+
}
427+
402428
private function getResolverPrivateExecutor($resolver)
403429
{
404430
$executor = $this->getResolverPrivateMemberValue($resolver, 'executor');

0 commit comments

Comments
 (0)