Skip to content

Commit 7da8a6b

Browse files
authored
Merge pull request #304 from clue-labs/errno-pcntl
Improve errno detection for failed connections without ext-sockets
2 parents 936546b + cdc7a31 commit 7da8a6b

20 files changed

+65
-46
lines changed

src/Connector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ public function connect($uri)
170170
if (!isset($this->connectors[$scheme])) {
171171
return \React\Promise\reject(new \RuntimeException(
172172
'No connector available for URI scheme "' . $scheme . '" (EINVAL)',
173-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
173+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
174174
));
175175
}
176176

src/DnsConnector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function connect($uri)
3333
if (!$parts || !isset($parts['host'])) {
3434
return Promise\reject(new \InvalidArgumentException(
3535
'Given URI "' . $original . '" is invalid (EINVAL)',
36-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
36+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
3737
));
3838
}
3939

src/FdServer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public function __construct($fd, LoopInterface $loop = null)
8383
if (!\is_int($fd) || $fd < 0 || $fd >= \PHP_INT_MAX) {
8484
throw new \InvalidArgumentException(
8585
'Invalid FD number given (EINVAL)',
86-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
86+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
8787
);
8888
}
8989

src/HappyEyeBallsConnector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function connect($uri)
4545
if (!$parts || !isset($parts['host'])) {
4646
return Promise\reject(new \InvalidArgumentException(
4747
'Given URI "' . $original . '" is invalid (EINVAL)',
48-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
48+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
4949
));
5050
}
5151

src/SecureConnector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public function connect($uri)
3636
if (!$parts || !isset($parts['scheme']) || $parts['scheme'] !== 'tls') {
3737
return Promise\reject(new \InvalidArgumentException(
3838
'Given URI "' . $uri . '" is invalid (EINVAL)',
39-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
39+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
4040
));
4141
}
4242

