Skip to content

Commit

Permalink
Merge pull request #318 from clue-labs/nullable-v1
Browse files Browse the repository at this point in the history
[1.x] Improve PHP 8.4+ support by avoiding implicitly nullable types
  • Loading branch information
WyriHaximus authored Jul 25, 2024
2 parents 216d3ae + dbf58dc commit 038f639
Show file tree
Hide file tree
Showing 23 changed files with 161 additions and 23 deletions.
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@
"require": {
"php": ">=5.3.0",
"evenement/evenement": "^3.0 || ^2.0 || ^1.0",
"react/dns": "^1.11",
"react/dns": "^1.13",
"react/event-loop": "^1.2",
"react/promise": "^3 || ^2.6 || ^1.2.1",
"react/stream": "^1.2"
"react/promise": "^3.2 || ^2.6 || ^1.2.1",
"react/stream": "^1.4"
},
"require-dev": {
"phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36",
"react/async": "^4 || ^3 || ^2",
"react/async": "^4.3 || ^3.3 || ^2",
"react/promise-stream": "^1.4",
"react/promise-timer": "^1.10"
"react/promise-timer": "^1.11"
},
"autoload": {
"psr-4": {
Expand Down
6 changes: 5 additions & 1 deletion src/FdServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ final class FdServer extends EventEmitter implements ServerInterface
* @throws \InvalidArgumentException if the listening address is invalid
* @throws \RuntimeException if listening on this address fails (already in use etc.)
*/
public function __construct($fd, LoopInterface $loop = null)
public function __construct($fd, $loop = null)
{
if (\preg_match('#^php://fd/(\d+)$#', $fd, $m)) {
$fd = (int) $m[1];
Expand All @@ -87,6 +87,10 @@ public function __construct($fd, LoopInterface $loop = null)
);
}

if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}

$this->loop = $loop ?: Loop::get();

$errno = 0;
Expand Down
17 changes: 14 additions & 3 deletions src/HappyEyeBallsConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,26 @@ final class HappyEyeBallsConnector implements ConnectorInterface
private $connector;
private $resolver;

public function __construct(LoopInterface $loop = null, ConnectorInterface $connector = null, ResolverInterface $resolver = null)
/**
* @param ?LoopInterface $loop
* @param ConnectorInterface $connector
* @param ResolverInterface $resolver
*/
public function __construct($loop = null, $connector = null, $resolver = null)
{
// $connector and $resolver arguments are actually required, marked
// optional for technical reasons only. Nullable $loop without default
// requires PHP 7.1, null default is also supported in legacy PHP
// versions, but required parameters are not allowed after arguments
// with null default. Mark all parameters optional and check accordingly.
if ($connector === null || $resolver === null) {
throw new \InvalidArgumentException('Missing required $connector or $resolver argument');
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
}
if (!$connector instanceof ConnectorInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($connector) expected React\Socket\ConnectorInterface');
}
if (!$resolver instanceof ResolverInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #3 ($resolver) expected React\Dns\Resolver\ResolverInterface');
}

$this->loop = $loop ?: Loop::get();
Expand Down
11 changes: 10 additions & 1 deletion src/SecureConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ final class SecureConnector implements ConnectorInterface
private $streamEncryption;
private $context;

public function __construct(ConnectorInterface $connector, LoopInterface $loop = null, array $context = array())
/**
* @param ConnectorInterface $connector
* @param ?LoopInterface $loop
* @param array $context
*/
public function __construct(ConnectorInterface $connector, $loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}

$this->connector = $connector;
$this->streamEncryption = new StreamEncryption($loop ?: Loop::get(), false);
$this->context = $context;
Expand Down
6 changes: 5 additions & 1 deletion src/SecureServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,12 @@ final class SecureServer extends EventEmitter implements ServerInterface
* @see TcpServer
* @link https://www.php.net/manual/en/context.ssl.php for TLS context options
*/
public function __construct(ServerInterface $tcp, LoopInterface $loop = null, array $context = array())
public function __construct(ServerInterface $tcp, $loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}

if (!\function_exists('stream_socket_enable_crypto')) {
throw new \BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)'); // @codeCoverageIgnore
}
Expand Down
12 changes: 8 additions & 4 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,18 @@ final class Server extends EventEmitter implements ServerInterface
* For BC reasons, you can also pass the TCP socket context options as a simple
* array without wrapping this in another array under the `tcp` key.
*
* @param string|int $uri
* @param LoopInterface $loop
* @param array $context
* @param string|int $uri
* @param ?LoopInterface $loop
* @param array $context
* @deprecated 1.9.0 See `SocketServer` instead
* @see SocketServer
*/
public function __construct($uri, LoopInterface $loop = null, array $context = array())
public function __construct($uri, $loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}

$loop = $loop ?: Loop::get();

// sanitize TCP context options if not properly wrapped
Expand Down
6 changes: 5 additions & 1 deletion src/SocketServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ final class SocketServer extends EventEmitter implements ServerInterface
* @throws \InvalidArgumentException if the listening address is invalid
* @throws \RuntimeException if listening on this address fails (already in use etc.)
*/
public function __construct($uri, array $context = array(), LoopInterface $loop = null)
public function __construct($uri, array $context = array(), $loop = null)
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #3 ($loop) expected null|React\EventLoop\LoopInterface');
}

