Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
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
16 changes: 16 additions & 0 deletions ext/sockets/sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -2329,6 +2329,22 @@ PHP_FUNCTION(socket_set_option)
}
#endif

#if defined(TCP_USER_TIMEOUT)
case TCP_USER_TIMEOUT: {
ov = zval_get_long(arg4);
Copy link
Member

@devnexen devnexen Dec 16, 2025

Choose a reason for hiding this comment

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

my bad, I realise ov is an int.
you would need the following change

zend_long timeout = zval_get_long(arg4);
<overflow check on timeout>
unsigned int val = (unsigned int)timeout;
optlen = sizeof(val);
opt_ptr = &val;

Copy link
Member

Choose a reason for hiding this comment

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

I did not mean this. more like

case TCP_USER_TIMEOUT: {
			zend_long timeout 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;
			break;

Your test passes because you test -1, but ov can never be greater than UINT_MAX.

Copy link
Member

Choose a reason for hiding this comment

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

Bear with me :) we re almost there.


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

optlen = sizeof(ov);
opt_ptr = &ov;
break;
}
#endif

#if defined(UDP_SEGMENT)
case UDP_SEGMENT: {
ov = zval_get_long(arg4);
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);
} 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);
?>
--EXPECT--
socket_setopt(): Argument #4 ($value) must be of between 0 and 4294967295
bool(true)
bool(true)