Skip to content

Commit e463fc4

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 7850c10 commit e463fc4

File tree

5 files changed

+30
-10
lines changed

5 files changed

+30
-10
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ PHP NEWS
44

55
- CLI:
66
. Fixed bug #81496 (Server logs incorrect request method). (lauri)
7+
. Fixed GH-8575 (CLI closes standard streams too early). (Levi Morrison)
78

89
- Core:
910
. Fixed bug #81380 (Observer may not be initialized properly). (krakjoe)

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
@@ -495,6 +495,7 @@ static ZEND_METHOD(ZendTestChildClassWithMethodWithParameterAttribute, override)
495495
PHP_INI_BEGIN()
496496
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)
497497
STD_PHP_INI_BOOLEAN("zend_test.register_passes", "0", PHP_INI_SYSTEM, OnUpdateBool, register_passes, zend_zend_test_globals, zend_test_globals)
498+
STD_PHP_INI_BOOLEAN("zend_test.print_stderr_mshutdown", "0", PHP_INI_SYSTEM, OnUpdateBool, print_stderr_mshutdown, zend_zend_test_globals, zend_test_globals)
498499
PHP_INI_END()
499500

500501
void (*old_zend_execute_ex)(zend_execute_data *execute_data);
@@ -620,6 +621,10 @@ PHP_MSHUTDOWN_FUNCTION(zend_test)
620621

621622
zend_test_observer_shutdown(SHUTDOWN_FUNC_ARGS_PASSTHRU);
622623

624+
if (ZT_G(print_stderr_mshutdown)) {
625+
fprintf(stderr, "[zend-test] MSHUTDOWN\n");
626+
}
627+
623628
return SUCCESS;
624629
}
625630

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: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ static void php_cli_usage(char *argv0)
526526

527527
static php_stream *s_in_process = NULL;
528528

529-
static void cli_register_file_handles(bool no_close) /* {{{ */
529+
static void cli_register_file_handles(void) /* {{{ */
530530
{
531531
php_stream *s_in, *s_out, *s_err;
532532
php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
@@ -536,19 +536,21 @@ static void cli_register_file_handles(bool no_close) /* {{{ */
536536
s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
537537
s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
538538

539+
/* Release stream resources, but don't free the underlying handles. Othewrise,
540+
* extensions which write to stderr or company during mshutdown/gshutdown
541+
* won't have the expected functionality.
542+
*/
543+
if (s_in) s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE;
544+
if (s_out) s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
545+
if (s_err) s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
546+
539547
if (s_in==NULL || s_out==NULL || s_err==NULL) {
540548
if (s_in) php_stream_close(s_in);
541549
if (s_out) php_stream_close(s_out);
542550
if (s_err) php_stream_close(s_err);
543551
return;
544552
}
545553

546-
if (no_close) {
547-
s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE;
548-
s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
549-
s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
550-
}
551-
552554
s_in_process = s_in;
553555

554556
php_stream_to_zval(s_in, &ic.value);
@@ -954,7 +956,7 @@ static int do_cli(int argc, char **argv) /* {{{ */
954956
switch (behavior) {
955957
case PHP_MODE_STANDARD:
956958
if (script_file) {
957-
cli_register_file_handles(/* no_close */ PHP_DEBUG || num_repeats > 1);
959+
cli_register_file_handles();
958960
}
959961

960962
if (interactive) {
@@ -989,7 +991,7 @@ static int do_cli(int argc, char **argv) /* {{{ */
989991
}
990992
break;
991993
case PHP_MODE_CLI_DIRECT:
992-
cli_register_file_handles(/* no_close */ PHP_DEBUG || num_repeats > 1);
994+
cli_register_file_handles();
993995
zend_eval_string_ex(exec_direct, NULL, "Command line code", 1);
994996
break;
995997

@@ -1004,7 +1006,7 @@ static int do_cli(int argc, char **argv) /* {{{ */
10041006
file_handle.filename = NULL;
10051007
}
10061008

1007-
cli_register_file_handles(/* no_close */ PHP_DEBUG || num_repeats > 1);
1009+
cli_register_file_handles();
10081010

10091011
if (exec_begin) {
10101012
zend_eval_string_ex(exec_begin, NULL, "Command line begin code", 1);

0 commit comments

Comments
 (0)