Browse Source

MFH: 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.
PECL
Arnaud Le Blanc 18 years ago
parent
commit
c6e1b8bc67
  1. 3
      NEWS
  2. 94
      ext/standard/tests/file/userstreams_002.phpt
  3. 153
      ext/standard/tests/file/userstreams_003.phpt
  4. 179
      main/streams/userspace.c

3
NEWS

@ -10,6 +10,9 @@ PHP NEWS
parameter validation. (Felipe)
- Changed openssl info to show the shared library version number. (Scott)
- 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. (Arnaud)
- Added header_remove() function. (chsc at peytz dot dk, Arnaud)
- Added stream_context_get_params() function. (Arnaud)
- Added optional parameter "new" to sybase_connect() (Timm)

94
ext/standard/tests/file/userstreams_002.phpt

@ -0,0 +1,94 @@
--TEST--
User-space streams: stream_cast()
--FILE--
<?php
class test_wrapper_base {
public $return_value;
function stream_open($path, $mode, $openedpath) {
return true;
}
function stream_eof() {
return false;
}
}
class test_wrapper extends test_wrapper_base {
function stream_cast($castas) {
return $this->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)

153
ext/standard/tests/file/userstreams_003.phpt

@ -0,0 +1,153 @@
--TEST--
User-space streams: stream_set_option()
--FILE--
<?php
class test_wrapper_base {
public $return_value;
public $expected_option;
public $expected_value;
function stream_open($path, $mode, $openedpath) {
return true;
}
function stream_eof() {
return false;
}
}
class test_wrapper extends test_wrapper_base {
function stream_set_option($option, $value, $ptrparam) {
echo "value:\n";
var_dump($value);
echo "ptrparam:\n";
var_dump($ptrparam);
echo "\$option === $option === " . $this->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)

179
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,
};

Loading…
Cancel
Save