Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.6.0RC3

- Streams:
. Fixed bug GH-23879 (stream_socket_accept() reports AcceptFailed when a
non-blocking listener has no pending connection). (Lazizbek Ergashev)

24 Sep 2026, PHP 8.6.0RC2

Expand Down
3 changes: 3 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES
zval* that is IS_UNDEF when the closure is unbound.
. object_properties_load() now verifies that the given value is assignable
to typed properties. The check is performed in strict mode.
. php_stream_xport_accept() now returns 0 with a NULL client when a
non-blocking listener has no pending connection. Callers must check the
client, not only the return value.

- Added:
. New zend_class_entry.ce_flags2 and zend_function.fn_flags2 fields were
Expand Down
4 changes: 4 additions & 0 deletions ext/openssl/xp_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -3556,6 +3556,10 @@ static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_
}
}
}
} else if (!sock->s.is_blocked && (xparam->outputs.error_code == PHP_TIMEOUT_ERROR_VALUE
|| PHP_IS_TRANSIENT_ERROR(xparam->outputs.error_code))) {
/* No pending connection is not an error for a non-blocking listener. */
return 0;
}

return xparam->outputs.client == NULL ? -1 : 0;
Expand Down
10 changes: 7 additions & 3 deletions ext/standard/streamsfuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,13 @@ PHP_FUNCTION(stream_socket_accept)

php_stream_error_operation_begin();

if (0 == php_stream_xport_accept(stream, &clistream,
int ret = php_stream_xport_accept(stream, &clistream,
zpeername ? &peername : NULL,
NULL, NULL,
tv_pointer, &errstr
) && clistream) {
);

if (0 == ret && clistream) {

if (peername) {
ZEND_TRY_ASSIGN_REF_STR(zpeername, peername);
Expand All @@ -328,7 +330,9 @@ PHP_FUNCTION(stream_socket_accept)
if (peername) {
zend_string_release(peername);
}
php_stream_warn(stream, AcceptFailed, "Accept failed: %s", errstr ? ZSTR_VAL(errstr) : "Unknown error");
if (0 != ret) {
php_stream_warn(stream, AcceptFailed, "Accept failed: %s", errstr ? ZSTR_VAL(errstr) : "Unknown error");
}
RETVAL_FALSE;
}

Expand Down
36 changes: 36 additions & 0 deletions ext/standard/tests/streams/gh23879.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
GH-23879 (stream_socket_accept() reports AcceptFailed when a non-blocking listener has no pending connection)
--FILE--
<?php
$server = stream_socket_server(
'tcp://127.0.0.1:0',
context: stream_context_create([
'stream' => [
'error_mode' => StreamErrorMode::Exception,
'error_store' => StreamErrorStore::All,
],
]),
);
stream_set_blocking($server, false);

var_dump(stream_socket_accept($server, 0));
var_dump(stream_last_errors());

$client = stream_socket_client('tcp://' . stream_socket_get_name($server, false));
var_dump(is_resource(stream_socket_accept($server, 1)));
var_dump(stream_socket_accept($server, 0));

stream_set_blocking($server, true);
try {
stream_socket_accept($server, 0);
} catch (StreamException $e) {
var_dump($e->getErrors()[0]->code);
}
?>
--EXPECT--
bool(false)
array(0) {
}
bool(true)
bool(false)
enum(StreamErrorCode::AcceptFailed)
2 changes: 0 additions & 2 deletions main/network.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@
# include "win32/time.h"
# define SOCK_ERR INVALID_SOCKET
# define SOCK_CONN_ERR SOCKET_ERROR
# define PHP_TIMEOUT_ERROR_VALUE WSAETIMEDOUT

#ifdef HAVE_IPV6
const struct in6_addr in6addr_any = {0}; /* IN6ADDR_ANY_INIT; */
Expand All @@ -78,7 +77,6 @@ const struct in6_addr in6addr_any = {0}; /* IN6ADDR_ANY_INIT; */
#else
# define SOCK_ERR -1
# define SOCK_CONN_ERR -1
# define PHP_TIMEOUT_ERROR_VALUE ETIMEDOUT
#endif

#ifdef HAVE_GETADDRINFO
Expand Down
2 changes: 2 additions & 0 deletions main/php_network.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,12 @@ typedef int php_socket_t;
# define SOCK_ERR INVALID_SOCKET
# define SOCK_CONN_ERR SOCKET_ERROR
# define SOCK_RECV_ERR SOCKET_ERROR
# define PHP_TIMEOUT_ERROR_VALUE WSAETIMEDOUT
#else
# define SOCK_ERR -1
# define SOCK_CONN_ERR -1
# define SOCK_RECV_ERR -1
# define PHP_TIMEOUT_ERROR_VALUE ETIMEDOUT
#endif

#define STREAM_SOCKOP_NONE (1 << 0)
Expand Down
4 changes: 4 additions & 0 deletions main/streams/xp_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,10 @@ static inline int php_tcp_sockop_accept(php_stream *stream, php_netstream_data_t
GC_ADDREF(stream->ctx);
}
}
} else if (!sock->is_blocked && (xparam->outputs.error_code == PHP_TIMEOUT_ERROR_VALUE
|| PHP_IS_TRANSIENT_ERROR(xparam->outputs.error_code))) {
/* No pending connection is not an error for a non-blocking listener. */
return 0;
}

return xparam->outputs.client == NULL ? -1 : 0;
Expand Down
Loading