From: Wez Furlong Date: Tue, 19 Mar 2002 03:51:01 +0000 (+0000) Subject: Implement user-space streams. X-Git-Tag: php-4.3.0dev-ZendEngine2-Preview1~1242 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=06712a508a82965acbfa9a59eb8a55df11948acd;p=php Implement user-space streams. There's probably room for improvement, docs will following some time this week. --- diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index f77eda437c..a056db2fa0 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -624,6 +624,7 @@ function_entry basic_functions[] = { PHP_FE(socket_set_blocking, NULL) PHP_FE(fgetwrapperdata, NULL) + PHP_FE(file_register_wrapper, NULL) #if HAVE_SYS_TIME_H PHP_FE(socket_set_timeout, NULL) diff --git a/ext/standard/file.h b/ext/standard/file.h index a6929ad658..994ec7be87 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -71,6 +71,7 @@ PHP_NAMED_FUNCTION(php_if_ftruncate); PHP_NAMED_FUNCTION(php_if_fstat); PHP_FUNCTION(fgetwrapperdata); +PHP_FUNCTION(file_register_wrapper); PHPAPI int php_set_sock_blocking(int socketd, int block); PHPAPI int php_file_le_stream(void); diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index f204d1549f..6ef61b9e7b 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -79,13 +79,14 @@ static int php_get_ftp_result(php_stream *stream TSRMLS_DC) php_stream_wrapper php_stream_ftp_wrapper = { php_stream_url_wrap_ftp, + NULL, NULL }; /* {{{ php_fopen_url_wrap_ftp */ -php_stream * php_stream_url_wrap_ftp(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC) +php_stream * php_stream_url_wrap_ftp(char *path, char *mode, int options, char **opened_path, void *wrappercontext STREAMS_DC TSRMLS_DC) { php_stream *stream=NULL; php_url *resource=NULL; diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index aeaec330a8..06508b4c5a 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -71,7 +71,7 @@ #define HTTP_HEADER_BLOCK_SIZE 1024 -php_stream *php_stream_url_wrap_http(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC) +php_stream *php_stream_url_wrap_http(char *path, char *mode, int options, char **opened_path, void *wrappercontext STREAMS_DC TSRMLS_DC) { php_stream *stream = NULL; php_url *resource = NULL; @@ -268,7 +268,7 @@ php_stream *php_stream_url_wrap_http(char *path, char *mode, int options, char * else { strlcpy(new_path, location, sizeof(new_path)); } - stream = php_stream_url_wrap_http(new_path, mode, options, opened_path STREAMS_CC TSRMLS_CC); + stream = php_stream_url_wrap_http(new_path, mode, options, opened_path, NULL STREAMS_CC TSRMLS_CC); if (stream->wrapperdata) { entryp = &entry; MAKE_STD_ZVAL(entry); @@ -311,6 +311,7 @@ out: php_stream_wrapper php_stream_http_wrapper = { php_stream_url_wrap_http, + NULL, NULL }; diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index cd1929c768..8677486c52 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -30,7 +30,7 @@ #include "php_standard.h" #include "php_fopen_wrappers.h" -php_stream * php_stream_url_wrap_php(char * path, char * mode, int options, char ** opened_path STREAMS_DC TSRMLS_DC) +php_stream * php_stream_url_wrap_php(char * path, char * mode, int options, char ** opened_path, void *wrappercontext STREAMS_DC TSRMLS_DC) { FILE * fp = NULL; php_stream * stream = NULL; @@ -54,6 +54,7 @@ php_stream * php_stream_url_wrap_php(char * path, char * mode, int options, char php_stream_wrapper php_stream_php_wrapper = { php_stream_url_wrap_php, + NULL, NULL }; diff --git a/ext/standard/php_fopen_wrappers.h b/ext/standard/php_fopen_wrappers.h index 3d23975b44..5178d74ce1 100644 --- a/ext/standard/php_fopen_wrappers.h +++ b/ext/standard/php_fopen_wrappers.h @@ -23,8 +23,8 @@ #ifndef PHP_FOPEN_WRAPPERS_H #define PHP_FOPEN_WRAPPERS_H -php_stream *php_stream_url_wrap_http(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC); -php_stream *php_stream_url_wrap_ftp(char *path, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC); +php_stream *php_stream_url_wrap_http(char *path, char *mode, int options, char **opened_path, void *wrappercontext STREAMS_DC TSRMLS_DC); +php_stream *php_stream_url_wrap_ftp(char *path, char *mode, int options, char **opened_path, void *wrappercontext STREAMS_DC TSRMLS_DC); php_stream_wrapper php_stream_http_wrapper; php_stream_wrapper php_stream_ftp_wrapper; php_stream_wrapper php_stream_php_wrapper; diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 69060165bf..76aafb3ee6 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -237,9 +237,7 @@ PHP_MINFO_FUNCTION(zlib) { php_info_print_table_start(); php_info_print_table_row(2, "ZLib Support", "enabled"); -#if HAVE_FOPENCOOKIE php_info_print_table_row(2, "'zlib:' fopen wrapper", "enabled"); -#endif php_info_print_table_row(2, "Compiled Version", ZLIB_VERSION ); php_info_print_table_row(2, "Linked Version", (char *)zlibVersion() ); php_info_print_table_end(); diff --git a/main/php_streams.h b/main/php_streams.h index 28f9e89794..4a46902536 100755 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -101,12 +101,13 @@ typedef struct _php_stream_ops { } php_stream_ops; /* options uses the IGNORE_URL family of defines from fopen_wrappers.h */ -typedef php_stream *(*php_stream_factory_func_t)(char *filename, char *mode, int options, char **opened_path STREAMS_DC TSRMLS_DC); +typedef php_stream *(*php_stream_factory_func_t)(char *filename, char *mode, int options, char **opened_path, void * wrappercontext STREAMS_DC TSRMLS_DC); typedef void (*php_stream_wrapper_dtor_func_t)(php_stream *stream TSRMLS_DC); typedef struct _php_stream_wrapper { php_stream_factory_func_t create; php_stream_wrapper_dtor_func_t destroy; + void * wrappercontext; } php_stream_wrapper; struct _php_stream { @@ -253,6 +254,7 @@ PHPAPI int _php_stream_cast(php_stream *stream, int castas, void **ret, int show # define IGNORE_URL_WIN 0 #endif +int php_init_user_streams(TSRMLS_D); int php_init_stream_wrappers(TSRMLS_D); int php_shutdown_stream_wrappers(TSRMLS_D); PHPAPI int php_register_url_stream_wrapper(char *protocol, php_stream_wrapper *wrapper TSRMLS_DC); @@ -268,6 +270,11 @@ PHPAPI php_stream *_php_stream_open_wrapper(char *path, char *mode, int options, PHPAPI int _php_stream_make_seekable(php_stream *origstream, php_stream **newstream STREAMS_DC TSRMLS_DC); #define php_stream_make_seekable(origstream, newstream) _php_stream_make_seekable(origstream, newstream STREAMS_CC TSRMLS_CC) + +/* for user-space streams */ +extern php_stream_ops php_stream_userspace_ops; +#define PHP_STREAM_IS_USERSPACE &php_stream_userspace_ops + #endif /* diff --git a/main/streams.c b/main/streams.c index a448fa2a99..a4f3d6ee21 100755 --- a/main/streams.c +++ b/main/streams.c @@ -891,7 +891,9 @@ exit_success: int php_init_stream_wrappers(TSRMLS_D) { if (PG(allow_url_fopen)) { - return zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1); + if (zend_hash_init(&url_stream_wrappers_hash, 0, NULL, NULL, 1) == SUCCESS) + return php_init_user_streams(TSRMLS_C); + return FAILURE; } return SUCCESS; } @@ -940,7 +942,7 @@ static php_stream *php_stream_open_url(char *path, char *mode, int options, char protocol = NULL; } if (wrapper) { - php_stream *stream = wrapper->create(path, mode, options, opened_path STREAMS_REL_CC TSRMLS_CC); + php_stream *stream = wrapper->create(path, mode, options, opened_path, wrapper->wrappercontext STREAMS_REL_CC TSRMLS_CC); if (stream) stream->wrapper = wrapper; return stream; diff --git a/main/user_streams.c b/main/user_streams.c new file mode 100644 index 0000000000..164639e1fd --- /dev/null +++ b/main/user_streams.c @@ -0,0 +1,484 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2002 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: | + | Wez Furlong (wez@thebrainroom.com) | + +----------------------------------------------------------------------+ + */ + +#include "php.h" +#include "php_globals.h" + +static int le_protocols; + +struct php_user_stream_wrapper { + char * protoname; + char * classname; + zend_class_entry *ce; + php_stream_wrapper wrapper; +}; + +static php_stream *user_wrapper_factory(char *filename, char *mode, int options, char **opened_path, void * wrappercontext STREAMS_DC TSRMLS_DC); + +static void stream_wrapper_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +{ + struct php_user_stream_wrapper * uwrap = (struct php_user_stream_wrapper*)rsrc->ptr; + + php_unregister_url_stream_wrapper(uwrap->protoname TSRMLS_CC); + efree(uwrap->protoname); + efree(uwrap->classname); + efree(uwrap); +} + +int php_init_user_streams(TSRMLS_D) +{ + le_protocols = zend_register_list_destructors_ex(stream_wrapper_dtor, NULL, "stream factory", 0); + return le_protocols == FAILURE ? FAILURE : SUCCESS; +} + +struct _php_userstream_data { + struct php_user_stream_wrapper * wrapper; + zval * object; +}; +typedef struct _php_userstream_data php_userstream_data_t; + +/* names of methods */ +#define USERSTREAM_OPEN "stream_open" +#define USERSTREAM_CLOSE "stream_close" +#define USERSTREAM_READ "stream_read" +#define USERSTREAM_WRITE "stream_write" +#define USERSTREAM_FLUSH "stream_flush" +#define USERSTREAM_SEEK "stream_seek" +#define USERSTREAM_GETS "stream_gets" +#define USERSTREAM_TELL "stream_tell" +#define USERSTREAM_EOF "stream_eof" + +/* class should have methods like these: + +function stream_open($path, $mode, $options, &$opened_path) + { + return true/false; + } + function stream_read($count) + { + return false on error; + else return string; + } + function stream_write($data) + { + return false on error; + else return count written; + } + function stream_close() + { + } + function stream_flush() + { + } + function stream_seek($offset, $whence) + { + } + function stream_gets($size) + { + return false on error; + else return string; + } + + * */ + +static php_stream *user_wrapper_factory(char *filename, char *mode, int options, char **opened_path, void *wrappercontext STREAMS_DC TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrappercontext; + php_userstream_data_t *us; + zval *zfilename, *zmode, *zopened, *zoptions, *zretval = NULL, *zfuncname; + zval **args[4]; + int call_result; + php_stream *stream = NULL; + + us = emalloc(sizeof(*us)); + us->wrapper = uwrap; + + /* create an instance of our class */ + ALLOC_ZVAL(us->object); + object_init_ex(us->object, uwrap->ce); + ZVAL_REFCOUNT(us->object) = 1; + PZVAL_IS_REF(us->object) = 1; + + /* call it's stream_open method - set up params first */ + MAKE_STD_ZVAL(zfilename); + ZVAL_STRING(zfilename, filename, 1); + args[0] = &zfilename; + + MAKE_STD_ZVAL(zmode); + ZVAL_STRING(zmode, mode, 1); + args[1] = &zmode; + + MAKE_STD_ZVAL(zoptions); + ZVAL_LONG(zoptions, options); + args[2] = &zoptions; + + MAKE_STD_ZVAL(zopened); + ZVAL_REFCOUNT(zopened) = 1; + PZVAL_IS_REF(zopened) = 1; + ZVAL_NULL(zopened); + args[3] = &zopened; + + MAKE_STD_ZVAL(zfuncname); + ZVAL_STRING(zfuncname, USERSTREAM_OPEN, 1); + + call_result = call_user_function_ex(NULL, + &us->object, + zfuncname, + &zretval, + 4, args, + 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && zretval != NULL) { + /* the stream is now open! */ + stream = php_stream_alloc_rel(&php_stream_userspace_ops, us, 0, mode); + + /* if the opened path is set, copy it out */ + if (Z_TYPE_P(zopened) == IS_STRING && opened_path) { + *opened_path = estrndup(Z_STRVAL_P(zopened), Z_STRLEN_P(zopened)); + } + } else { + /* destroy the object */ + zval_ptr_dtor(&us->object); + efree(us); + } + + /* destroy everything else */ + if (zretval) + zval_ptr_dtor(&zretval); + + zval_ptr_dtor(&zfuncname); + zval_ptr_dtor(&zopened); + zval_ptr_dtor(&zoptions); + zval_ptr_dtor(&zmode); + zval_ptr_dtor(&zfilename); + + return stream; +} + +/* {{{ proto bool file_register_wrapper(string protocol, string classname) + Registers a custom URL protocol handler class */ +PHP_FUNCTION(file_register_wrapper) +{ + char *protocol, *classname; + int protocol_len, classname_len; + struct php_user_stream_wrapper * uwrap; + int rsrc_id; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &protocol, &protocol_len, &classname, &classname_len) == FAILURE) { + RETURN_FALSE; + } + + if (!PG(allow_url_fopen)) { + zend_error(E_WARNING, "%s(): fopen wrappers have been disabled", get_active_function_name(TSRMLS_C)); + RETURN_FALSE; + } + + uwrap = (struct php_user_stream_wrapper *)ecalloc(1, sizeof(*uwrap)); + uwrap->protoname = estrndup(protocol, protocol_len); + uwrap->classname = estrndup(classname, classname_len); + uwrap->wrapper.create = user_wrapper_factory; + uwrap->wrapper.wrappercontext = uwrap; + + zend_str_tolower(uwrap->classname, classname_len); + rsrc_id = ZEND_REGISTER_RESOURCE(NULL, uwrap, le_protocols); + + if (zend_hash_find(EG(class_table), uwrap->classname, classname_len + 1, (void**)&uwrap->ce) == SUCCESS) { +#ifdef ZEND_ENGINE_2 + uwrap->ce = *(zend_class_entry**)uwrap->ce; +#endif + if (php_register_url_stream_wrapper(protocol, &uwrap->wrapper TSRMLS_CC) == SUCCESS) { + RETURN_TRUE; + } + } else { + zend_error(E_WARNING, "%s(): class '%s' is undefined", get_active_function_name(TSRMLS_C), + classname); + } + + zend_list_delete(rsrc_id); + RETURN_FALSE; +} +/* }}} */ + + +static size_t php_userstreamop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + zval **args[1]; + zval zbuff, *zbufptr; + size_t didwrite = 0; + + assert(us != NULL); + + ZVAL_STRINGL(&func_name, USERSTREAM_WRITE, sizeof(USERSTREAM_WRITE)-1, 0); + + ZVAL_STRINGL(&zbuff, (char*)buf, count, 0); + zbufptr = &zbuff; + args[0] = &zbufptr; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, + 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG) + didwrite = Z_LVAL_P(retval); + else + didwrite = 0; + + if (retval) + zval_ptr_dtor(&retval); + + return didwrite; +} + +static size_t php_userstreamop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + zval **args[1]; + int call_result; + size_t didread = 0; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + if (buf == NULL && count == 0) { + ZVAL_STRINGL(&func_name, USERSTREAM_EOF, sizeof(USERSTREAM_EOF)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) + didread = 0; + else + didread = EOF; + + } else { + zval *zcount; + + ZVAL_STRINGL(&func_name, USERSTREAM_READ, sizeof(USERSTREAM_READ)-1, 0); + + MAKE_STD_ZVAL(zcount); + ZVAL_LONG(zcount, count); + args[0] = &zcount; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, + 0, NULL TSRMLS_CC); + + if (retval && Z_TYPE_P(retval) == IS_STRING) { + didread = Z_STRLEN_P(retval); + if (didread > count) { + zend_error(E_WARNING, "%s::" USERSTREAM_READ " - read more data than requested; some data will be lost", + us->wrapper->classname); + didread = count; + } + if (didread > 0) + memcpy(buf, Z_STRVAL_P(retval), didread); + } + + zval_ptr_dtor(&zcount); + } + + if (retval) + zval_ptr_dtor(&retval); + + return didread; +} + +static int php_userstreamop_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + ZVAL_STRINGL(&func_name, USERSTREAM_CLOSE, sizeof(USERSTREAM_CLOSE)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (retval) + zval_ptr_dtor(&retval); + + zval_ptr_dtor(&us->object); + + efree(us); + + return 0; +} + +static int php_userstreamop_flush(php_stream *stream TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + ZVAL_STRINGL(&func_name, USERSTREAM_FLUSH, sizeof(USERSTREAM_FLUSH)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + if (call_result == SUCCESS && retval != NULL && zval_is_true(retval)) + call_result = 0; + else + call_result = -1; + + if (retval) + zval_ptr_dtor(&retval); + + return call_result; +} + +static int php_userstreamop_seek(php_stream *stream, off_t offset, int whence TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + int call_result; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + if (offset == 0 && whence == SEEK_CUR) { + ZVAL_STRINGL(&func_name, USERSTREAM_TELL, sizeof(USERSTREAM_TELL)-1, 0); + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 0, NULL, 0, NULL TSRMLS_CC); + + + } else { + zval **args[2]; + zval *zoffs, *zwhence; + + ZVAL_STRINGL(&func_name, USERSTREAM_SEEK, sizeof(USERSTREAM_SEEK)-1, 0); + + MAKE_STD_ZVAL(zoffs); + ZVAL_LONG(zoffs, offset); + args[0] = &zoffs; + + MAKE_STD_ZVAL(zwhence); + ZVAL_LONG(zwhence, whence); + args[1] = &zwhence; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 2, args, + 0, NULL TSRMLS_CC); + + zval_ptr_dtor(&zoffs); + zval_ptr_dtor(&zwhence); + + } + + if (call_result == SUCCESS && retval != NULL && Z_TYPE_P(retval) == IS_LONG) + call_result = Z_LVAL_P(retval); + else + call_result = -1; + + if (retval) + zval_ptr_dtor(&retval); + + return 0; +} + +static char *php_userstreamop_gets(php_stream *stream, char *buf, size_t size TSRMLS_DC) +{ + zval func_name; + zval *retval = NULL; + zval *zcount; + zval **args[2]; + int call_result; + size_t didread = 0; + php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract; + + assert(us != NULL); + + /* TODO: when the gets method is not available, fall back on emulated version using read */ + + ZVAL_STRINGL(&func_name, USERSTREAM_GETS, sizeof(USERSTREAM_GETS)-1, 0); + + MAKE_STD_ZVAL(zcount); + ZVAL_LONG(zcount, size); + args[0] = &zcount; + + call_result = call_user_function_ex(NULL, + &us->object, + &func_name, + &retval, + 1, args, + 0, NULL TSRMLS_CC); + + if (retval && Z_TYPE_P(retval) == IS_STRING) { + didread = Z_STRLEN_P(retval); + if (didread > size) { + zend_error(E_WARNING, "%s::" USERSTREAM_GETS " - read more data than requested; some data will be lost", + us->wrapper->classname); + didread = size; + } + if (didread > 0) + memcpy(buf, Z_STRVAL_P(retval), didread); + + zval_ptr_dtor(&retval); + } + + if (retval) + zval_ptr_dtor(&zcount); + + if (didread) + return buf; + + return 0; +} + +php_stream_ops php_stream_userspace_ops = { + php_userstreamop_write, php_userstreamop_read, + php_userstreamop_close, php_userstreamop_flush, + php_userstreamop_seek, php_userstreamop_gets, + NULL, /* cast */ + "user-space" +}; + +