Skip to content

Commit 0f3486e

Browse files
committed
Stop closing stderr and stdout streams
Extensions may (and do) write to stderr in mshutdown and similar. In the best case, with the stderr stream closed, it's just swallowed. However, some libraries will do things like try to detect color, and these will outright fail and cause an error path to be taken.
1 parent 33cd61c commit 0f3486e

File tree

5 files changed

+33
-9
lines changed

5 files changed

+33
-9
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.1.4
44

5+
- CLI:
6+
. Fixed GH-8575 (CLI closes standard streams too early). (Levi Morrison)
7+
58
- Core:
69
. Fixed Haiku ZTS build. (David Carlier)
710
. Fixed bug GH-8059 arginfo not regenerated for extension. (Remi)

ext/zend_test/php_test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
5151
HashTable global_weakmap;
5252
int replace_zend_execute_ex;
5353
int register_passes;
54+
bool print_stderr_mshutdown;
5455
zend_test_fiber *active_fiber;
5556
ZEND_END_MODULE_GLOBALS(zend_test)
5657

ext/zend_test/test.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ static ZEND_METHOD(ZendTestNS2_ZendSubNS_Foo, method)
393393
PHP_INI_BEGIN()
394394
STD_PHP_INI_BOOLEAN("zend_test.replace_zend_execute_ex", "0", PHP_INI_SYSTEM, OnUpdateBool, replace_zend_execute_ex, zend_zend_test_globals, zend_test_globals)
395395
STD_PHP_INI_BOOLEAN("zend_test.register_passes", "0", PHP_INI_SYSTEM, OnUpdateBool, register_passes, zend_zend_test_globals, zend_test_globals)
396+
STD_PHP_INI_BOOLEAN("zend_test.print_stderr_mshutdown", "0", PHP_INI_SYSTEM, OnUpdateBool, print_stderr_mshutdown, zend_zend_test_globals, zend_test_globals)
396397
PHP_INI_END()
397398

398399
void (*old_zend_execute_ex)(zend_execute_data *execute_data);
@@ -463,6 +464,10 @@ PHP_MSHUTDOWN_FUNCTION(zend_test)
463464

464465
zend_test_observer_shutdown(SHUTDOWN_FUNC_ARGS_PASSTHRU);
465466

467+
if (ZT_G(print_stderr_mshutdown)) {
468+
fprintf(stderr, "[zend-test] MSHUTDOWN\n");
469+
}
470+
466471
return SUCCESS;
467472
}
468473

ext/zend_test/tests/gh8575.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
CLI: stderr is available in mshutdown
3+
--SKIPIF--
4+
<?php if (!extension_loaded('zend-test')) die('skip: zend-test extension required'); ?>
5+
--INI--
6+
zend_test.print_stderr_mshutdown=1
7+
--FILE--
8+
==DONE==
9+
--EXPECTF--
10+
==DONE==
11+
[zend-test] MSHUTDOWN

sapi/cli/php_cli.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -538,19 +538,23 @@ static void cli_register_file_handles(bool no_close) /* {{{ */
538538
s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
539539
s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
540540

541+
/* Release stream resources, but don't free the underlying handles. Othewrise,
542+
* extensions which write to stderr or company during mshutdown/gshutdown
543+
* won't have the expected functionality.
544+
*/
545+
if (no_close) {
546+
s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE;
547+
s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
548+
s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
549+
}
550+
541551
if (s_in==NULL || s_out==NULL || s_err==NULL) {
542552
if (s_in) php_stream_close(s_in);
543553
if (s_out) php_stream_close(s_out);
544554
if (s_err) php_stream_close(s_err);
545555
return;
546556
}
547557

548-
if (no_close) {
549-
s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE;
550-
s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
551-
s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
552-
}
553-
554558
s_in_process = s_in;
555559

556560
php_stream_to_zval(s_in, &ic.value);
@@ -956,7 +960,7 @@ static int do_cli(int argc, char **argv) /* {{{ */
956960
switch (behavior) {
957961
case PHP_MODE_STANDARD:
958962
if (script_file) {
959-
cli_register_file_handles(/* no_close */ PHP_DEBUG || num_repeats > 1);
963+
cli_register_file_handles(/* no_close */ true);
960964
}
961965

962966
if (interactive) {
@@ -991,7 +995,7 @@ static int do_cli(int argc, char **argv) /* {{{ */
991995
}
992996
break;
993997
case PHP_MODE_CLI_DIRECT:
994-
cli_register_file_handles(/* no_close */ PHP_DEBUG || num_repeats > 1);
998+
cli_register_file_handles(/* no_close */ true);
995999
zend_eval_string_ex(exec_direct, NULL, "Command line code", 1);
9961000
break;
9971001

@@ -1006,7 +1010,7 @@ static int do_cli(int argc, char **argv) /* {{{ */
10061010
file_handle.filename = NULL;
10071011
}
10081012

1009-
cli_register_file_handles(/* no_close */ PHP_DEBUG || num_repeats > 1);
1013+
cli_register_file_handles(/* no_close */ true);
10101014

10111015
if (exec_begin) {
10121016
zend_eval_string_ex(exec_begin, NULL, "Command line begin code", 1);

0 commit comments

Comments
 (0)