src/SocketServer.php

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function __construct($uri, array $context = array(), LoopInterface $loop
5454
if (preg_match('#^(?:\w+://)?\d+$#', $uri)) {
5555
throw new \InvalidArgumentException(
5656
'Invalid URI given (EINVAL)',
57-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
57+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
5858
);
5959
}
6060

@@ -135,25 +135,42 @@ public static function accept($socket)
135135
* The errno and errstr values describes the type of error that has been
136136
* encountered. This method tries to look up the given errstr and find a
137137
* matching errno value which can be useful to provide more context to error
138-
* messages. It goes through the list of known errno constants when
139-
* ext-sockets is available to find an errno matching the given errstr.
138+
* messages. It goes through the list of known errno constants when either
139+
* `ext-sockets`, `ext-posix` or `ext-pcntl` is available to find an errno
140+
* matching the given errstr.
140141
*
141142
* @param string $errstr
142143
* @return int errno value (e.g. value of `SOCKET_ECONNREFUSED`) or 0 if not found
143144
* @internal
144-
* @copyright Copyright (c) 2018 Christian Lück, taken from https://github.com/clue/errno with permission
145+
* @copyright Copyright (c) 2023 Christian Lück, taken from https://github.com/clue/errno with permission
145146
* @codeCoverageIgnore
146147
*/
147148
public static function errno($errstr)
148149
{
149-
if (\function_exists('socket_strerror')) {
150+
// PHP defines the required `strerror()` function through either `ext-sockets`, `ext-posix` or `ext-pcntl`
151+
$strerror = \function_exists('socket_strerror') ? 'socket_strerror' : (\function_exists('posix_strerror') ? 'posix_strerror' : (\function_exists('pcntl_strerror') ? 'pcntl_strerror' : null));
152+
if ($strerror !== null) {
153+
assert(\is_string($strerror) && \is_callable($strerror));
154+
155+
// PHP defines most useful errno constants like `ECONNREFUSED` through constants in `ext-sockets` like `SOCKET_ECONNREFUSED`
156+
// PHP also defines a hand full of errno constants like `EMFILE` through constants in `ext-pcntl` like `PCNTL_EMFILE`
157+
// go through list of all defined constants like `SOCKET_E*` and `PCNTL_E*` and see if they match the given `$errstr`
150158
foreach (\get_defined_constants(false) as $name => $value) {
151-
if (\strpos($name, 'SOCKET_E') === 0 && \socket_strerror($value) === $errstr) {
159+
if (\is_int($value) && (\strpos($name, 'SOCKET_E') === 0 || \strpos($name, 'PCNTL_E') === 0) && $strerror($value) === $errstr) {
152160
return $value;
153161
}
154162
}
163+
164+
// if we reach this, no matching errno constant could be found (unlikely when `ext-sockets` is available)
165+
// go through list of all possible errno values from 1 to `MAX_ERRNO` and see if they match the given `$errstr`
166+
for ($errno = 1, $max = \defined('MAX_ERRNO') ? \MAX_ERRNO : 4095; $errno <= $max; ++$errno) {
167+
if ($strerror($errno) === $errstr) {
168+
return $errno;
169+
}
170+
}
155171
}
156172

173+
// if we reach this, no matching errno value could be found (unlikely when either `ext-sockets`, `ext-posix` or `ext-pcntl` is available)
157174
return 0;
158175
}
159176

@@ -164,8 +181,8 @@ public static function errno($errstr)
164181
* This method tries to look up the given errno value and find a matching
165182
* errno constant name which can be useful to provide more context and more
166183
* descriptive error messages. It goes through the list of known errno
167-
* constants when ext-sockets is available to find the matching errno
168-
* constant name.
184+
* constants when either `ext-sockets` or `ext-pcntl` is available to find
185+
* the matching errno constant name.
169186
*
170187
* Because this method is used to append more context to error messages, the
171188
* constant name will be prefixed with a space and put between parenthesis
@@ -174,19 +191,21 @@ public static function errno($errstr)
174191
* @param int $errno
175192
* @return string e.g. ` (ECONNREFUSED)` or empty string if no matching const for the given errno could be found
176193
* @internal
177-
* @copyright Copyright (c) 2018 Christian Lück, taken from https://github.com/clue/errno with permission
194+
* @copyright Copyright (c) 2023 Christian Lück, taken from https://github.com/clue/errno with permission
178195
* @codeCoverageIgnore
179196
*/
180197
public static function errconst($errno)
181198
{
182-
if (\function_exists('socket_strerror')) {
183-
foreach (\get_defined_constants(false) as $name => $value) {
184-
if ($value === $errno && \strpos($name, 'SOCKET_E') === 0) {
185-
return ' (' . \substr($name, 7) . ')';
186-
}
199+
// PHP defines most useful errno constants like `ECONNREFUSED` through constants in `ext-sockets` like `SOCKET_ECONNREFUSED`
200+
// PHP also defines a hand full of errno constants like `EMFILE` through constants in `ext-pcntl` like `PCNTL_EMFILE`
201+
// go through list of all defined constants like `SOCKET_E*` and `PCNTL_E*` and see if they match the given `$errno`
202+
foreach (\get_defined_constants(false) as $name => $value) {
203+
if ($value === $errno && (\strpos($name, 'SOCKET_E') === 0 || \strpos($name, 'PCNTL_E') === 0)) {
204+
return ' (' . \substr($name, \strpos($name, '_') + 1) . ')';
187205
}
188206
}
189207

208+
// if we reach this, no matching errno constant could be found (unlikely when `ext-sockets` is available)
190209
return '';
191210
}
192211
}

src/TcpConnector.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ public function connect($uri)
2929
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
3030
return Promise\reject(new \InvalidArgumentException(
3131
'Given URI "' . $uri . '" is invalid (EINVAL)',
32-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
32+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
3333
));
3434
}
3535

3636
$ip = \trim($parts['host'], '[]');
3737
if (@\inet_pton($ip) === false) {
3838
return Promise\reject(new \InvalidArgumentException(
3939
'Given URI "' . $uri . '" does not contain a valid host IP (EINVAL)',
40-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
40+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
4141
));
4242
}
4343

src/TcpServer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,14 @@ public function __construct($uri, LoopInterface $loop = null, array $context = a
156156
if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') {
157157
throw new \InvalidArgumentException(
158158
'Invalid URI "' . $uri . '" given (EINVAL)',
159-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
159+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
160160
);
161161
}
162162

163163
if (@\inet_pton(\trim($parts['host'], '[]')) === false) {
164164
throw new \InvalidArgumentException(
165165
'Given URI "' . $uri . '" does not contain a valid host IP (EINVAL)',
166-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
166+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
167167
);
168168
}
169169

src/UnixConnector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function connect($path)
3030
} elseif (\substr($path, 0, 7) !== 'unix://') {
3131
return Promise\reject(new \InvalidArgumentException(
3232
'Given URI "' . $path . '" is invalid (EINVAL)',
33-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
33+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
3434
));
3535
}
3636

src/UnixServer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function __construct($path, LoopInterface $loop = null, array $context =
5959
} elseif (\substr($path, 0, 7) !== 'unix://') {
6060
throw new \InvalidArgumentException(
6161
'Given URI "' . $path . '" is invalid (EINVAL)',
62-
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
62+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22)
6363
);
6464
}
6565

0 commit comments

Comments
 (0)