// apply default options if not explicitly given
$context += array(
'tcp' => array(),
Expand Down
10 changes: 9 additions & 1 deletion src/TcpConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@ final class TcpConnector implements ConnectorInterface
private $loop;
private $context;

public function __construct(LoopInterface $loop = null, array $context = array())
/**
* @param ?LoopInterface $loop
* @param array $context
*/
public function __construct($loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
}

$this->loop = $loop ?: Loop::get();
$this->context = $context;
}
Expand Down
6 changes: 5 additions & 1 deletion src/TcpServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,12 @@ final class TcpServer extends EventEmitter implements ServerInterface
* @throws InvalidArgumentException if the listening address is invalid
* @throws RuntimeException if listening on this address fails (already in use etc.)
*/
public function __construct($uri, LoopInterface $loop = null, array $context = array())
public function __construct($uri, $loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}

$this->loop = $loop ?: Loop::get();

// a single port has been given => assume localhost
Expand Down
11 changes: 10 additions & 1 deletion src/TimeoutConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,17 @@ final class TimeoutConnector implements ConnectorInterface
private $timeout;
private $loop;

public function __construct(ConnectorInterface $connector, $timeout, LoopInterface $loop = null)
/**
* @param ConnectorInterface $connector
* @param float $timeout
* @param ?LoopInterface $loop
*/
public function __construct(ConnectorInterface $connector, $timeout, $loop = null)
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #3 ($loop) expected null|React\EventLoop\LoopInterface');
}

$this->connector = $connector;
$this->timeout = $timeout;
$this->loop = $loop ?: Loop::get();
Expand Down
9 changes: 8 additions & 1 deletion src/UnixConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,15 @@ final class UnixConnector implements ConnectorInterface
{
private $loop;

public function __construct(LoopInterface $loop = null)
/**
* @param ?LoopInterface $loop
*/
public function __construct($loop = null)
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
}

$this->loop = $loop ?: Loop::get();
}

Expand Down
6 changes: 5 additions & 1 deletion src/UnixServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@ final class UnixServer extends EventEmitter implements ServerInterface
* @throws InvalidArgumentException if the listening address is invalid
* @throws RuntimeException if listening on this address fails (already in use etc.)
*/
public function __construct($path, LoopInterface $loop = null, array $context = array())
public function __construct($path, $loop = null, array $context = array())
{
if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1
throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
}

$this->loop = $loop ?: Loop::get();

if (\strpos($path, '://') === false) {
Expand Down
6 changes: 6 additions & 0 deletions tests/FdServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ public function testCtorThrowsForInvalidUrl()
new FdServer('tcp://127.0.0.1:8080', $loop);
}

public function testCtorThrowsForInvalidLoop()
{
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
new FdServer(0, 'loop');
}

