1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Allow filtered streams to be casted as fd for select

This removes the artificial limitation that is not necessary. The fact
that some streams can have some data buffered is not a problem because
the similar situation is already present for OpenSSL streams where
OpenSSL can internally buffer data for the unprocessed part of the
record.

Closes GH-20540
This commit is contained in:
Jakub Zelenka
2025-11-20 17:18:35 +01:00
parent 241e43f2d8
commit e49be5f8e2
5 changed files with 94 additions and 1 deletions

1
NEWS
View File

@@ -79,6 +79,7 @@ PHP NEWS
address resuse.
. Fixed bug GH-20370 (User stream filters could violate typed property
constraints). (alexandre-daubois)
. Allowed filtered streams to be casted as fd for select. (Jakub Zelenka)
- Zip:
. Fixed ZipArchive callback being called after executor has shut down.

View File

@@ -49,6 +49,7 @@ PHP 8.6 UPGRADE NOTES
. Added stream socket context options so_keepalive, tcp_keepidle,
tcp_keepintvl and tcp_keepcnt that allow setting socket keepalive
options.
. Allowed casting casting filtered streams as file descriptor for select.
========================================
3. Changes in SAPI modules

View File

@@ -0,0 +1,32 @@
--TEST--
stream_select() base64-decode filter basic usage
--FILE--
<?php
$file = fopen('php://temp', 'r+');
// Write complete base64 "Hello World"
fwrite($file, 'SGVsbG8gV29ybGQ=');
stream_filter_append($file, 'convert.base64-decode');
rewind($file);
$read = [$file];
$write = null;
$except = null;
$result = stream_select($read, $write, $except, 0);
if ($result !== false) {
echo "stream_select succeeded\n";
if (count($read) > 0) {
echo fread($file, 1024) . "\n";
}
} else {
echo "stream_select failed\n";
}
fclose($file);
?>
--EXPECT--
stream_select succeeded
Hello World

View File

@@ -0,0 +1,59 @@
--TEST--
stream_select() base64-decode filter buffered data usage
--FILE--
<?php
$domain = (strtoupper(substr(PHP_OS, 0, 3) == 'WIN') ? STREAM_PF_INET : STREAM_PF_UNIX);
$pipes = stream_socket_pair($domain, STREAM_SOCK_STREAM, 0);
if ($pipes === false) {
die("Failed to create socket pair");
}
list($read_pipe, $write_pipe) = $pipes;
stream_set_blocking($read_pipe, false);
stream_filter_append($read_pipe, 'convert.base64-decode');
// Write incomplete base64 data: "SGVs" decodes to "Hel" but "SGVs" is incomplete
fwrite($write_pipe, 'SGVs');
fflush($write_pipe);
$read = [$read_pipe];
$write = null;
$except = null;
$result = fread($read_pipe, 1024);
echo "Decoded content (before select): " . $result . "\n";
// Should succeed but stream should NOT be readable yet (data buffered in filter)
$result = stream_select($read, $write, $except, 0, 1000);
echo "After incomplete data - select result: " . $result . "\n";
echo "After incomplete data - readable streams: " . count($read) . "\n";
// Now complete the base64 sequence: "SGVsbG8=" decodes to "Hello"
fwrite($write_pipe, 'bG8=');
fflush($write_pipe);
$read = [$read_pipe];
$write = null;
$except = null;
// Now stream should be readable
$result = stream_select($read, $write, $except, 0, 1000);
echo "After complete data - select result: " . $result . "\n";
echo "After complete data - readable streams: " . count($read) . "\n";
if (count($read) > 0) {
$content = fread($read_pipe, 1024);
echo "Decoded content: " . $content . "\n";
}
fclose($read_pipe);
fclose($write_pipe);
?>
--EXPECT--
Decoded content (before select): Hel
After incomplete data - select result: 0
After incomplete data - readable streams: 0
After complete data - select result: 1
After complete data - readable streams: 1
Decoded content: lo

View File

@@ -297,7 +297,7 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret,
}
}
if (php_stream_is_filtered(stream)) {
if (php_stream_is_filtered(stream) && castas != PHP_STREAM_AS_FD_FOR_SELECT) {
if (show_err) {
php_error_docref(NULL, E_WARNING, "Cannot cast a filtered stream on this system");
}