Skip to content

Commit bf8babc

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 bf8babc

8 files changed

+79
-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

+7
Original file line numberDiff line numberDiff line change
@@ -943,4 +943,11 @@ public function testQueryAgainAfterPreviousQueryResolvedWillReuseSocketAndCancel
943943
// trigger second query
944944
$executor->query($query);
945945
}
946+
947+
/** @test */
948+
public function constructorThrowsExceptionForInvalidLoop()
949+
{
950+
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
951+
new TcpTransportExecutor('127.0.0.1:0', 'loop');
952+
}
946953
}

tests/Query/TimeoutExecutorTest.php

+7
Original file line numberDiff line numberDiff line change
@@ -185,4 +185,11 @@ public function testRejectsPromiseAndCancelsPendingQueryWhenTimeoutTriggers()
185185
$this->assertInstanceOf('React\Dns\Query\TimeoutException', $exception);
186186
$this->assertEquals('DNS query for igor.io (A) timed out' , $exception->getMessage());
187187
}
188+
189+
/** @test */
190+
public function constructorThrowsExceptionForInvalidLoop()
191+
{
192+
$this->setExpectedException('InvalidArgumentException', 'Argument #3 ($loop) expected null|React\EventLoop\LoopInterface');
193+
new TimeoutExecutor($this->executor, 5.0, 'loop');
194+
}
188195
}

tests/Query/UdpTransportExecutorTest.php

+7
Original file line numberDiff line numberDiff line change
@@ -375,4 +375,11 @@ public function testQueryResolvesIfServerSendsValidResponse()
375375

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

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)