diff --git a/ext/standard/io_poll.c b/ext/standard/io_poll.c index a8a0563627fa..336a02d79118 100644 --- a/ext/standard/io_poll.c +++ b/ext/standard/io_poll.c @@ -67,7 +67,6 @@ struct php_io_poll_context_object { /* Stream poll handle specific data */ typedef struct php_stream_poll_handle_data { - php_stream *stream; zend_resource *res; } php_stream_poll_handle_data; @@ -180,16 +179,29 @@ static const char *php_io_poll_backend_type_to_name(php_poll_backend_type type) /* Stream Poll Handle Implementation */ -static php_socket_t php_stream_poll_handle_get_fd(php_poll_handle_object *handle) +/* The stream is resolved from the resource on every use: fclose() frees the stream + * while the resource, which the handle holds a reference to, stays as a closed one. */ +static php_stream *php_stream_poll_handle_get_stream(php_poll_handle_object *handle) { php_stream_poll_handle_data *data = handle->handle_data; + + if (!data) { + return NULL; + } + + return zend_fetch_resource2(data->res, NULL, php_file_le_stream(), php_file_le_pstream()); +} + +static php_socket_t php_stream_poll_handle_get_fd(php_poll_handle_object *handle) +{ + php_stream *stream = php_stream_poll_handle_get_stream(handle); php_socket_t fd; - if (!data || !data->stream) { + if (!stream) { return SOCK_ERR; } - if (php_stream_cast(data->stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, + if (php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void *) &fd, 1) != SUCCESS || fd == -1) { @@ -201,8 +213,8 @@ static php_socket_t php_stream_poll_handle_get_fd(php_poll_handle_object *handle static int php_stream_poll_handle_is_valid(php_poll_handle_object *handle) { - php_stream_poll_handle_data *data = handle->handle_data; - return data && data->stream && !php_stream_eof(data->stream); + php_stream *stream = php_stream_poll_handle_get_stream(handle); + return stream && !php_stream_eof(stream); } static void php_stream_poll_handle_cleanup(php_poll_handle_object *handle) @@ -454,7 +466,6 @@ PHP_METHOD(StreamPollHandle, __construct) /* Set up stream-specific data */ php_stream_poll_handle_data *data = emalloc(sizeof(php_stream_poll_handle_data)); - data->stream = stream; data->res = stream->res; intern->handle_data = data; @@ -469,12 +480,12 @@ PHP_METHOD(StreamPollHandle, getStream) php_poll_handle_object *intern = PHP_POLL_HANDLE_OBJ_FROM_ZV(getThis()); php_stream_poll_handle_data *data = intern->handle_data; - if (!data || !data->stream) { + if (!data || !data->res) { RETURN_NULL(); } - GC_ADDREF(data->stream->res); - php_stream_to_zval(data->stream, return_value); + GC_ADDREF(data->res); + ZVAL_RES(return_value, data->res); } PHP_METHOD(StreamPollHandle, isValid) diff --git a/ext/standard/tests/poll/poll_stream_handle_closed_stream.phpt b/ext/standard/tests/poll/poll_stream_handle_closed_stream.phpt new file mode 100644 index 000000000000..56b5c08fadef --- /dev/null +++ b/ext/standard/tests/poll/poll_stream_handle_closed_stream.phpt @@ -0,0 +1,43 @@ +--TEST-- +Io\Poll: handle operations after the stream has been closed +--FILE-- +add($handle, [Io\Poll\Event::Read]); +$not_added_yet = new StreamPollHandle($w); + +fclose($r); +fclose($w); + +var_dump($handle->isValid()); +var_dump(get_debug_type($handle->getStream())); + +try { + $watcher->modifyEvents([Io\Poll\Event::Write]); +} catch (Io\Poll\InvalidHandleException $e) { + echo $e->getMessage(), "\n"; +} + +try { + $poll_ctx->add($not_added_yet, [Io\Poll\Event::Read]); +} catch (Io\Poll\InvalidHandleException $e) { + echo $e->getMessage(), "\n"; +} + +echo "Events count: ", count($poll_ctx->wait(Time\Duration::fromSeconds(0))), "\n"; + +$watcher->remove(); +var_dump($watcher->isActive()); +?> +--EXPECT-- +bool(false) +string(17) "resource (closed)" +Invalid handle for polling +Invalid handle for polling +Events count: 0 +bool(false)