public function testCtorThrowsForUnknownFdWithoutCallingCustomErrorHandler()
{
if (!is_dir('/dev/fd') || defined('HHVM_VERSION')) {
Expand Down
10 changes: 8 additions & 2 deletions tests/HappyEyeBallsConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,21 @@ public function testConstructWithoutLoopAssignsLoopAutomatically()
$this->assertInstanceOf('React\EventLoop\LoopInterface', $loop);
}

public function testConstructWithInvalidLoopThrows()
{
$this->setExpectedException('InvalidArgumentException', 'Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
new HappyEyeBallsConnector('loop', $this->tcp, $this->resolver);
}

public function testConstructWithoutRequiredConnectorThrows()
{
$this->setExpectedException('InvalidArgumentException');
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($connector) expected React\Socket\ConnectorInterface');
new HappyEyeBallsConnector(null, null, $this->resolver);
}

public function testConstructWithoutRequiredResolverThrows()
{
$this->setExpectedException('InvalidArgumentException');
$this->setExpectedException('InvalidArgumentException', 'Argument #3 ($resolver) expected React\Dns\Resolver\ResolverInterface');
new HappyEyeBallsConnector(null, $this->tcp);
}

Expand Down
6 changes: 6 additions & 0 deletions tests/SecureConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public function setUpConnector()
$this->connector = new SecureConnector($this->tcp, $this->loop);
}

public function testCtorThrowsForInvalidLoop()
{
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
new SecureConnector($this->tcp, 'loop');
}

public function testConstructWithoutLoopAssignsLoopAutomatically()
{
$connector = new SecureConnector($this->tcp);
Expand Down
8 changes: 8 additions & 0 deletions tests/SecureServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ public function setUpSkipTest()
}
}

public function testCtorThrowsForInvalidLoop()
{
$tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();

$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
new SecureServer($tcp, 'loop');
}

public function testConstructWithoutLoopAssignsLoopAutomatically()
{
$tcp = $this->getMockBuilder('React\Socket\ServerInterface')->getMock();
Expand Down
6 changes: 6 additions & 0 deletions tests/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ public function testConstructorThrowsForInvalidUri()
$server = new Server('invalid URI', $loop);
}

public function testCtorThrowsForInvalidLoop()
{
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
new Server('127.0.0.1:0', 'loop');
}

public function testConstructorCreatesExpectedTcpServer()
{
$server = new Server(0);
Expand Down
6 changes: 6 additions & 0 deletions tests/SocketServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ public function testConstructorWithInvalidUriWithSchemaAndPortOnlyThrows()
new SocketServer('tcp://0');
}

public function testCtorThrowsForInvalidLoop()
{
$this->setExpectedException('InvalidArgumentException', 'Argument #3 ($loop) expected null|React\EventLoop\LoopInterface');
new SocketServer('127.0.0.1:0', array(), 'loop');
}

public function testConstructorCreatesExpectedTcpServer()
{
$socket = new SocketServer('127.0.0.1:0', array());
Expand Down
6 changes: 6 additions & 0 deletions tests/TcpConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ class TcpConnectorTest extends TestCase
{
const TIMEOUT = 5.0;

public function testCtorThrowsForInvalidLoop()
{
$this->setExpectedException('InvalidArgumentException', 'Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
new TcpConnector('loop');
}

public function testConstructWithoutLoopAssignsLoopAutomatically()
{
$connector = new TcpConnector();
Expand Down
6 changes: 6 additions & 0 deletions tests/TcpServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public function setUpServer()
$this->port = parse_url($this->server->getAddress(), PHP_URL_PORT);
}

public function testCtorThrowsForInvalidLoop()
{
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
new TcpServer(0, 'loop');
}

public function testConstructWithoutLoopAssignsLoopAutomatically()
{
$server = new TcpServer(0);
Expand Down
8 changes: 8 additions & 0 deletions tests/TimeoutConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@

class TimeoutConnectorTest extends TestCase
{
public function testCtorThrowsForInvalidLoop()
{
$base = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();

$this->setExpectedException('InvalidArgumentException', 'Argument #3 ($loop) expected null|React\EventLoop\LoopInterface');
new TimeoutConnector($base, 0.001, 'loop');
}

public function testConstructWithoutLoopAssignsLoopAutomatically()
{
$base = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
Expand Down
6 changes: 6 additions & 0 deletions tests/UnixConnectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ public function setUpConnector()
$this->connector = new UnixConnector($this->loop);
}

public function testCtorThrowsForInvalidLoop()
{
$this->setExpectedException('InvalidArgumentException', 'Argument #1 ($loop) expected null|React\EventLoop\LoopInterface');
new UnixConnector('loop');
}

public function testConstructWithoutLoopAssignsLoopAutomatically()
{
$connector = new UnixConnector();
Expand Down
6 changes: 6 additions & 0 deletions tests/UnixServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ public function setUpServer()
$this->server = new UnixServer($this->uds);
}

public function testCtorThrowsForInvalidLoop()
{
$this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface');
new UnixServer($this->getRandomSocketUri(), 'loop');
}

public function testConstructWithoutLoopAssignsLoopAutomatically()
{
unlink(str_replace('unix://', '', $this->uds));
Expand Down

0 comments on commit 038f639

Please sign in to comment.