From e49be5f8e2903dbefa262847f1494c8e4c86004e Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 20 Nov 2025 17:18:35 +0100 Subject: [PATCH] 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 --- NEWS | 1 + UPGRADING | 1 + .../stream_select_filtered_base64_basic.phpt | 32 ++++++++++ ...tream_select_filtered_base64_buffered.phpt | 59 +++++++++++++++++++ main/streams/cast.c | 2 +- 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/streams/stream_select_filtered_base64_basic.phpt create mode 100644 ext/standard/tests/streams/stream_select_filtered_base64_buffered.phpt diff --git a/NEWS b/NEWS index 3b20cbb026c44..14b47a44d3863 100644 --- a/NEWS +++ b/NEWS @@ -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. diff --git a/UPGRADING b/UPGRADING index f9e506235197a..85cad750f0866 100644 --- a/UPGRADING +++ b/UPGRADING @@ -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 diff --git a/ext/standard/tests/streams/stream_select_filtered_base64_basic.phpt b/ext/standard/tests/streams/stream_select_filtered_base64_basic.phpt new file mode 100644 index 0000000000000..f7ec2b6a4b542 --- /dev/null +++ b/ext/standard/tests/streams/stream_select_filtered_base64_basic.phpt @@ -0,0 +1,32 @@ +--TEST-- +stream_select() base64-decode filter basic usage +--FILE-- + 0) { + echo fread($file, 1024) . "\n"; + } +} else { + echo "stream_select failed\n"; +} + +fclose($file); +?> +--EXPECT-- +stream_select succeeded +Hello World diff --git a/ext/standard/tests/streams/stream_select_filtered_base64_buffered.phpt b/ext/standard/tests/streams/stream_select_filtered_base64_buffered.phpt new file mode 100644 index 0000000000000..4d11bf718a1bd --- /dev/null +++ b/ext/standard/tests/streams/stream_select_filtered_base64_buffered.phpt @@ -0,0 +1,59 @@ +--TEST-- +stream_select() base64-decode filter buffered data usage +--FILE-- + 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 diff --git a/main/streams/cast.c b/main/streams/cast.c index 4b7183024571b..4dc8ddb5f6a30 100644 --- a/main/streams/cast.c +++ b/main/streams/cast.c @@ -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"); }