Add tests for uncastable streams and dataloss streams (#10173)

And suppress the nonsensical warnings by passing the PHP_STREAM_CAST_INTERNAL flag.
This commit is contained in:
George Peter Banyard 2023-07-10 13:24:23 +01:00 committed by GitHub
parent 4863d93c96
commit 39ef5ca31c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 346 additions and 25 deletions

View file

@ -426,10 +426,15 @@ static int php_posix_stream_get_fd(zval *zfp, zend_long *fd) /* {{{ */
if (stream == NULL) {
return 0;
}
if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT, (void*)fd, 0);
} else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)fd, 0);
/* get the fd.
* NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting.
* It is only used here so that the buffered data warning is not displayed.
*/
if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&fd, 0);
} else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fd, 0);
} else {
php_error_docref(NULL, E_WARNING, "Could not use stream of type '%s'",
stream->ops->label);

View file

@ -15,7 +15,7 @@ try {
} catch (\TypeError $e) {
echo $e->getMessage() . "\n";
}
$fd = fopen(sys_get_temp_dir(), "r");
$fd = fopen(__DIR__, "r");
var_dump(posix_fpathconf($fd, POSIX_PC_PATH_MAX));
fclose($fd);
?>

View file

@ -2,15 +2,11 @@
posix_isatty(): Basic tests
--EXTENSIONS--
posix
--SKIPIF--
<?php
if (!function_exists('posix_isatty')) die('skip posix_isatty() not found');
?>
--FILE--
<?php
var_dump(posix_isatty(0));
var_dump(posix_isatty(STDIN));
?>
--EXPECTF--
bool(%s)
--EXPECT--
bool(false)

View file

@ -0,0 +1,33 @@
--TEST--
posix_isatty(): casting stream does not emit data loss and should not emit warnings
--EXTENSIONS--
posix
--SKIPIF--
<?php
require __DIR__ . '/../../standard/tests/http/server.inc'; http_server_skipif();
?>
--INI--
allow_url_fopen=1
--FILE--
<?php
require __DIR__ . '/../../standard/tests/http/server.inc';
$responses = array(
"data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\nSome: Header\r\n\r\nBody",
);
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
/* Note: the warning is bogus in this case as no data actually gets lost,
* but this checks that stream casting works */
$handle = fopen($uri, 'r');
var_dump(posix_isatty($handle));
var_dump(fread($handle, 20));
fclose($handle);
http_server_kill($pid);
?>
--EXPECT--
bool(false)
string(4) "Body"

View file

@ -0,0 +1,20 @@
--TEST--
posix_isatty(): uncastable user stream
--EXTENSIONS--
posix
--FILE--
<?php
require dirname(__DIR__, 3) . '/tests/output/DummyStreamWrapper.inc';
stream_wrapper_register('custom', DummyStreamWrapper::class);
$fp = fopen("custom://myvar", "r+");
var_dump(posix_isatty($fp));
fclose($fp);
echo "Done";
?>
--EXPECTF--
Warning: posix_isatty(): Could not use stream of type 'user-space' in %s on line %d
bool(false)
Done

View file

@ -0,0 +1,33 @@
--TEST--
posix_ttyname(): casting stream does not emit data loss and should not emit warnings
--EXTENSIONS--
posix
--SKIPIF--
<?php
require __DIR__ . '/../../standard/tests/http/server.inc'; http_server_skipif();
?>
--INI--
allow_url_fopen=1
--FILE--
<?php
require __DIR__ . '/../../standard/tests/http/server.inc';
$responses = array(
"data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\nSome: Header\r\n\r\nBody",
);
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
/* Note: the warning is bogus in this case as no data actually gets lost,
* but this checks that stream casting works */
$handle = fopen($uri, 'r');
var_dump(posix_ttyname($handle));
var_dump(fread($handle, 20));
fclose($handle);
http_server_kill($pid);
?>
--EXPECT--
bool(false)
string(4) "Body"

View file

@ -0,0 +1,20 @@
--TEST--
posix_ttyname(): uncastable user stream
--EXTENSIONS--
posix
--FILE--
<?php
require dirname(__DIR__, 3) . '/tests/output/DummyStreamWrapper.inc';
stream_wrapper_register('custom', DummyStreamWrapper::class);
$fp = fopen("custom://myvar", "r+");
var_dump(posix_ttyname($fp));
fclose($fp);
echo "Done";
?>
--EXPECTF--
Warning: posix_ttyname(): Could not use stream of type 'user-space' in %s on line %d
bool(false)
Done

View file

@ -1645,10 +1645,14 @@ PHP_FUNCTION(stream_isatty)
php_stream_from_zval(stream, zsrc);
if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT, (void*)&fileno, 0);
} else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fileno, 0);
/* get the fd.
* NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting.
* It is only used here so that the buffered data warning is not displayed.
*/
if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0);
} else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0);
} else {
RETURN_FALSE;
}
@ -1686,13 +1690,15 @@ PHP_FUNCTION(sapi_windows_vt100_support)
php_stream_from_zval(stream, zsrc);
if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT, (void*)&fileno, 0);
}
else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fileno, 0);
}
else {
/* get the fd.
* NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting.
* It is only used here so that the buffered data warning is not displayed.
*/
if (php_stream_can_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0);
} else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) {
php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0);
} else {
if (!enable_is_null) {
php_error_docref(
NULL,

View file

@ -0,0 +1,37 @@
--TEST--
Casting a stream can lose data and needs to emit a warning
--SKIPIF--
<?php
if (PHP_OS_FAMILY === "Windows") die("skip non-Windows tests (popen cannot delete file as open in cmd.exe)");
?>
--FILE--
<?php
$tempnam = tempnam(sys_get_temp_dir(), 'test');
$stream = popen('echo 1; echo 2; rm ' . escapeshellarg($tempnam), 'r');
do {
usleep(10);
clearstatcache();
} while (file_exists($tempnam));
// fills the read buffer with up to 8192 bytes
fgets($stream);
// cast $stream and read fd until eof. Print each line that was read, prefixed with "proc open stdin:"
$process = proc_open(
'sed "s/^/proc open stdin:/"',
[
0 => $stream,
1 => ['pipe', 'w'],
],
$pipes,
);
var_dump(stream_get_contents($pipes[1]));
?>
--EXPECTF--
Warning: proc_open(): 2 bytes of buffered data lost during stream conversion! in %s on line %d
string(0) ""

View file

@ -0,0 +1,61 @@
<?php
class DummyStreamWrapper
{
/** @var resource|null */
public $context;
/** @var resource|null */
public $handle;
public function stream_cast(int $castAs)
{
return false;
}
public function stream_close(): void { }
public function stream_open(string $path, string $mode, int $options = 0, ?string &$openedPath = null): bool
{
return true;
}
public function stream_read(int $count)
{
return false;
}
public function stream_seek(int $offset, int $whence = SEEK_SET): bool
{
var_dump('stream_seek!');
return true;
}
public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
{
return false;
}
public function stream_stat()
{
return false;
}
public function stream_tell()
{
return 0;
}
public function stream_truncate(int $newSize): bool
{
return true;
}
public function stream_write(string $data) { }
public function unlink(string $path): bool
{
return false;
}
}

View file

@ -0,0 +1,34 @@
--TEST--
sapi_windows_vt100_support(): casting stream does not emit data loss and should not emit warnings
--SKIPIF--
<?php
if (PHP_OS_FAMILY !== "Windows") {
die("skip Only for Windows systems");
}
require __DIR__ . '/../../ext/standard/tests/http/server.inc'; http_server_skipif();
?>
--INI--
allow_url_fopen=1
--FILE--
<?php
require __DIR__ . '/../../ext/standard/tests/http/server.inc';
$responses = array(
"data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\nSome: Header\r\n\r\nBody",
);
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
/* Note: the warning is bogus in this case as no data actually gets lost,
* but this checks that stream casting works */
$handle = fopen($uri, 'r');
var_dump(sapi_windows_vt100_support($handle));
var_dump(fread($handle, 20));
fclose($handle);
http_server_kill($pid);
?>
--EXPECT--
bool(false)
string(4) "Body"

View file

@ -0,0 +1,27 @@
--TEST--
sapi_windows_vt100_support(): uncastable user stream
--SKIPIF--
<?php
if (PHP_OS_FAMILY !== "Windows") {
die("skip Only for Windows systems");
}
?>
--FILE--
<?php
require __DIR__ . '/DummyStreamWrapper.inc';
stream_wrapper_register('custom', DummyStreamWrapper::class);
$fp = fopen("custom://myvar", "r+");
try {
var_dump(sapi_windows_vt100_support($fp));
} catch (\TypeError $e) {
echo $e->getMessage(), PHP_EOL;
}
fclose($fp);
echo "Done";
?>
--EXPECT--
bool(false)
Done

View file

@ -0,0 +1,32 @@
--TEST--
stream_isatty(): casting stream does not emit data loss and should not emit warnings
Bug GH-10092 (Internal stream casting should not emit lost bytes warning twice)
--SKIPIF--
<?php
require __DIR__ . '/../../ext/standard/tests/http/server.inc'; http_server_skipif();
?>
--INI--
allow_url_fopen=1
--FILE--
<?php
require __DIR__ . '/../../ext/standard/tests/http/server.inc';
$responses = array(
"data://text/plain,HTTP/1.0 200 Ok\r\nSome: Header\r\nSome: Header\r\n\r\nBody",
);
['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
/* Note: the warning is bogus in this case as no data actually gets lost,
* but this checks that stream casting works */
$handle = fopen($uri, 'r');
var_dump(stream_isatty($handle));
var_dump(fread($handle, 20));
fclose($handle);
http_server_kill($pid);
?>
--EXPECT--
bool(false)
string(4) "Body"

View file

@ -0,0 +1,17 @@
--TEST--
stream_isatty(): uncastable user stream
--FILE--
<?php
require __DIR__ . '/DummyStreamWrapper.inc';
stream_wrapper_register('custom', DummyStreamWrapper::class);
$fp = fopen("custom://myvar", "r+");
var_dump(stream_isatty($fp));
fclose($fp);
echo "Done";
?>
--EXPECT--
bool(false)
Done