From d21ca0965c5889ac0d24d036496cc41399442be8 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 2 Aug 2026 16:11:04 +0800 Subject: [PATCH 1/5] ext/readline: enhance test coverage --- .../tests/readline_cli_multiline_states.phpt | 32 +++++++++++++++++++ ext/readline/tests/readline_cli_pager.phpt | 22 +++++++++++++ ext/readline/tests/readline_cli_prompt.phpt | 25 +++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 ext/readline/tests/readline_cli_multiline_states.phpt create mode 100644 ext/readline/tests/readline_cli_pager.phpt create mode 100644 ext/readline/tests/readline_cli_prompt.phpt diff --git a/ext/readline/tests/readline_cli_multiline_states.phpt b/ext/readline/tests/readline_cli_multiline_states.phpt new file mode 100644 index 000000000000..1020126031b9 --- /dev/null +++ b/ext/readline/tests/readline_cli_multiline_states.phpt @@ -0,0 +1,32 @@ +--TEST-- +Interactive shell: multiline input states +--EXTENSIONS-- +readline +--SKIPIF-- + +--FILE-- +outside- +--EXPECTF-- +%AInteractive shell%Asingle%Adouble%APAREN%Ablock%Acomment%Aattribute%Aoutside-%Ainside%A diff --git a/ext/readline/tests/readline_cli_pager.phpt b/ext/readline/tests/readline_cli_pager.phpt new file mode 100644 index 000000000000..79fbe37f7f73 --- /dev/null +++ b/ext/readline/tests/readline_cli_pager.phpt @@ -0,0 +1,22 @@ +--TEST-- +Interactive shell: output through cli.pager +--EXTENSIONS-- +readline +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%Apager output%A diff --git a/ext/readline/tests/readline_cli_prompt.phpt b/ext/readline/tests/readline_cli_prompt.phpt new file mode 100644 index 000000000000..548fedbe0283 --- /dev/null +++ b/ext/readline/tests/readline_cli_prompt.phpt @@ -0,0 +1,25 @@ +--TEST-- +Interactive shell: custom prompt escape sequences +--EXTENSIONS-- +readline +--SKIPIF-- + +--FILE-- +-\`-\q-' . "\xC3\xA9" . '-`echo "dyn";`-x '; +$descriptorspec = [['pipe', 'r'], STDOUT, STDERR]; +$proc = proc_open("$php $ini -d " . escapeshellarg("cli.prompt=$prompt") . " -a", $descriptorspec, $pipes); +fwrite($pipes[0], "if (true) {\n"); +fwrite($pipes[0], "echo \"prompt body\n\";\n"); +fwrite($pipes[0], "}\n"); +fwrite($pipes[0], "quit\n"); +fclose($pipes[0]); +proc_close($proc); +?> +--EXPECTF-- +%AInteractive shell%Aprompt contains unsupported unicode characters%Adyn%Aprompt body%A From 0b4eaf3a5f458022fba3ca6de1c0eb7e3d487bf2 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 2 Aug 2026 16:23:36 +0800 Subject: [PATCH 2/5] add tests for edge cases --- ext/readline/tests/readline_cli_multiline_states.phpt | 8 ++++---- ext/readline/tests/readline_cli_prompt.phpt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/readline/tests/readline_cli_multiline_states.phpt b/ext/readline/tests/readline_cli_multiline_states.phpt index 1020126031b9..5e09b6647e90 100644 --- a/ext/readline/tests/readline_cli_multiline_states.phpt +++ b/ext/readline/tests/readline_cli_multiline_states.phpt @@ -15,15 +15,15 @@ $proc = proc_open("$php $ini -a", $descriptorspec, $pipes); fwrite($pipes[0], "\n"); fwrite($pipes[0], "# comment without ini assignment\n"); -fwrite($pipes[0], "echo 'single\n';\n"); -fwrite($pipes[0], "echo \"double\n\";\n"); +fwrite($pipes[0], "echo 'single \\\\ escape\n';\n"); +fwrite($pipes[0], "echo \"double \\\\ escape\n\";\n"); fwrite($pipes[0], "echo strtoupper(\n\"paren\"\n);\n"); fwrite($pipes[0], "if (true) {\necho \"block\n\";\n}\n"); +fwrite($pipes[0], "// line comment\n"); fwrite($pipes[0], "/*\n*/\necho \"comment\n\";\n"); fwrite($pipes[0], "#[AllowDynamicProperties]\nclass ReadlineCliCoverageClass {}\n"); fwrite($pipes[0], "echo \"attribute\n\";\n"); -fwrite($pipes[0], "?>outside-outside-\n-\`-\q-' . "\xC3\xA9" . '-`echo "dyn";`-x '; +$prompt = 'pre\\\\-\n-\t-\e-\v-\b-\>-\`-\q-' . "\xC3\xA9\xC3\xA9" . '-`echo "dyn";`-x '; $descriptorspec = [['pipe', 'r'], STDOUT, STDERR]; $proc = proc_open("$php $ini -d " . escapeshellarg("cli.prompt=$prompt") . " -a", $descriptorspec, $pipes); fwrite($pipes[0], "if (true) {\n"); From f5761225063d4dd6a9d7e094e4501ac195fa4b60 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 2 Aug 2026 16:29:41 +0800 Subject: [PATCH 3/5] more tests --- .../tests/readline_cli_auto_prepend.phpt | 30 ++++++++++++++ .../readline_cli_completion_readline.phpt | 39 +++++++++++++++++++ .../tests/readline_cli_long_input.phpt | 22 +++++++++++ .../tests/readline_cli_multiline_states.phpt | 3 +- ext/readline/tests/readline_cli_prompt.phpt | 2 +- 5 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 ext/readline/tests/readline_cli_auto_prepend.phpt create mode 100644 ext/readline/tests/readline_cli_completion_readline.phpt create mode 100644 ext/readline/tests/readline_cli_long_input.phpt diff --git a/ext/readline/tests/readline_cli_auto_prepend.phpt b/ext/readline/tests/readline_cli_auto_prepend.phpt new file mode 100644 index 000000000000..71d4477a7cde --- /dev/null +++ b/ext/readline/tests/readline_cli_auto_prepend.phpt @@ -0,0 +1,30 @@ +--TEST-- +Interactive shell: auto_prepend_file is executed before input +--EXTENSIONS-- +readline +--SKIPIF-- + +--FILE-- + []); +PHP); + +$php = getenv('TEST_PHP_EXECUTABLE_ESCAPED'); +$ini = getenv('TEST_PHP_EXTRA_ARGS'); +$descriptorspec = [['pipe', 'r'], STDOUT, STDERR]; +$proc = proc_open("$php $ini -d auto_prepend_file=" . escapeshellarg($prepend) . " -a", $descriptorspec, $pipes); +fwrite($pipes[0], "echo READLINE_CLI_PREPENDED . \"\n\";\n"); +fwrite($pipes[0], "exit\n"); +fclose($pipes[0]); +proc_close($proc); +unlink($prepend); +?> +--EXPECTF-- +%AInteractive shell%Aprepended%Aok%A diff --git a/ext/readline/tests/readline_cli_completion_readline.phpt b/ext/readline/tests/readline_cli_completion_readline.phpt new file mode 100644 index 000000000000..fabcf59deba6 --- /dev/null +++ b/ext/readline/tests/readline_cli_completion_readline.phpt @@ -0,0 +1,39 @@ +--TEST-- +Interactive shell: default completion function +--EXTENSIONS-- +readline +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%AInteractive shell%Avariable%Aprecision=3%Afunction%Aconstant%AReadlineCliCompletionClass%Aclass constant%Amethod%A diff --git a/ext/readline/tests/readline_cli_long_input.phpt b/ext/readline/tests/readline_cli_long_input.phpt new file mode 100644 index 000000000000..a74850aef2d4 --- /dev/null +++ b/ext/readline/tests/readline_cli_long_input.phpt @@ -0,0 +1,22 @@ +--TEST-- +Interactive shell: input buffer grows for long lines +--EXTENSIONS-- +readline +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%AInteractive shell%A5000%A diff --git a/ext/readline/tests/readline_cli_multiline_states.phpt b/ext/readline/tests/readline_cli_multiline_states.phpt index 5e09b6647e90..aebbcc112542 100644 --- a/ext/readline/tests/readline_cli_multiline_states.phpt +++ b/ext/readline/tests/readline_cli_multiline_states.phpt @@ -18,6 +18,7 @@ fwrite($pipes[0], "# comment without ini assignment\n"); fwrite($pipes[0], "echo 'single \\\\ escape\n';\n"); fwrite($pipes[0], "echo \"double \\\\ escape\n\";\n"); fwrite($pipes[0], "echo strtoupper(\n\"paren\"\n);\n"); +fwrite($pipes[0], "echo 6 /\n2;\n"); fwrite($pipes[0], "if (true) {\necho \"block\n\";\n}\n"); fwrite($pipes[0], "// line comment\n"); fwrite($pipes[0], "/*\n*/\necho \"comment\n\";\n"); @@ -29,4 +30,4 @@ fclose($pipes[0]); proc_close($proc); ?> --EXPECTF-- -%AInteractive shell%Asingle%Adouble%APAREN%Ablock%Acomment%Aattribute%Aoutside-%Ainside%A +%AInteractive shell%Asingle%Adouble%APAREN%A3%Ablock%Acomment%Aattribute%Aoutside-%Ainside%A diff --git a/ext/readline/tests/readline_cli_prompt.phpt b/ext/readline/tests/readline_cli_prompt.phpt index e93b9acbe0b3..dd46ca362860 100644 --- a/ext/readline/tests/readline_cli_prompt.phpt +++ b/ext/readline/tests/readline_cli_prompt.phpt @@ -11,7 +11,7 @@ if (!function_exists('proc_open')) die('skip proc_open() not available'); -\`-\q-' . "\xC3\xA9\xC3\xA9" . '-`echo "dyn";`-x '; +$prompt = 'pre\\\\-\n-\t-\e-\v-\b-\>-\`-\q-' . "\xC3\xA9\xC3\xA9" . '-`echo "dyn";`-`-x '; $descriptorspec = [['pipe', 'r'], STDOUT, STDERR]; $proc = proc_open("$php $ini -d " . escapeshellarg("cli.prompt=$prompt") . " -a", $descriptorspec, $pipes); fwrite($pipes[0], "if (true) {\n"); From d7d2bd170dadf4672e653357f4686e71d2ff1320 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 2 Aug 2026 19:26:50 +0800 Subject: [PATCH 4/5] [skip ci] Add NEWS and UPGRADING --- NEWS | 3 +++ UPGRADING | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/NEWS b/NEWS index 0d5b6300fc7a..425a48481a98 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,9 @@ PHP NEWS ReflectionAttribute::getShortName(). (Girgias) - Standard: + . Added the "filter.max_filter_count" stream context option for php://filter + URLs. Using more than 16 filters without configuring this option is now + deprecated. (Sjoerd Langkemper) . The following functions now raise a ValueError when the $filename argument contains NUL bytes: fileperms(), fileinode(), filesize(), fileowner(), filegroup(), fileatime(), filemtime(), filectime(), filetype(), diff --git a/UPGRADING b/UPGRADING index fd66c7e61180..135a61d0d205 100644 --- a/UPGRADING +++ b/UPGRADING @@ -363,6 +363,10 @@ PHP 8.6 UPGRADE NOTES internal API. It is controlled using error_mode, error_store and error_handler stream context options. RFC: https://wiki.php.net/rfc/stream_errors + . Added the "filter.max_filter_count" stream context option for php://filter + URLs. When set, opening the stream fails with a warning if the URL would + add more filters than the configured value. Negative values disable the + check. . Added stream socket context option so_reuseaddr that allows disabling address reuse (SO_REUSEADDR) and explicitly uses SO_EXCLUSIVEADDRUSE on Windows. @@ -413,6 +417,11 @@ PHP 8.6 UPGRADE NOTES is no longer maintained. RFC: https://wiki.php.net/rfc/eol-oniguruma +- Standard: + . Using more than 16 filters in a php://filter URL without configuring the + "filter.max_filter_count" stream context option now emits an E_DEPRECATED + warning. Use stream_filter_append() or configure this option explicitly. + ======================================== 5. Changed Functions ======================================== From 740bb6bc59a20a012b997015863707ce6ec426af Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 2 Aug 2026 19:27:51 +0800 Subject: [PATCH 5/5] [skip ci] Revert Add NEWS and UPGRADING This reverts commit d7d2bd170dadf4672e653357f4686e71d2ff1320. --- NEWS | 3 --- UPGRADING | 9 --------- 2 files changed, 12 deletions(-) diff --git a/NEWS b/NEWS index 425a48481a98..0d5b6300fc7a 100644 --- a/NEWS +++ b/NEWS @@ -21,9 +21,6 @@ PHP NEWS ReflectionAttribute::getShortName(). (Girgias) - Standard: - . Added the "filter.max_filter_count" stream context option for php://filter - URLs. Using more than 16 filters without configuring this option is now - deprecated. (Sjoerd Langkemper) . The following functions now raise a ValueError when the $filename argument contains NUL bytes: fileperms(), fileinode(), filesize(), fileowner(), filegroup(), fileatime(), filemtime(), filectime(), filetype(), diff --git a/UPGRADING b/UPGRADING index 135a61d0d205..fd66c7e61180 100644 --- a/UPGRADING +++ b/UPGRADING @@ -363,10 +363,6 @@ PHP 8.6 UPGRADE NOTES internal API. It is controlled using error_mode, error_store and error_handler stream context options. RFC: https://wiki.php.net/rfc/stream_errors - . Added the "filter.max_filter_count" stream context option for php://filter - URLs. When set, opening the stream fails with a warning if the URL would - add more filters than the configured value. Negative values disable the - check. . Added stream socket context option so_reuseaddr that allows disabling address reuse (SO_REUSEADDR) and explicitly uses SO_EXCLUSIVEADDRUSE on Windows. @@ -417,11 +413,6 @@ PHP 8.6 UPGRADE NOTES is no longer maintained. RFC: https://wiki.php.net/rfc/eol-oniguruma -- Standard: - . Using more than 16 filters in a php://filter URL without configuring the - "filter.max_filter_count" stream context option now emits an E_DEPRECATED - warning. Use stream_filter_append() or configure this option explicitly. - ======================================== 5. Changed Functions ========================================