diff --git a/NEWS b/NEWS index 9aa95dab6985d..60fe2b91fe380 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,8 @@ PHP NEWS the exit value). (Laruence) . Fixed bug #51363 (Fatal error raised by var_export() not caught by error handler). (Lonny Kapelushnik) + . Fixed bug #40459 (Stat and Dir stream wrapper methods do not call + constructor). (Stas) - PDO: . Fixed bug #62685 (Wrong return datatype in PDO::inTransaction()). (Laruence) diff --git a/UPGRADING b/UPGRADING index f3a9c3ee8d1c5..68102b8447ea6 100755 --- a/UPGRADING +++ b/UPGRADING @@ -347,6 +347,9 @@ PHP 5.4 UPGRADE NOTES allows for toggling if the list of namespaces starts from the document root or from the node you call the method on +- Since 5.4.7, ctor is always called when new user stream wrapper object is created. + Before, it was called only when stream_open was called. + ============================== 5. Changes to existing classes ============================== diff --git a/ext/standard/tests/streams/bug40459.phpt b/ext/standard/tests/streams/bug40459.phpt new file mode 100644 index 0000000000000..8ee4363ed93bb --- /dev/null +++ b/ext/standard/tests/streams/bug40459.phpt @@ -0,0 +1,103 @@ +--TEST-- +bug 40459 - Test whether the constructor of the user-space stream wrapper is called when stream functions are called +--FILE-- +constructorCalled = true; + } + + function stream_open($path, $mode, $options, &$opened_path) + { + echo $this->constructorCalled ? 'yes' : 'no'; + return true; + } + + function url_stat($url, $flags) + { + echo $this->constructorCalled ? 'yes' : 'no'; + return array(); + } + + function unlink($url) + { + echo $this->constructorCalled ? 'yes' : 'no'; + } + + function rename($from, $to) + { + echo $this->constructorCalled ? 'yes' : 'no'; + } + + function mkdir($dir, $mode, $options) + { + echo $this->constructorCalled ? 'yes' : 'no'; + } + + function rmdir($dir, $options) + { + echo $this->constructorCalled ? 'yes' : 'no'; + } + + function dir_opendir($url, $options) + { + echo $this->constructorCalled ? 'yes' : 'no'; + return TRUE; + } + function stream_metadata() + { + echo $this->constructorCalled ? 'yes' : 'no'; + return TRUE; + } +} + +stream_wrapper_register('test', 'testwrapper', STREAM_IS_URL); + +echo 'stream_open: '; +fopen('test://test', 'r'); +echo "\n"; + +echo 'url_stat: '; +stat('test://test'); +echo "\n"; + +echo 'dir_opendir: '; +opendir('test://test'); +echo "\n"; + +echo 'rmdir: '; +rmdir('test://test'); +echo "\n"; + +echo 'mkdir: '; +mkdir('test://test'); +echo "\n"; + +echo 'rename: '; +rename('test://test', 'test://test2'); +echo "\n"; + +echo 'unlink: '; +unlink('test://test'); +echo "\n"; + +echo 'touch: '; +touch('test://test', time()); +echo "\n"; + + + +?> +==DONE== +--EXPECT-- +stream_open: yes +url_stat: yes +dir_opendir: yes +rmdir: yes +mkdir: yes +rename: yes +unlink: yes +touch: yes +==DONE== diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 96a5195ed324d..3b277160d6a42 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -281,43 +281,22 @@ typedef struct _php_userstream_data php_userstream_data_t; }}} **/ -static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +static zval *user_stream_create_object(struct php_user_stream_wrapper *uwrap, php_stream_context *context TSRMLS_DC) { - struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; - php_userstream_data_t *us; - zval *zfilename, *zmode, *zopened, *zoptions, *zretval = NULL, *zfuncname; - zval **args[4]; - int call_result; - php_stream *stream = NULL; - zend_bool old_in_user_include; - - /* Try to catch bad usage without preventing flexibility */ - if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented"); - return NULL; - } - FG(user_stream_current_filename) = filename; + zval *object; + /* create an instance of our class */ + ALLOC_ZVAL(object); + object_init_ex(object, uwrap->ce); + Z_SET_REFCOUNT_P(object, 1); + Z_SET_ISREF_P(object); - /* if the user stream was registered as local and we are in include context, - we add allow_url_include restrictions to allow_url_fopen ones */ - /* we need only is_url == 0 here since if is_url == 1 and remote wrappers - were restricted we wouldn't get here */ - old_in_user_include = PG(in_user_include); - if(uwrap->wrapper.is_url == 0 && - (options & STREAM_OPEN_FOR_INCLUDE) && - !PG(allow_url_include)) { - PG(in_user_include) = 1; + if (context) { + add_property_resource(object, "context", context->rsrc_id); + zend_list_addref(context->rsrc_id); + } else { + add_property_null(object, "context"); } - us = emalloc(sizeof(*us)); - us->wrapper = uwrap; - - /* create an instance of our class */ - ALLOC_ZVAL(us->object); - object_init_ex(us->object, uwrap->ce); - Z_SET_REFCOUNT_P(us->object, 1); - Z_SET_ISREF_P(us->object); - if (uwrap->ce->constructor) { zend_fcall_info fci; zend_fcall_info_cache fcc; @@ -327,7 +306,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena fci.function_table = &uwrap->ce->function_table; fci.function_name = NULL; fci.symbol_table = NULL; - fci.object_ptr = us->object; + fci.object_ptr = object; fci.retval_ptr_ptr = &retval_ptr; fci.param_count = 0; fci.params = NULL; @@ -336,16 +315,13 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena fcc.initialized = 1; fcc.function_handler = uwrap->ce->constructor; fcc.calling_scope = EG(scope); - fcc.called_scope = Z_OBJCE_P(us->object); - fcc.object_ptr = us->object; + fcc.called_scope = Z_OBJCE_P(object); + fcc.object_ptr = object; if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute %s::%s()", uwrap->ce->name, uwrap->ce->constructor->common.function_name); - zval_dtor(us->object); - FREE_ZVAL(us->object); - efree(us); - FG(user_stream_current_filename) = NULL; - PG(in_user_include) = old_in_user_include; + zval_dtor(object); + FREE_ZVAL(object); return NULL; } else { if (retval_ptr) { @@ -353,12 +329,46 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filena } } } + return object; +} - if (context) { - add_property_resource(us->object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(us->object, "context"); +static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, char *filename, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; + php_userstream_data_t *us; + zval *zfilename, *zmode, *zopened, *zoptions, *zretval = NULL, *zfuncname; + zval **args[4]; + int call_result; + php_stream *stream = NULL; + zend_bool old_in_user_include; + + /* Try to catch bad usage without preventing flexibility */ + if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "infinite recursion prevented"); + return NULL; + } + FG(user_stream_current_filename) = filename; + + /* if the user stream was registered as local and we are in include context, + we add allow_url_include restrictions to allow_url_fopen ones */ + /* we need only is_url == 0 here since if is_url == 1 and remote wrappers + were restricted we wouldn't get here */ + old_in_user_include = PG(in_user_include); + if(uwrap->wrapper.is_url == 0 && + (options & STREAM_OPEN_FOR_INCLUDE) && + !PG(allow_url_include)) { + PG(in_user_include) = 1; + } + + us = emalloc(sizeof(*us)); + us->wrapper = uwrap; + + us->object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(us->object == NULL) { + FG(user_stream_current_filename) = NULL; + PG(in_user_include) = old_in_user_include; + efree(us); + return NULL; } /* call it's stream_open method - set up params first */ @@ -447,17 +457,11 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, char *filen us = emalloc(sizeof(*us)); us->wrapper = uwrap; - /* create an instance of our class */ - ALLOC_ZVAL(us->object); - object_init_ex(us->object, uwrap->ce); - Z_SET_REFCOUNT_P(us->object, 1); - Z_SET_ISREF_P(us->object); - - if (context) { - add_property_resource(us->object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(us->object, "context"); + us->object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(us == NULL) { + FG(user_stream_current_filename) = NULL; + efree(us); + return NULL; } /* call it's dir_open method - set up params first */ @@ -1157,16 +1161,9 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int optio int ret = 0; /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; } /* call the unlink method */ @@ -1211,16 +1208,9 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char int ret = 0; /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; } /* call the rename method */ @@ -1270,16 +1260,9 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, char *url, int mode, int ret = 0; /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; } /* call the mkdir method */ @@ -1335,16 +1318,9 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int option int ret = 0; /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; } /* call the rmdir method */ @@ -1420,16 +1396,10 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, char *url, int opt } /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + zval_ptr_dtor(&zvalue); + return ret; } /* call the mkdir method */ @@ -1484,16 +1454,9 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, char *url, int fla int ret = -1; /* create an instance of our class */ - ALLOC_ZVAL(object); - object_init_ex(object, uwrap->ce); - Z_SET_REFCOUNT_P(object, 1); - Z_SET_ISREF_P(object); - - if (context) { - add_property_resource(object, "context", context->rsrc_id); - zend_list_addref(context->rsrc_id); - } else { - add_property_null(object, "context"); + object = user_stream_create_object(uwrap, context TSRMLS_CC); + if(object == NULL) { + return ret; } /* call it's stat_url method - set up params first */