Skip to content

Commit da1cbd2

Browse files
committed
Merge branch 'PHP-8.1'
* PHP-8.1: [ci skip] NEWS Fix GH-8952: std streams can not be deliberately closed (#8953) intl extension, build fix for icu >= 69.x release. ubrk/ucnv_safeClone had been deprecated in favor of ubrk/ucnv_clone which does not use user provided stacks but remain thread safe.
2 parents e809254 + d86141a commit da1cbd2

File tree

4 files changed

+148
-8
lines changed

4 files changed

+148
-8
lines changed

sapi/cli/php_cli.c

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -536,21 +536,19 @@ static void cli_register_file_handles(void) /* {{{ */
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-
547539
if (s_in==NULL || s_out==NULL || s_err==NULL) {
548540
if (s_in) php_stream_close(s_in);
549541
if (s_out) php_stream_close(s_out);
550542
if (s_err) php_stream_close(s_err);
551543
return;
552544
}
553545

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+
554552
s_in_process = s_in;
555553

556554
php_stream_to_zval(s_in, &ic.value);

sapi/cli/tests/gh8827-001.phpt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
std handles can be deliberately closed 001
3+
--SKIPIF--
4+
<?php
5+
if (php_sapi_name() != "cli") {
6+
die("skip CLI only");
7+
}
8+
if (PHP_OS_FAMILY == 'Windows') {
9+
die("skip not for Windows");
10+
}
11+
if (PHP_DEBUG) {
12+
die("skip std streams are not closeable in debug builds");
13+
}
14+
if (getenv('SKIP_REPEAT')) {
15+
die("skip cannot be repeated");
16+
}
17+
?>
18+
--FILE--
19+
<?php
20+
print "STDIN:\n";
21+
fclose(STDIN);
22+
var_dump(@fopen('php://stdin', 'r'));
23+
24+
print "STDERR:\n";
25+
fclose(STDERR);
26+
var_dump(@fopen('php://stderr', 'a'));
27+
28+
print "STDOUT:\n";
29+
fclose(STDOUT);
30+
// not printed if stdout is closed
31+
var_dump(@fopen('php://stdout', 'a'));
32+
?>
33+
--EXPECT--
34+
STDIN:
35+
bool(false)
36+
STDERR:
37+
bool(false)
38+
STDOUT:

sapi/cli/tests/gh8827-002.phpt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
std handles can be deliberately closed 002
3+
--SKIPIF--
4+
<?php
5+
if (php_sapi_name() != "cli") {
6+
die("skip CLI only");
7+
}
8+
if (PHP_OS_FAMILY == 'Windows') {
9+
die("skip not for Windows");
10+
}
11+
if (PHP_DEBUG) {
12+
die("skip std streams are not closeable in debug builds");
13+
}
14+
if (getenv('SKIP_REPEAT')) {
15+
die("skip cannot be repeated");
16+
}
17+
?>
18+
--FILE--
19+
<?php
20+
21+
$stdin = fopen('php://stdin', 'r');
22+
$stdout = fopen('php://stdout', 'r');
23+
$stderr = fopen('php://stderr', 'r');
24+
25+
ob_start(function ($buffer) use ($stdout) {
26+
fwrite($stdout, $buffer);
27+
}, 1);
28+
29+
print "STDIN:\n";
30+
fclose(STDIN);
31+
var_dump(@fopen('php://stdin', 'r'));
32+
33+
print "STDERR:\n";
34+
fclose(STDERR);
35+
var_dump(@fopen('php://stderr', 'a'));
36+
37+
print "STDOUT:\n";
38+
fclose(STDOUT);
39+
var_dump(@fopen('php://stdout', 'a'));
40+
?>
41+
--EXPECT--
42+
STDIN:
43+
bool(false)
44+
STDERR:
45+
bool(false)
46+
STDOUT:
47+
bool(false)

sapi/cli/tests/gh8827-003.phpt

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
--TEST--
2+
std handles can be deliberately closed 003
3+
--SKIPIF--
4+
<?php
5+
if (php_sapi_name() != "cli") {
6+
die("skip CLI only");
7+
}
8+
if (PHP_OS_FAMILY == 'Windows') {
9+
die("skip not for Windows");
10+
}
11+
if (PHP_DEBUG) {
12+
die("skip std streams are not closeable in debug builds");
13+
}
14+
if (getenv('SKIP_REPEAT')) {
15+
die("skip cannot be repeated");
16+
}
17+
?>
18+
--FILE--
19+
<?php
20+
21+
$stdoutStream = fopen('php://stdout', 'r');
22+
23+
$stdoutFile = tempnam(sys_get_temp_dir(), 'gh8827');
24+
$stderrFile = tempnam(sys_get_temp_dir(), 'gh8827');
25+
register_shutdown_function(function () use ($stdoutFile, $stderrFile) {
26+
unlink($stdoutFile);
27+
unlink($stderrFile);
28+
});
29+
30+
fclose(STDOUT);
31+
fclose(STDERR);
32+
33+
$stdoutFileStream = fopen($stdoutFile, 'a');
34+
$stderrFileStream = fopen($stderrFile, 'a');
35+
36+
print "Goes to stdoutFile\n";
37+
file_put_contents('php://fd/1', "Also goes to stdoutFile\n");
38+
39+
file_put_contents('php://fd/2', "Goes to stderrFile\n");
40+
41+
ob_start(function ($buffer) use ($stdoutStream) {
42+
fwrite($stdoutStream, $buffer);
43+
}, 1);
44+
45+
print "stdoutFile:\n";
46+
readfile($stdoutFile);
47+
48+
print "stderrFile:\n";
49+
readfile($stderrFile);
50+
51+
?>
52+
--EXPECT--
53+
stdoutFile:
54+
Goes to stdoutFile
55+
Also goes to stdoutFile
56+
stderrFile:
57+
Goes to stderrFile

0 commit comments

Comments
 (0)