From ec8b016d08dbe7582102a7baf36c015cd1c5642b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 2 Jul 2025 13:57:50 +0200 Subject: [PATCH 1/5] uri: Do not create new `UrlValidationErrorType` objects (#19009) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `zend_enum_new()` is not intended to be used “at runtime”, since it will create a new object, breaking the singleton property. Instead `zend_enum_get_case_cstr()` must be used. --- NEWS | 4 +++ ext/uri/php_lexbor.c | 76 ++++++++++++++++++++---------------------- ext/uri/tests/054.phpt | 18 ++++++++++ 3 files changed, 59 insertions(+), 39 deletions(-) create mode 100644 ext/uri/tests/054.phpt diff --git a/NEWS b/NEWS index 3646c48404f81..391593dc0c965 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.5.0alpha2 +- URI: + . Return the singleton UrlValidationErrorType instances from Uri\WhatWg\Url + instead of creating new objects that are different from the singleton. + (timwolla) 03 Jul 2025, PHP 8.5.0alpha1 diff --git a/ext/uri/php_lexbor.c b/ext/uri/php_lexbor.c index 44bca30f8fda7..39b0fb7d09ce3 100644 --- a/ext/uri/php_lexbor.c +++ b/ext/uri/php_lexbor.c @@ -73,7 +73,7 @@ static void lexbor_cleanup_parser(void) * When errors is NULL, the caller is not interested in the additional error information, * so the function does nothing. */ -static zend_string *fill_errors(zval *errors) +static const char *fill_errors(zval *errors) { if (errors == NULL) { return NULL; @@ -87,140 +87,138 @@ static zend_string *fill_errors(zval *errors) return NULL; } - zend_string *result = NULL; + const char *result = NULL; lexbor_plog_entry_t *lxb_error; while ((lxb_error = lexbor_array_obj_pop(&lexbor_parser.log->list)) != NULL) { zval error; object_init_ex(&error, uri_whatwg_url_validation_error_ce); zend_update_property_string(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("context"), (const char *) lxb_error->data); - zend_string *error_str; + const char *error_str; zval failure; switch (lxb_error->id) { case LXB_URL_ERROR_TYPE_DOMAIN_TO_ASCII: - error_str = ZSTR_INIT_LITERAL("DomainToAscii", false); + error_str = "DomainToAscii"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_DOMAIN_TO_UNICODE: - error_str = ZSTR_INIT_LITERAL("DomainToUnicode", false); + error_str = "DomainToUnicode"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_DOMAIN_INVALID_CODE_POINT: - error_str = ZSTR_INIT_LITERAL("DomainInvalidCodePoint", false); + error_str = "DomainInvalidCodePoint"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_HOST_INVALID_CODE_POINT: - error_str = ZSTR_INIT_LITERAL("HostInvalidCodePoint", false); + error_str = "HostInvalidCodePoint"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_EMPTY_PART: - error_str = ZSTR_INIT_LITERAL("Ipv4EmptyPart", false); + error_str = "Ipv4EmptyPart"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_TOO_MANY_PARTS: - error_str = ZSTR_INIT_LITERAL("Ipv4TooManyParts", false); + error_str = "Ipv4TooManyParts"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_NON_NUMERIC_PART: - error_str = ZSTR_INIT_LITERAL("Ipv4NonNumericPart", false); + error_str = "Ipv4NonNumericPart"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_NON_DECIMAL_PART: - error_str = ZSTR_INIT_LITERAL("Ipv4NonDecimalPart", false); + error_str = "Ipv4NonDecimalPart"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_OUT_OF_RANGE_PART: - error_str = ZSTR_INIT_LITERAL("Ipv4OutOfRangePart", false); + error_str = "Ipv4OutOfRangePart"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_UNCLOSED: - error_str = ZSTR_INIT_LITERAL("Ipv6Unclosed", false); + error_str = "Ipv6Unclosed"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_INVALID_COMPRESSION: - error_str = ZSTR_INIT_LITERAL("Ipv6InvalidCompression", false); + error_str = "Ipv6InvalidCompression"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_TOO_MANY_PIECES: - error_str = ZSTR_INIT_LITERAL("Ipv6TooManyPieces", false); + error_str = "Ipv6TooManyPieces"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_MULTIPLE_COMPRESSION: - error_str = ZSTR_INIT_LITERAL("Ipv6MultipleCompression", false); + error_str = "Ipv6MultipleCompression"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_INVALID_CODE_POINT: - error_str = ZSTR_INIT_LITERAL("Ipv6InvalidCodePoint", false); + error_str = "Ipv6InvalidCodePoint"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV6_TOO_FEW_PIECES: - error_str = ZSTR_INIT_LITERAL("Ipv6TooFewPieces", false); + error_str = "Ipv6TooFewPieces"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_MANY_PIECES: - error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6TooManyPieces", false); + error_str = "Ipv4InIpv6TooManyPieces"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_INVALID_CODE_POINT: - error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6InvalidCodePoint", false); + error_str = "Ipv4InIpv6InvalidCodePoint"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_OUT_OF_RANGE_PART: - error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6OutOfRangePart", false); + error_str = "Ipv4InIpv6OutOfRangePart"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_IPV4_IN_IPV6_TOO_FEW_PARTS: - error_str = ZSTR_INIT_LITERAL("Ipv4InIpv6TooFewParts", false); + error_str = "Ipv4InIpv6TooFewParts"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_INVALID_URL_UNIT: - error_str = ZSTR_INIT_LITERAL("InvalidUrlUnit", false); + error_str = "InvalidUrlUnit"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_SPECIAL_SCHEME_MISSING_FOLLOWING_SOLIDUS: - error_str = ZSTR_INIT_LITERAL("SpecialSchemeMissingFollowingSolidus", false); + error_str = "SpecialSchemeMissingFollowingSolidus"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_MISSING_SCHEME_NON_RELATIVE_URL: - error_str = ZSTR_INIT_LITERAL("MissingSchemeNonRelativeUrl", false); + error_str = "MissingSchemeNonRelativeUrl"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_INVALID_REVERSE_SOLIDUS: - error_str = ZSTR_INIT_LITERAL("InvalidReverseSoldius", false); + error_str = "InvalidReverseSoldius"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_INVALID_CREDENTIALS: - error_str = ZSTR_INIT_LITERAL("InvalidCredentials", false); + error_str = "InvalidCredentials"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_HOST_MISSING: - error_str = ZSTR_INIT_LITERAL("HostMissing", false); + error_str = "HostMissing"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_PORT_OUT_OF_RANGE: - error_str = ZSTR_INIT_LITERAL("PortOutOfRange", false); + error_str = "PortOutOfRange"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_PORT_INVALID: - error_str = ZSTR_INIT_LITERAL("PortInvalid", false); + error_str = "PortInvalid"; ZVAL_TRUE(&failure); break; case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER: - error_str = ZSTR_INIT_LITERAL("FileInvalidWindowsDriveLetter", false); + error_str = "FileInvalidWindowsDriveLetter"; ZVAL_FALSE(&failure); break; case LXB_URL_ERROR_TYPE_FILE_INVALID_WINDOWS_DRIVE_LETTER_HOST: - error_str = ZSTR_INIT_LITERAL("FileInvalidWindowsDriveLetterHost", false); + error_str = "FileInvalidWindowsDriveLetterHost"; ZVAL_FALSE(&failure); break; EMPTY_SWITCH_DEFAULT_CASE() } zval error_type; - zend_enum_new(&error_type, uri_whatwg_url_validation_error_type_ce, error_str, NULL); + ZVAL_OBJ(&error_type, zend_enum_get_case_cstr(uri_whatwg_url_validation_error_type_ce, error_str)); zend_update_property_ex(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZSTR_KNOWN(ZEND_STR_TYPE), &error_type); - zend_string_release_ex(error_str, false); - zval_ptr_dtor(&error_type); zend_update_property(uri_whatwg_url_validation_error_ce, Z_OBJ(error), ZEND_STRL("failure"), &failure); @@ -236,14 +234,14 @@ static zend_string *fill_errors(zval *errors) static void throw_invalid_url_exception_during_write(zval *errors, const char *component) { - zend_string *reason = fill_errors(errors); + const char *reason = fill_errors(errors); zend_object *exception = zend_throw_exception_ex( uri_whatwg_invalid_url_exception_ce, 0, "The specified %s is malformed%s%s%s", component, reason ? " (" : "", - reason ? ZSTR_VAL(reason) : "", + reason ? reason : "", reason ? ")" : "" ); zend_update_property(exception->ce, exception, ZEND_STRL("errors"), errors); @@ -567,10 +565,10 @@ lxb_url_t *lexbor_parse_uri_ex(const zend_string *uri_str, const lxb_url_t *lexb lexbor_cleanup_parser(); lxb_url_t *url = lxb_url_parse(&lexbor_parser, lexbor_base_url, (unsigned char *) ZSTR_VAL(uri_str), ZSTR_LEN(uri_str)); - zend_string *reason = fill_errors(errors); + const char *reason = fill_errors(errors); if (url == NULL && !silent) { - zend_object *exception = zend_throw_exception_ex(uri_whatwg_invalid_url_exception_ce, 0, "The specified URI is malformed%s%s%s", reason ? " (" : "", reason ? ZSTR_VAL(reason) : "", reason ? ")" : ""); + zend_object *exception = zend_throw_exception_ex(uri_whatwg_invalid_url_exception_ce, 0, "The specified URI is malformed%s%s%s", reason ? " (" : "", reason ? reason : "", reason ? ")" : ""); zend_update_property(exception->ce, exception, ZEND_STRL("errors"), errors); } diff --git a/ext/uri/tests/054.phpt b/ext/uri/tests/054.phpt new file mode 100644 index 0000000000000..562ba981a12a6 --- /dev/null +++ b/ext/uri/tests/054.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test UrlValidationErrorType singleton +--EXTENSIONS-- +uri +--FILE-- +getMessage() . "\n"; + var_dump($e->errors[0]->type === \Uri\WhatWg\UrlValidationErrorType::PortOutOfRange); +} + +?> +--EXPECT-- +The specified URI is malformed (PortOutOfRange) +bool(true) From 1a4dfd56583588181319667d8d98b08e96fb5cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 2 Jul 2025 13:58:05 +0200 Subject: [PATCH 2/5] random: Fix error message formatting for `Randomizer::getFloat()` (#19008) Error messages should not end with a `.`. --- ext/random/randomizer.c | 2 +- ext/random/tests/03_randomizer/methods/getFloat_error.phpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c index 4f63388e8f56a..2b5c98ae03313 100644 --- a/ext/random/randomizer.c +++ b/ext/random/randomizer.c @@ -196,7 +196,7 @@ PHP_METHOD(Random_Randomizer, getFloat) RETVAL_DOUBLE(php_random_gammasection_open_open(randomizer->engine, min, max)); if (UNEXPECTED(isnan(Z_DVAL_P(return_value)))) { - zend_value_error("The given interval is empty, there are no floats between argument #1 ($min) and argument #2 ($max)."); + zend_value_error("The given interval is empty, there are no floats between argument #1 ($min) and argument #2 ($max)"); RETURN_THROWS(); } diff --git a/ext/random/tests/03_randomizer/methods/getFloat_error.phpt b/ext/random/tests/03_randomizer/methods/getFloat_error.phpt index 286435e1752fb..42e933cbefb29 100644 --- a/ext/random/tests/03_randomizer/methods/getFloat_error.phpt +++ b/ext/random/tests/03_randomizer/methods/getFloat_error.phpt @@ -127,4 +127,4 @@ Random\Randomizer::getFloat(): Argument #2 ($max) must be finite Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) -The given interval is empty, there are no floats between argument #1 ($min) and argument #2 ($max). +The given interval is empty, there are no floats between argument #1 ($min) and argument #2 ($max) From 11ea995ff3966ae2c5bf2bceb5a6853ca68401d5 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 1 Jul 2025 00:44:50 +0200 Subject: [PATCH 3/5] curl: Remove incorrect string release on error The string is owned by the caller, and the caller releases it. Closes GH-18989. --- NEWS | 1 + ext/curl/interface.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 259270fdfcf82..22f667e57446e 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,7 @@ PHP NEWS - Curl: . Fix memory leaks when returning refcounted value from curl callback. (nielsdos) + . Remove incorrect string release. (nielsdos) - LDAP: . Fixed GH-18902 ldap_exop/ldap_exop_sync assert triggered on empty diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 6c480907b7629..b3139422cffa5 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1369,7 +1369,6 @@ static inline CURLcode add_simple_field(struct HttpPost **first, struct HttpPost part = curl_mime_addpart(mime); if (part == NULL) { zend_tmp_string_release(tmp_postval); - zend_string_release_ex(string_key, 0); return CURLE_OUT_OF_MEMORY; } if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK From 09c223de00af9b312e49db7bbc915aefaca5dbf8 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 1 Jul 2025 21:10:33 +0200 Subject: [PATCH 4/5] Fix leak when path is too long in ZipArchive::extractTo() I did not find an easy way to trigger this branch without also triggering some other error conditions earlier. Closes GH-19002. --- NEWS | 3 +++ ext/zip/php_zip.c | 1 + 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 22f667e57446e..5df4c88e972b1 100644 --- a/NEWS +++ b/NEWS @@ -42,6 +42,9 @@ PHP NEWS . Fixed GH-13264 (fgets() and stream_get_line() do not return false on filter fatal error). (Jakub Zelenka) +- Zip: + . Fix leak when path is too long in ZipArchive::extractTo(). (nielsdos) + 03 Jul 2025, PHP 8.3.23 - Core: diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 62f51ce9f35f3..3710b304c3515 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -218,6 +218,7 @@ static int php_zip_extract_file(struct zip * za, char *dest, const char *file, s return 0; } else if (len > MAXPATHLEN) { php_error_docref(NULL, E_WARNING, "Full extraction path exceed MAXPATHLEN (%i)", MAXPATHLEN); + efree(fullpath); efree(file_dirname_fullpath); zend_string_release_ex(file_basename, 0); CWD_STATE_FREE(new_state.cwd); From 69328ba304f2a47e0e9b3ba872db5681982efb96 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 1 Jul 2025 19:50:52 +0200 Subject: [PATCH 5/5] Fix GH-18990, bug #81029, bug #47314: SOAP HTTP socket not closing on object destruction Currently the resource is attached to the object and its refcount is increased. This means that the refcount to the resource is 2 instead of 1 as expected. A refcount of 2 is necessary in the current code because of how the error handling works: by using convert_to_null() the resource actually goes to rc_dtor_func(), dropping its refcount to 1. So on error the refcount is correct. To solve the issue, let `stream` conceptually be a borrow of the resource with refcount 1, and just use ZVAL_NULL() to prevent calling rc_dtor_func() on the resource. Closes GH-19001. --- NEWS | 4 +++ ext/soap/php_http.c | 21 +++++------- ext/soap/tests/bugs/gh18990.phpt | 58 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 ext/soap/tests/bugs/gh18990.phpt diff --git a/NEWS b/NEWS index 5df4c88e972b1..08be236abbdd0 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,10 @@ PHP NEWS . Fixed bug GH-18958 (Fatal error during shutdown after pcntl_rfork() or pcntl_forkx() with zend-max-execution-timers). (Arnaud) +- SOAP: + . Fixed bug GH-18990, bug #81029, bug #47314 (SOAP HTTP socket not closing + on object destruction). (nielsdos) + - Standard: . Fix misleading errors in printf(). (nielsdos) . Fix RCN violations in array functions. (nielsdos) diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index c908bb4d8ff16..3dfafda4f9573 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -511,9 +511,9 @@ int make_http_soap_request(zval *this_ptr, zend_string_equals(orig->host, phpurl->host) && orig->port == phpurl->port))) { } else { + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); stream = NULL; use_proxy = 0; @@ -522,9 +522,9 @@ int make_http_soap_request(zval *this_ptr, /* Check if keep-alive connection is still opened */ if (stream != NULL && php_stream_eof(stream)) { + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); stream = NULL; use_proxy = 0; @@ -533,9 +533,7 @@ int make_http_soap_request(zval *this_ptr, if (!stream) { stream = http_connect(this_ptr, phpurl, use_ssl, context, &use_proxy); if (stream) { - php_stream_auto_cleanup(stream); - ZVAL_RES(Z_CLIENT_HTTPSOCKET_P(this_ptr), stream->res); - GC_ADDREF(stream->res); + php_stream_to_zval(stream, Z_CLIENT_HTTPSOCKET_P(this_ptr)); ZVAL_LONG(Z_CLIENT_USE_PROXY_P(this_ptr), use_proxy); } else { php_url_free(phpurl); @@ -555,7 +553,6 @@ int make_http_soap_request(zval *this_ptr, zval *cookies, *login, *password; zend_resource *ret = zend_register_resource(phpurl, le_url); ZVAL_RES(Z_CLIENT_HTTPURL_P(this_ptr), ret); - GC_ADDREF(ret); if (context && (tmp = php_stream_context_get_option(context, "http", "protocol_version")) != NULL && @@ -683,9 +680,9 @@ int make_http_soap_request(zval *this_ptr, if (UNEXPECTED(php_random_bytes_throw(&nonce, sizeof(nonce)) != SUCCESS)) { ZEND_ASSERT(EG(exception)); + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); smart_str_free(&soap_headers_z); smart_str_free(&soap_headers); @@ -901,9 +898,9 @@ int make_http_soap_request(zval *this_ptr, if (request != buf) { zend_string_release_ex(request, 0); } + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); convert_to_null(Z_CLIENT_HTTPURL_P(this_ptr)); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); add_soap_fault(this_ptr, "HTTP", "Failed Sending HTTP SOAP request", NULL, NULL); smart_str_free(&soap_headers_z); @@ -919,8 +916,8 @@ int make_http_soap_request(zval *this_ptr, } if (!return_value) { + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); smart_str_free(&soap_headers_z); efree(http_msg); @@ -933,8 +930,8 @@ int make_http_soap_request(zval *this_ptr, if (request != buf) { zend_string_release_ex(request, 0); } + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); add_soap_fault(this_ptr, "HTTP", "Error Fetching http headers", NULL, NULL); smart_str_free(&soap_headers_z); @@ -1102,9 +1099,9 @@ int make_http_soap_request(zval *this_ptr, if (request != buf) { zend_string_release_ex(request, 0); } + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); zend_string_release_ex(http_headers, 0); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); add_soap_fault(this_ptr, "HTTP", "Error Fetching http body, No Content-Length, connection closed or chunked data", NULL, NULL); if (http_msg) { @@ -1119,8 +1116,8 @@ int make_http_soap_request(zval *this_ptr, } if (http_close) { + ZVAL_NULL(Z_CLIENT_HTTPSOCKET_P(this_ptr)); php_stream_close(stream); - convert_to_null(Z_CLIENT_HTTPSOCKET_P(this_ptr)); convert_to_null(Z_CLIENT_USE_PROXY_P(this_ptr)); stream = NULL; } diff --git a/ext/soap/tests/bugs/gh18990.phpt b/ext/soap/tests/bugs/gh18990.phpt new file mode 100644 index 0000000000000..30dbc0fe8b751 --- /dev/null +++ b/ext/soap/tests/bugs/gh18990.phpt @@ -0,0 +1,58 @@ +--TEST-- +GH-18990 (SOAP HTTP socket not closing on object destruction) +--INI-- +soap.wsdl_cache_enabled=0 +--EXTENSIONS-- +soap +--SKIPIF-- + +text0text1text2text3text4text5text6text7text8text9 +EOF; + +$responses = [ + "data://text/plain,HTTP/1.1 200 OK\r\n". + "Content-Type: text/xml;charset=utf-8\r\n". + "Connection: Keep-Alive\r\n". + "Content-Length: ".strlen($wsdl)."\r\n". + "\r\n". + $wsdl, + + "data://text/plain,HTTP/1.1 200 OK\r\n". + "Content-Type: text/xml;charset=utf-8\r\n". + "Connection: Keep-Alive\r\n". + "Content-Length: ".strlen($soap)."\r\n". + "\r\n". + $soap, +]; + +['pid' => $pid, 'uri' => $uri] = http_server($responses); + +$options = [ + 'trace' => false, + 'location' => $uri, +]; + +$cnt = count(get_resources()); + +$client = new SoapClient($uri, $options); + +var_dump(count($client->getItems())); + +http_server_kill($pid); + +unset($client); +var_dump(count(get_resources()) - $cnt); +?> +--EXPECT-- +int(10) +int(0)