Skip to content
Open
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ PHP NEWS
. Soap::__setCookie() when cookie name is a digit is now not stored and represented
as a string anymore but a int. (David Carlier)

- Sockets:
. Added the TCP_USER_TIMEOUT constant for Linux to set the maximum time in milliseconds
transmitted data can remain unacknowledged. (James Lucas)

- SPL:
. DirectoryIterator key can now work better with filesystem supporting larger
directory indexing. (David Carlier)
Expand Down
3 changes: 3 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ PHP 8.6 UPGRADE NOTES
10. New Global Constants
========================================

- Sockets:
. TCP_USER_TIMEOUT (Linux only).

========================================
11. Changes to INI File Handling
========================================
Expand Down
24 changes: 23 additions & 1 deletion ext/sockets/sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -1773,7 +1773,7 @@ PHP_FUNCTION(socket_sendto)
RETURN_THROWS();
}
memset(&sll, 0, sizeof(sll));
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = port;
Expand Down Expand Up @@ -2130,6 +2130,28 @@ PHP_FUNCTION(socket_set_option)
}
#endif

#if defined(TCP_USER_TIMEOUT)
case TCP_USER_TIMEOUT: {
zend_long timeout = zval_get_long(arg4);

// TCP_USER_TIMEOUT unsigned int
if (timeout < 0 || timeout > UINT_MAX) {
zend_argument_value_error(4, "must be of between 0 and %u", UINT_MAX);
RETURN_THROWS();
}

unsigned int val = (unsigned int)timeout;
optlen = sizeof(val);
opt_ptr = &val;
if (setsockopt(php_sock->bsd_socket, level, optname, opt_ptr, optlen) != 0) {
PHP_SOCKET_ERROR(php_sock, "Unable to set socket option", errno);
RETURN_FALSE;
}

RETURN_TRUE;
}
#endif

}
}

Expand Down
7 changes: 7 additions & 0 deletions ext/sockets/sockets.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,13 @@
*/
const TCP_SYNCNT = UNKNOWN;
#endif
#ifdef TCP_USER_TIMEOUT
/**
* @var int
* @cvalue TCP_USER_TIMEOUT
*/
const TCP_USER_TIMEOUT = UNKNOWN;
#endif
#ifdef SO_ZEROCOPY
/**
* @var int
Expand Down
5 changes: 4 additions & 1 deletion ext/sockets/sockets_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions ext/sockets/tests/socket_setoption_tcpusertimeout.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
Test if socket_set_option() works, option:TCP_USER_TIMEOUT
--EXTENSIONS--
sockets
--SKIPIF--
<?php
if (!defined('TCP_USER_TIMEOUT')) { die('skip TCP_USER_TIMEOUT is not defined'); }
?>
--FILE--
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!$socket) {
die('Unable to create AF_INET socket [socket]');
}
socket_set_block($socket);

try {
socket_setopt($socket, SOL_TCP, TCP_USER_TIMEOUT, -1);
Copy link
Member

@devnexen devnexen Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: now I see it s working, please add a test with a large number (e.g. PHP_INT_MAX)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after that, that will be all :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do I account for differences in 32-bit and 64-bit PHP versions. PHP_INT_MAX will be less than UINT_MAX on 32-bit versions

UINT_MAX 4294967295
PHP_INT_MAX (32-bit) 2147483647
PHP_INT_MAX (64-bit) 9223372036854775807

I know we can test for PHP_INT_MAX = 4 and skip the check, however unsure how I would then handle the missing output in 32-bit EXPECTF (seperate test case?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that s a good point there is the possibility to create two tests one for 32 and for 64 bits indeed.

} catch (\ValueError $e) {
echo $e->getMessage(), PHP_EOL;
}

$timeout = 200;
$retval_2 = socket_set_option($socket, SOL_TCP, TCP_USER_TIMEOUT, $timeout);
$retval_3 = socket_get_option($socket, SOL_TCP, TCP_USER_TIMEOUT);
var_dump($retval_2);
var_dump($retval_3 === $timeout);
socket_close($socket);
?>
--EXPECTF--
socket_setopt(): Argument #4 ($value) must be of between 0 and %d
bool(true)
bool(true)