From 47ecadfa72c413eeb68280ec020947dcd4ef9bc5 Mon Sep 17 00:00:00 2001 From: George Peter Banyard Date: Sat, 10 Dec 2022 17:47:24 +0000 Subject: [PATCH] Add file_descriptor() function to retrieve the file descriptor of a PHP stream. --- ext/standard/basic_functions.stub.php | 3 + ext/standard/basic_functions_arginfo.h | 12 ++- ext/standard/file.c | 26 ++++++ .../tests/file/file_descriptor_basic.phpt | 26 ++++++ .../file_descriptor_non_castable_stream.phpt | 44 +++++++++++ ...e_descriptor_non_castable_user_stream.phpt | 79 +++++++++++++++++++ 6 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 ext/standard/tests/file/file_descriptor_basic.phpt create mode 100644 ext/standard/tests/file/file_descriptor_non_castable_stream.phpt create mode 100644 ext/standard/tests/file/file_descriptor_non_castable_user_stream.phpt diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index ab56a8c0e8fbb..774b2078fedf7 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -2642,6 +2642,9 @@ function fread($stream, int $length): string|false {} */ function fopen(string $filename, string $mode, bool $use_include_path = false, $context = null) {} +/** @param resource $stream */ +function file_descriptor($stream): int {} + /** * @param resource $stream * @return array|int|false|null diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 077f9876df7c2..37705b6d07e19 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 39d455982dfdea9d0b9b646bc207b05f7108d1b2 */ + * Stub hash: 3525b2e171019ad376db7667975b21a161c87147 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -1244,15 +1244,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_fopen, 0, 0, 2) ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_file_descriptor, 0, 1, IS_LONG, 0) + ZEND_ARG_INFO(0, stream) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_fscanf, 0, 2, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE|MAY_BE_NULL) ZEND_ARG_INFO(0, stream) ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) ZEND_ARG_VARIADIC_TYPE_INFO(1, vars, IS_MIXED, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fpassthru, 0, 1, IS_LONG, 0) - ZEND_ARG_INFO(0, stream) -ZEND_END_ARG_INFO() +#define arginfo_fpassthru arginfo_file_descriptor ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ftruncate, 0, 2, _IS_BOOL, 0) ZEND_ARG_INFO(0, stream) @@ -2542,6 +2544,7 @@ ZEND_FUNCTION(fgetc); ZEND_FUNCTION(fgets); ZEND_FUNCTION(fread); ZEND_FUNCTION(fopen); +ZEND_FUNCTION(file_descriptor); ZEND_FUNCTION(fscanf); ZEND_FUNCTION(fpassthru); ZEND_FUNCTION(ftruncate); @@ -3177,6 +3180,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(fgets, arginfo_fgets) ZEND_FE(fread, arginfo_fread) ZEND_FE(fopen, arginfo_fopen) + ZEND_FE(file_descriptor, arginfo_file_descriptor) ZEND_FE(fscanf, arginfo_fscanf) ZEND_FE(fpassthru, arginfo_fpassthru) ZEND_FE(ftruncate, arginfo_ftruncate) diff --git a/ext/standard/file.c b/ext/standard/file.c index 345536624d8fc..0e419f8fe8b97 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -864,6 +864,32 @@ PHP_FUNCTION(pclose) } /* }}} */ +PHP_FUNCTION(file_descriptor) +{ + zval *zsrc; + php_stream *stream; + php_socket_t fileno; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_RESOURCE(zsrc) + ZEND_PARSE_PARAMETERS_END(); + + php_stream_from_zval(stream, zsrc); + + /* TODO Should support streams that can be cast with PHP_STREAM_AS_FD_FOR_SELECT ? */ + /* 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 | PHP_STREAM_CAST_INTERNAL) == FAILURE) { + zend_argument_type_error(1, "cannot represent as a file descriptor"); + RETURN_THROWS(); + } + php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, true); + + RETURN_LONG(fileno); +} + /* {{{ Test for end-of-file on a file pointer */ PHPAPI PHP_FUNCTION(feof) { diff --git a/ext/standard/tests/file/file_descriptor_basic.phpt b/ext/standard/tests/file/file_descriptor_basic.phpt new file mode 100644 index 0000000000000..c294a225b251c --- /dev/null +++ b/ext/standard/tests/file/file_descriptor_basic.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test file_descriptor() function: basic functionality +--FILE-- + +--CLEAN-- + +--EXPECTF-- +*** Testing fileno() : basic functionality *** +int(%d) +int(1) +Done diff --git a/ext/standard/tests/file/file_descriptor_non_castable_stream.phpt b/ext/standard/tests/file/file_descriptor_non_castable_stream.phpt new file mode 100644 index 0000000000000..728ff49c42cdf --- /dev/null +++ b/ext/standard/tests/file/file_descriptor_non_castable_stream.phpt @@ -0,0 +1,44 @@ +--TEST-- +Test file_descriptor() function: retrieve file descriptor on TCP socket +--EXTENSIONS-- +pcntl +posix +--SKIPIF-- + +--INI-- +allow_url_fopen=1 +--FILE-- + $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'); +$fd = file_descriptor($handle); +var_dump($fd); +var_dump(fread($handle, 20)); +fclose($handle); + +$socket = stream_socket_client('tcp://example.com:80', timeout: 0.5); +$fds = file_descriptor($socket); +var_dump($fds); +fclose($socket); + +var_dump($fd == $fds); + +http_server_kill($pid); +?> +--EXPECTF-- +int(%d) +string(4) "Body" +int(%d) +bool(true) diff --git a/ext/standard/tests/file/file_descriptor_non_castable_user_stream.phpt b/ext/standard/tests/file/file_descriptor_non_castable_user_stream.phpt new file mode 100644 index 0000000000000..96d72ba380738 --- /dev/null +++ b/ext/standard/tests/file/file_descriptor_non_castable_user_stream.phpt @@ -0,0 +1,79 @@ +--TEST-- +Test file_descriptor() function: error on uncastable user stream +--FILE-- +getMessage(), PHP_EOL; +} +fclose($fp); + +echo "Done"; +?> +--EXPECT-- +file_descriptor(): Argument #1 ($stream) cannot represent as a file descriptor +Done