diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0ecf22..36cf6a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,7 @@ jobs: strategy: matrix: php: + - 8.4 - 8.3 - 8.2 - 8.1 diff --git a/composer.json b/composer.json index a71b956..d8e01f8 100644 --- a/composer.json +++ b/composer.json @@ -13,16 +13,16 @@ "require": { "php": ">=5.3", "clue/socks-react": "^1.5", - "react/child-process": "^0.6", + "react/child-process": "^0.6.6", "react/event-loop": "^1.2", - "react/promise": "^3 || ^2.1 || ^1.2.1", - "react/socket": "^1.12.0", - "react/stream": "^1.2" + "react/promise": "^3.2 || ^2.11 || ^1.3", + "react/socket": "^1.16", + "react/stream": "^1.4" }, "require-dev": { "phpunit/phpunit": "^9.6 || ^8.5 || ^5.7 || ^4.8.36", - "react/async": "^4 || ^3 || ^2", - "react/http": "^1.5", + "react/async": "^4.3 || ^3.2 || ^2.2", + "react/http": "^1.11", "react/mysql": "^0.5.5", "react/promise-timer": "^1.11" }, diff --git a/examples/31-mysql-ssh-tunnel.php b/examples/31-mysql-ssh-tunnel.php index 3e4a63e..acacd25 100644 --- a/examples/31-mysql-ssh-tunnel.php +++ b/examples/31-mysql-ssh-tunnel.php @@ -20,7 +20,13 @@ $proxy = new Clue\React\SshProxy\SshProcessConnector($url); $url = getenv('MYSQL_LOGIN') !== false ? getenv('MYSQL_LOGIN') : 'user:pass@localhost'; + +// create MySQL factory without raising deprecation notice on PHP 8.4+ +// need this hack until react/mysql v0.7.0 ships https://github.com/friends-of-reactphp/mysql/pull/203 +set_error_handler(function () { }, E_DEPRECATED); $factory = new React\MySQL\Factory(null, $proxy); +restore_error_handler(); + $client = $factory->createLazyConnection($url); $client->query('SELECT * FROM (SELECT "foo" UNION SELECT "bar") data')->then(function (React\MySQL\QueryResult $query) { diff --git a/src/SshProcessConnector.php b/src/SshProcessConnector.php index 603746a..995316f 100644 --- a/src/SshProcessConnector.php +++ b/src/SshProcessConnector.php @@ -41,7 +41,7 @@ class SshProcessConnector implements ConnectorInterface public function __construct( #[\SensitiveParameter] $uri, - LoopInterface $loop = null + $loop = null ) { // URI must use optional ssh:// scheme, must contain host and neither pass nor target must start with dash $parts = \parse_url((\strpos($uri, '://') === false ? 'ssh://' : '') . $uri); @@ -51,6 +51,10 @@ public function __construct( throw new \InvalidArgumentException('Invalid SSH server URI'); } + 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->cmd = 'exec '; if ($pass !== null) { $this->cmd .= 'sshpass -p ' . \escapeshellarg($pass) . ' '; diff --git a/src/SshSocksConnector.php b/src/SshSocksConnector.php index 969c835..a1da25b 100644 --- a/src/SshSocksConnector.php +++ b/src/SshSocksConnector.php @@ -52,7 +52,7 @@ class SshSocksConnector implements ConnectorInterface public function __construct( #[\SensitiveParameter] $uri, - LoopInterface $loop = null + $loop = null ) { // URI must use optional ssh:// scheme, must contain host and neither pass nor target must start with dash $parts = \parse_url((\strpos($uri, '://') === false ? 'ssh://' : '') . $uri); @@ -62,6 +62,10 @@ public function __construct( throw new \InvalidArgumentException('Invalid SSH server URI'); } + 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->cmd = 'exec '; if ($pass !== null) { $this->cmd .= 'sshpass -p ' . \escapeshellarg($pass) . ' '; diff --git a/tests/SshProcessConnectorTest.php b/tests/SshProcessConnectorTest.php index d3a56d2..ade2263 100644 --- a/tests/SshProcessConnectorTest.php +++ b/tests/SshProcessConnectorTest.php @@ -93,6 +93,12 @@ public function testConstructorThrowsForInvalidHost() new SshProcessConnector('-host', $loop); } + public function testConstructorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new SshProcessConnector('host', 'loop'); + } + /** * @doesNotPerformAssertions */ diff --git a/tests/SshSocksConnectorTest.php b/tests/SshSocksConnectorTest.php index 62bb548..7a4b502 100644 --- a/tests/SshSocksConnectorTest.php +++ b/tests/SshSocksConnectorTest.php @@ -123,6 +123,12 @@ public function testConstructorThrowsForInvalidBindHost() new SshSocksConnector('host?bind=example:1080', $loop); } + public function testConstructorThrowsForInvalidLoop() + { + $this->setExpectedException('InvalidArgumentException', 'Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); + new SshSocksConnector('host', 'loop'); + } + /** * @doesNotPerformAssertions */