From: Arnaud Le Blanc Date: Sat, 15 Nov 2008 12:50:17 +0000 (+0000) Subject: Added stream_cast() and stream_set_options() to user-space stream X-Git-Tag: BEFORE_HEAD_NS_CHANGES_MERGE~174 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e1fcaeef9dd7456563f4c30d49b18fa295b6a567;p=php Added stream_cast() and stream_set_options() to user-space stream wrappers, allowing stream_select(), stream_set_blocking(), stream_set_timeout() and stream_set_write_buffer() to work with user-space stream wrappers. Will document. --- diff --git a/ext/standard/tests/file/userstreams_002.phpt b/ext/standard/tests/file/userstreams_002.phpt new file mode 100644 index 0000000000..81f463ddd2 --- /dev/null +++ b/ext/standard/tests/file/userstreams_002.phpt @@ -0,0 +1,94 @@ +--TEST-- +User-space streams: stream_cast() +--FILE-- +return_value; + } +} +function test($name, $fd, $return_value) { + echo "\n------ $name: -------\n"; + $data = stream_get_meta_data($fd); + $data['wrapper_data']->return_value = $return_value; + $r = array($fd); + $w = $e = null; + var_dump(stream_select($r, $w, $e, 0) !== false); +} + +var_dump(stream_wrapper_register('test', 'test_wrapper')); +var_dump(stream_wrapper_register('test2', 'test_wrapper_base')); + +$fd = fopen("test://foo","r"); +$fd2 = fopen("test2://foo","r"); + +test("valid stream", $fd, STDIN); +test("stream_cast not implemented", $fd2, null); +test("return value is false", $fd, false); +test("return value not a stream resource", $fd, "foo"); +test("return value is stream itself", $fd, $fd); +test("return value cannot be casted", $fd, $fd2); + +?> +--EXPECTF-- +bool(true) +bool(true) + +------ valid stream: ------- +bool(true) + +------ stream_cast not implemented: ------- + +Warning: stream_select(): test_wrapper_base::stream_cast is not implemented! in %s + +Warning: stream_select(): cannot represent a stream of type user-space as a select()able descriptor in %s + +Warning: stream_select(): No stream arrays were passed in %s +bool(false) + +------ return value is false: ------- + +Warning: stream_select(): cannot represent a stream of type user-space as a select()able descriptor in %s + +Warning: stream_select(): No stream arrays were passed in %s +bool(false) + +------ return value not a stream resource: ------- + +Warning: stream_select(): supplied argument is not a valid stream resource in %s + +Warning: stream_select(): test_wrapper::stream_cast must return a stream resource in %s + +Warning: stream_select(): cannot represent a stream of type user-space as a select()able descriptor in %s + +Warning: stream_select(): No stream arrays were passed in %s +bool(false) + +------ return value is stream itself: ------- + +Warning: stream_select(): test_wrapper::stream_cast must not return itself in %s + +Warning: stream_select(): cannot represent a stream of type user-space as a select()able descriptor in %s + +Warning: stream_select(): No stream arrays were passed in %s +bool(false) + +------ return value cannot be casted: ------- + +Warning: stream_select(): test_wrapper_base::stream_cast is not implemented! in %s + +Warning: stream_select(): cannot represent a stream of type user-space as a select()able descriptor in %s + +Warning: stream_select(): cannot represent a stream of type user-space as a select()able descriptor in %s + +Warning: stream_select(): No stream arrays were passed in %s +bool(false) diff --git a/ext/standard/tests/file/userstreams_003.phpt b/ext/standard/tests/file/userstreams_003.phpt new file mode 100644 index 0000000000..b30636d205 --- /dev/null +++ b/ext/standard/tests/file/userstreams_003.phpt @@ -0,0 +1,153 @@ +--TEST-- +User-space streams: stream_set_option() +--FILE-- +expected_option . ":\n";; + var_dump($option === $this->expected_option); + echo "\$value === $value === " . $this->expected_value. ":\n";; + var_dump($value === $this->expected_value); + return $this->return_value; + } +} + +function test($name, $fd, $return_value, $func, $args, $expected_option, $expected_value) { + echo "\n------ $name: -------\n"; + $data = stream_get_meta_data($fd); + $data['wrapper_data']->return_value = $return_value; + $data['wrapper_data']->expected_option = $expected_option; + $data['wrapper_data']->expected_value = $expected_value; + var_dump(call_user_func_array($func, $args)); +} + +var_dump(stream_wrapper_register('test', 'test_wrapper')); +var_dump(stream_wrapper_register('test2', 'test_wrapper_base')); + +$fd = fopen("test://foo","r"); +$fd2 = fopen("test2://foo","r"); + +test("stream_set_blocking - 1", $fd, true, "stream_set_blocking", array($fd,0), STREAM_OPTION_BLOCKING, 0); +test("stream_set_blocking - 2", $fd, false, "stream_set_blocking", array($fd,1), STREAM_OPTION_BLOCKING, 1); +test("stream_set_blocking - 3", $fd, "foo", "stream_set_blocking", array($fd,0), STREAM_OPTION_BLOCKING, 0); +test("stream_set_blocking - 4", $fd2, true, "stream_set_blocking", array($fd2,1), STREAM_OPTION_BLOCKING, 1); + +test("stream_set_write_buffer - 1", $fd, true, "stream_set_write_buffer", array($fd,0), STREAM_OPTION_WRITE_BUFFER, STREAM_BUFFER_NONE); +test("stream_set_write_buffer - 2", $fd, true, "stream_set_write_buffer", array($fd,4096), STREAM_OPTION_WRITE_BUFFER, STREAM_BUFFER_FULL); +test("stream_set_write_buffer - 3", $fd, false, "stream_set_write_buffer", array($fd,8192), STREAM_OPTION_WRITE_BUFFER, STREAM_BUFFER_FULL); + +test("stream_set_timeout - 1", $fd, true, "stream_set_timeout", array($fd,10,11), STREAM_OPTION_READ_TIMEOUT, 10); +test("stream_set_timeout - 2", $fd, false, "stream_set_timeout", array($fd,11,12), STREAM_OPTION_READ_TIMEOUT, 11); + +?> +--EXPECTF-- +bool(true) +bool(true) + +------ stream_set_blocking - 1: ------- +value: +int(0) +ptrparam: +NULL +$option === 1 === 1: +bool(true) +$value === 0 === 0: +bool(true) +bool(true) + +------ stream_set_blocking - 2: ------- +value: +int(1) +ptrparam: +NULL +$option === 1 === 1: +bool(true) +$value === 1 === 1: +bool(true) +bool(false) + +------ stream_set_blocking - 3: ------- +value: +int(0) +ptrparam: +NULL +$option === 1 === 1: +bool(true) +$value === 0 === 0: +bool(true) +bool(true) + +------ stream_set_blocking - 4: ------- + +Warning: stream_set_blocking(): test_wrapper_base::stream_set_option is not implemented! in %s +bool(false) + +------ stream_set_write_buffer - 1: ------- +value: +int(0) +ptrparam: +int(8192) +$option === 3 === 3: +bool(true) +$value === 0 === 0: +bool(true) +int(0) + +------ stream_set_write_buffer - 2: ------- +value: +int(2) +ptrparam: +int(4096) +$option === 3 === 3: +bool(true) +$value === 2 === 2: +bool(true) +int(0) + +------ stream_set_write_buffer - 3: ------- +value: +int(2) +ptrparam: +int(8192) +$option === 3 === 3: +bool(true) +$value === 2 === 2: +bool(true) +int(-1) + +------ stream_set_timeout - 1: ------- +value: +int(10) +ptrparam: +int(11) +$option === 4 === 4: +bool(true) +$value === 10 === 10: +bool(true) +bool(true) + +------ stream_set_timeout - 2: ------- +value: +int(11) +ptrparam: +int(12) +$option === 4 === 4: +bool(true) +$value === 11 === 11: +bool(true) +bool(false) diff --git a/main/streams/userspace.c b/main/streams/userspace.c index fd3b70c673..a7c2da4e8f 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -82,6 +82,19 @@ PHP_MINIT_FUNCTION(user_streams) REGISTER_LONG_CONSTANT("STREAM_MKDIR_RECURSIVE", PHP_STREAM_MKDIR_RECURSIVE, CONST_CS|CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_IS_URL", PHP_STREAM_IS_URL, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("STREAM_OPTION_BLOCKING", PHP_STREAM_OPTION_BLOCKING, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_OPTION_READ_TIMEOUT", PHP_STREAM_OPTION_READ_TIMEOUT, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_OPTION_READ_BUFFER", PHP_STREAM_OPTION_READ_BUFFER, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_OPTION_WRITE_BUFFER", PHP_STREAM_OPTION_WRITE_BUFFER, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("STREAM_BUFFER_NONE", PHP_STREAM_BUFFER_NONE, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_BUFFER_LINE", PHP_STREAM_BUFFER_LINE, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_BUFFER_FULL", PHP_STREAM_BUFFER_FULL, CONST_CS|CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("STREAM_CAST_AS_STREAM", PHP_STREAM_AS_STDIO, CONST_CS|CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_CAST_FOR_SELECT", PHP_STREAM_AS_FD_FOR_SELECT, CONST_CS|CONST_PERSISTENT); + return SUCCESS; } @@ -111,6 +124,8 @@ typedef struct _php_userstream_data php_userstream_data_t; #define USERSTREAM_DIR_REWIND "dir_rewinddir" #define USERSTREAM_DIR_CLOSE "dir_closedir" #define USERSTREAM_LOCK "stream_lock" +#define USERSTREAM_CAST "stream_cast" +#define USERSTREAM_SET_OPTION "stream_set_option" /* {{{ class should have methods like these: @@ -160,6 +175,33 @@ typedef struct _php_userstream_data php_userstream_data_t; return array( just like that returned by fstat() ); } + function stream_cast($castas) + { + if ($castas == STREAM_CAST_FOR_SELECT) { + return $this->underlying_stream; + } + return false; + } + + function stream_set_option($option, $arg1, $arg2) + { + switch($option) { + case STREAM_OPTION_BLOCKING: + $blocking = $arg1; + ... + case STREAM_OPTION_READ_TIMEOUT: + $sec = $arg1; + $usec = $arg2; + ... + case STREAM_OPTION_WRITE_BUFFER: + $mode = $arg1; + $size = $arg2; + ... + default: + return false; + } + } + function url_stat(string $url, int $flags) { return array( just like that returned by stat() ); @@ -882,7 +924,7 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; int ret = -1; zval *zvalue = NULL; - zval **args[1]; + zval **args[3]; switch (option) { case PHP_STREAM_OPTION_CHECK_LIVENESS: @@ -925,6 +967,75 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value } break; + + case PHP_STREAM_OPTION_READ_BUFFER: + case PHP_STREAM_OPTION_WRITE_BUFFER: + case PHP_STREAM_OPTION_READ_TIMEOUT: + case PHP_STREAM_OPTION_BLOCKING: { + zval *zoption = NULL; + zval *zptrparam = NULL; + + ZVAL_STRINGL(&func_name, USERSTREAM_SET_OPTION, sizeof(USERSTREAM_SET_OPTION)-1, 0); + + ALLOC_INIT_ZVAL(zoption); + ZVAL_LONG(zoption, option); + + ALLOC_INIT_ZVAL(zvalue); + ALLOC_INIT_ZVAL(zptrparam); + + args[0] = &zoption; + args[1] = &zvalue; + args[2] = &zptrparam; + + switch(option) { + case PHP_STREAM_OPTION_READ_BUFFER: + case PHP_STREAM_OPTION_WRITE_BUFFER: + ZVAL_LONG(zvalue, value); + if (ptrparam) { + ZVAL_LONG(zptrparam, *(long *)ptrparam); + } else { + ZVAL_LONG(zptrparam, BUFSIZ); + } + break; + case PHP_STREAM_OPTION_READ_TIMEOUT: { + struct timeval tv = *(struct timeval*)ptrparam; + ZVAL_LONG(zvalue, tv.tv_sec); + ZVAL_LONG(zptrparam, tv.tv_usec); + break; + } + case PHP_STREAM_OPTION_BLOCKING: + ZVAL_LONG(zvalue, value); + break; + default: + break; + } + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 3, args, 0, NULL TSRMLS_CC); + + do { + if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_SET_OPTION " is not implemented!", + us->wrapper->classname); + break; + } + if (retval && zend_is_true(retval)) { + ret = PHP_STREAM_OPTION_RETURN_OK; + } + } while (0); + + if (zoption) { + zval_ptr_dtor(&zoption); + } + if (zptrparam) { + zval_ptr_dtor(&zptrparam); + } + + break; + } } /* clean up */ @@ -1331,12 +1442,76 @@ static int php_userstreamop_rewinddir(php_stream *stream, off_t offset, int when } +static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr TSRMLS_DC) +{ + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + zval func_name; + zval *retval = NULL; + zval *zcastas = NULL; + zval **args[1]; + php_stream * intstream = NULL; + int call_result; + int ret = FAILURE; + + ZVAL_STRINGL(&func_name, USERSTREAM_CAST, sizeof(USERSTREAM_CAST)-1, 0); + + ALLOC_INIT_ZVAL(zcastas); + switch(castas) { + case PHP_STREAM_AS_FD_FOR_SELECT: + ZVAL_LONG(zcastas, PHP_STREAM_AS_FD_FOR_SELECT); + break; + default: + ZVAL_LONG(zcastas, PHP_STREAM_AS_STDIO); + break; + } + args[0] = &zcastas; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, 0, NULL TSRMLS_CC); + + do { + if (call_result == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!", + us->wrapper->classname); + break; + } + if (retval == NULL || !zend_is_true(retval)) { + break; + } + php_stream_from_zval_no_verify(intstream, &retval); + if (!intstream) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource", + us->wrapper->classname); + break; + } + if (intstream == stream) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself", + us->wrapper->classname); + intstream = NULL; + break; + } + ret = php_stream_cast(intstream, castas, retptr, 1); + } while (0); + + if (retval) { + zval_ptr_dtor(&retval); + } + if (zcastas) { + zval_ptr_dtor(&zcastas); + } + + return ret; +} + php_stream_ops php_stream_userspace_ops = { php_userstreamop_write, php_userstreamop_read, php_userstreamop_close, php_userstreamop_flush, "user-space", php_userstreamop_seek, - NULL, /* cast */ + php_userstreamop_cast, php_userstreamop_stat, php_userstreamop_set_option, };