diff --git a/Zend/tests/gh8548.phpt b/Zend/tests/gh8548.phpt index 7f194f84db2a1..89354f6e1810c 100644 --- a/Zend/tests/gh8548.phpt +++ b/Zend/tests/gh8548.phpt @@ -11,19 +11,35 @@ class Wrapper { return true; } + + public function stream_eof(): bool + { + return true; + } } function test() { if (!stream_wrapper_register('foo', \Wrapper::class)) { throw new \Exception('Could not register stream wrapper'); } + + $file = fopen('foo://bar', 'r'); + if (!stream_wrapper_unregister('foo')) { throw new \Exception('Could not unregister stream wrapper'); } + + $wrapper = stream_get_meta_data($file)['wrapper_data']; + if (!$wrapper instanceof Wrapper) { + throw new \Exception('Wrapper is not of expected type'); + } + + fclose($file); + unset($file); } // The first iterations will allocate space for things like the resource list -for ($i = 0; $i < 5; $i++) { +for ($i = 0; $i < 10; $i++) { test(); } diff --git a/Zend/tests/gh8548_2.phpt b/Zend/tests/gh8548_2.phpt new file mode 100644 index 0000000000000..75cc871d2abc9 --- /dev/null +++ b/Zend/tests/gh8548_2.phpt @@ -0,0 +1,56 @@ +--TEST-- +GH-8548: Test stream_wrapper_unregister() for directories +--FILE-- + +--EXPECT-- +bool(true) diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 08679d1a8bca0..f48f03b8e9746 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -42,6 +42,7 @@ struct php_user_stream_wrapper { }; static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC); +static int user_wrapper_close(php_stream_wrapper *wrapper, php_stream *stream); static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context); static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context); static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context); @@ -53,7 +54,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char static const php_stream_wrapper_ops user_stream_wops = { user_wrapper_opener, - NULL, /* close - the streams themselves know how */ + user_wrapper_close, NULL, /* stat - the streams themselves know how */ user_wrapper_stat_url, user_wrapper_opendir, @@ -375,6 +376,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * /* set wrapper data to be a reference to our object */ ZVAL_COPY(&stream->wrapperdata, &us->object); + + GC_ADDREF(us->wrapper->resource); } else { php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" call failed", ZSTR_VAL(us->wrapper->ce->name)); @@ -399,6 +402,14 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * return stream; } +static int user_wrapper_close(php_stream_wrapper *wrapper, php_stream *stream) +{ + struct php_user_stream_wrapper *uwrap = (struct php_user_stream_wrapper*)wrapper->abstract; + zend_list_delete(uwrap->resource); + // FIXME: Unused? + return 0; +} + static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC) { @@ -440,6 +451,8 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char /* set wrapper data to be a reference to our object */ ZVAL_COPY(&stream->wrapperdata, &us->object); + + GC_ADDREF(us->wrapper->resource); } else { php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed", ZSTR_VAL(us->wrapper->ce->name));