diff --git a/Zend/tests/exception_stream_wrapper.phpt b/Zend/tests/exception_stream_wrapper.phpt index 4de8274fe0c13..904a218c52e88 100644 --- a/Zend/tests/exception_stream_wrapper.phpt +++ b/Zend/tests/exception_stream_wrapper.phpt @@ -29,4 +29,5 @@ try { stream_set_option stream_stat stream_read +stream_close Message diff --git a/Zend/tests/gh19525.phpt b/Zend/tests/gh19525.phpt new file mode 100644 index 0000000000000..2871672c9381c --- /dev/null +++ b/Zend/tests/gh19525.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-19525: Stream wrapper stream_close() not called on exception unwind +--FILE-- + +--EXPECTF-- +Loader::stream_close + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): foo() +#1 {main} + thrown in %s on line %d diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 64aa58f776bf5..a14fee3a76a2b 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -32,6 +32,7 @@ # endif #endif #include "userspace_arginfo.h" +#include "zend_exceptions.h" static int le_protocols; @@ -680,7 +681,27 @@ static int php_userstreamop_close(php_stream *stream, int close_handle) assert(us != NULL); zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_CLOSE, false); + + /* Reset currently thrown exception during call of close method. */ + zend_object *old_exception = NULL; + const zend_op *old_opline_before_exception = NULL; + if (UNEXPECTED(EG(exception))) { + old_exception = EG(exception); + old_opline_before_exception = EG(opline_before_exception); + EG(exception) = NULL; + } + zend_call_method_if_exists(Z_OBJ(us->object), func_name, &retval, 0, NULL); + + if (UNEXPECTED(old_exception)) { + EG(opline_before_exception) = old_opline_before_exception; + if (EG(exception)) { + zend_exception_set_previous(EG(exception), old_exception); + } else { + EG(exception) = old_exception; + } + } + zend_string_release_ex(func_name, false); zval_ptr_dtor(&retval);