From 28290655e864949ed0c08cad77240fb0956ab6e9 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 7 Aug 2024 10:02:23 +0200 Subject: [PATCH] Revert "Fix bug #69280: SoapClient classmap doesn't support fully qualified class name (#14398)" This reverts commit 476706165a227ea6b1d73299b9b6486a6ca073a9. Although the fix is correct, people are relying on the bug and their code stopped working, see GH-15252. --- NEWS | 2 ++ ext/soap/php_encoding.c | 53 ++++-------------------------------- ext/soap/php_encoding.h | 2 -- ext/soap/php_soap.h | 2 +- ext/soap/soap.c | 11 +++++--- ext/soap/tests/bug69280.phpt | 44 ------------------------------ ext/soap/tests/bug69280.wsdl | 46 ------------------------------- 7 files changed, 16 insertions(+), 144 deletions(-) delete mode 100644 ext/soap/tests/bug69280.phpt delete mode 100644 ext/soap/tests/bug69280.wsdl diff --git a/NEWS b/NEWS index 050e3369959..c4d189e561d 100644 --- a/NEWS +++ b/NEWS @@ -49,6 +49,8 @@ PHP NEWS - Soap: . Fixed bug #55639 (Digest autentication dont work). (nielsdos) . Fix SoapFault property destruction. (nielsdos) + . Fixed bug GH-15252 (SOAP XML broken since PHP 8.3.9 when using classmap + constructor option). (nielsdos) - Standard: . Fix passing non-finite timeout values in stream functions. (nielsdos) diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 82b21d15881..6568446249a 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -449,8 +449,11 @@ static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xml zend_string *type_name; ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(SOAP_GLOBAL(class_map), type_name, tmp) { - if (ZSTR_LEN(ce->name) == Z_STRLEN_P(tmp) && - zend_binary_strncasecmp(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), Z_STRVAL_P(tmp), ZSTR_LEN(ce->name), ZSTR_LEN(ce->name)) == 0) { + ZVAL_DEREF(tmp); + if (Z_TYPE_P(tmp) == IS_STRING && + ZSTR_LEN(ce->name) == Z_STRLEN_P(tmp) && + zend_binary_strncasecmp(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), Z_STRVAL_P(tmp), ZSTR_LEN(ce->name), ZSTR_LEN(ce->name)) == 0 && + type_name) { /* TODO: namespace isn't stored */ encodePtr enc = NULL; @@ -1379,6 +1382,7 @@ static zval *to_zval_object_ex(zval *ret, encodeTypePtr type, xmlNodePtr data, z zend_class_entry *tmp; if ((classname = zend_hash_str_find_deref(SOAP_GLOBAL(class_map), type->type_str, strlen(type->type_str))) != NULL && + Z_TYPE_P(classname) == IS_STRING && (tmp = zend_fetch_class(Z_STR_P(classname), ZEND_FETCH_CLASS_AUTO)) != NULL) { ce = tmp; } @@ -3630,48 +3634,3 @@ void delete_encoder_persistent(zval *zv) assert(t->details.map == NULL); free(t); } - -/* Normalize leading backslash similarly to how the engine strips it away. */ -static inline zend_string *drop_leading_backslash(zend_string *str) { - if (ZSTR_VAL(str)[0] == '\\') { - return zend_string_init(ZSTR_VAL(str) + 1, ZSTR_LEN(str) - 1, false); - } else { - return zend_string_copy(str); - } -} - -static HashTable *create_normalized_classmap_copy(HashTable *class_map) -{ - HashTable *normalized = zend_new_array(zend_hash_num_elements(class_map)); - - zend_string *key; - zval *value; - ZEND_HASH_FOREACH_STR_KEY_VAL(class_map, key, value) { - ZVAL_DEREF(value); - - if (key != NULL && Z_TYPE_P(value) == IS_STRING) { - zval zv; - ZVAL_STR(&zv, drop_leading_backslash(Z_STR_P(value))); - zend_hash_add_new(normalized, key, &zv); - } - } ZEND_HASH_FOREACH_END(); - - return normalized; -} - -void create_normalized_classmap(zval *return_value, zval *class_map) -{ - /* Check if we need to make a copy. */ - zend_string *key; - zval *value; - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARR_P(class_map), key, value) { - if (key == NULL || Z_TYPE_P(value) != IS_STRING || ZSTR_VAL(Z_STR_P(value))[0] == '\\') { - /* TODO: should probably throw in some of these cases to indicate programmer error, - * e.g. in the case where a non-string (after dereferencing) is provided. */ - RETURN_ARR(create_normalized_classmap_copy(Z_ARR_P(class_map))); - } - } ZEND_HASH_FOREACH_END(); - - /* We didn't have to make an actual copy, just increment the refcount. */ - RETURN_COPY(class_map); -} diff --git a/ext/soap/php_encoding.h b/ext/soap/php_encoding.h index 574ee4942a5..a4e16666e68 100644 --- a/ext/soap/php_encoding.h +++ b/ext/soap/php_encoding.h @@ -214,8 +214,6 @@ encodePtr get_conversion(int encode); void delete_encoder(zval *zv); void delete_encoder_persistent(zval *zv); -void create_normalized_classmap(zval *return_value, zval *class_map); - extern const encode defaultEncoding[]; extern int numDefaultEncodings; diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index c30a5a45c3a..33a071ed418 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -98,7 +98,7 @@ struct _soapService { char *actor; char *uri; xmlCharEncodingHandlerPtr encoding; - zval class_map; + HashTable *class_map; int features; struct _soapHeader **soap_headers_ptr; int send_errors; diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 3320f6dd480..e98820c630e 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -837,7 +837,7 @@ PHP_METHOD(SoapServer, __construct) if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && Z_TYPE_P(tmp) == IS_ARRAY) { - create_normalized_classmap(&service->class_map, tmp); + service->class_map = zend_array_dup(Z_ARRVAL_P(tmp)); } if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL && @@ -1303,7 +1303,7 @@ PHP_METHOD(SoapServer, handle) old_encoding = SOAP_GLOBAL(encoding); SOAP_GLOBAL(encoding) = service->encoding; old_class_map = SOAP_GLOBAL(class_map); - SOAP_GLOBAL(class_map) = Z_ARR(service->class_map); + SOAP_GLOBAL(class_map) = service->class_map; old_typemap = SOAP_GLOBAL(typemap); SOAP_GLOBAL(typemap) = service->typemap; old_features = SOAP_GLOBAL(features); @@ -2003,7 +2003,7 @@ PHP_METHOD(SoapClient, __construct) } if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && Z_TYPE_P(tmp) == IS_ARRAY) { - create_normalized_classmap(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); + ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); } if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL && @@ -4405,7 +4405,10 @@ static void delete_service(void *data) /* {{{ */ if (service->encoding) { xmlCharEncCloseFunc(service->encoding); } - zval_ptr_dtor(&service->class_map); + if (service->class_map) { + zend_hash_destroy(service->class_map); + FREE_HASHTABLE(service->class_map); + } zval_ptr_dtor(&service->soap_object); efree(service); } diff --git a/ext/soap/tests/bug69280.phpt b/ext/soap/tests/bug69280.phpt deleted file mode 100644 index 8c4e6068591..00000000000 --- a/ext/soap/tests/bug69280.phpt +++ /dev/null @@ -1,44 +0,0 @@ ---TEST-- -Bug #69280 (SoapClient classmap doesn't support fully qualified class name) ---EXTENSIONS-- -soap ---INI-- -soap.wsdl_cache_enabled=0 ---CREDITS-- -champetier dot etienne at gmail dot com ---FILE-- -__soapCall('TestMethod', [$parameters], [ - 'uri' => 'http://tempuri.org/', - 'soapaction' => '' - ] - ); - } - - public function __doRequest(string $request, string $location, string $action, int $version, bool $oneWay = false): ?string { - die($request); - } -} - -$a = new TestWS(__DIR__ . '/bug69280.wsdl', ['classmap' => [ - 'AbstractClass' => '\AbstractClass', - 'RealClass1' => '\RealClass1', -]]); -$r1 = new \RealClass1(); -$r1->prop = "prop"; -$r1->prop1 = "prop1"; -$a->TestMethod($r1); -?> ---EXPECT-- - -propprop1 diff --git a/ext/soap/tests/bug69280.wsdl b/ext/soap/tests/bug69280.wsdl deleted file mode 100644 index 8615ac77cdc..00000000000 --- a/ext/soap/tests/bug69280.wsdl +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -