diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 71bfb961826..177ac1edbf7 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -457,11 +457,8 @@ 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) { - 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) { + 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) { /* TODO: namespace isn't stored */ encodePtr enc = NULL; @@ -1395,7 +1392,6 @@ static zval *to_zval_object_ex(zval *ret, encodeTypePtr type, xmlNodePtr data, z classname = zend_hash_str_find_deref(SOAP_GLOBAL(class_map), type->type_str, strlen(type->type_str)); } if (classname != NULL && - Z_TYPE_P(classname) == IS_STRING && (tmp = zend_fetch_class(Z_STR_P(classname), ZEND_FETCH_CLASS_AUTO)) != NULL) { ce = tmp; } @@ -3668,3 +3664,48 @@ 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 67023a81d82..33b64d391b3 100644 --- a/ext/soap/php_encoding.h +++ b/ext/soap/php_encoding.h @@ -215,6 +215,8 @@ 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 encode defaultEncoding[]; extern int numDefaultEncodings; diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index bbb012cfa2e..d8b68b9f387 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -96,7 +96,7 @@ struct _soapService { char *actor; char *uri; xmlCharEncodingHandlerPtr encoding; - HashTable *class_map; + zval 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 3f7f8606875..2f788386d70 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -923,7 +923,7 @@ PHP_METHOD(SoapServer, __construct) if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && Z_TYPE_P(tmp) == IS_ARRAY) { - service->class_map = zend_array_dup(Z_ARRVAL_P(tmp)); + create_normalized_classmap(&service->class_map, tmp); } if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL && @@ -1395,7 +1395,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) = service->class_map; + SOAP_GLOBAL(class_map) = Z_ARR(service->class_map); old_typemap = SOAP_GLOBAL(typemap); SOAP_GLOBAL(typemap) = service->typemap; old_features = SOAP_GLOBAL(features); @@ -2084,7 +2084,7 @@ PHP_METHOD(SoapClient, __construct) } if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && Z_TYPE_P(tmp) == IS_ARRAY) { - ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); + create_normalized_classmap(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); } if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL && @@ -4484,10 +4484,7 @@ static void delete_service(soapServicePtr service) /* {{{ */ if (service->encoding) { xmlCharEncCloseFunc(service->encoding); } - if (service->class_map) { - zend_hash_destroy(service->class_map); - FREE_HASHTABLE(service->class_map); - } + zval_ptr_dtor(&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 new file mode 100644 index 00000000000..8c4e6068591 --- /dev/null +++ b/ext/soap/tests/bug69280.phpt @@ -0,0 +1,44 @@ +--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 new file mode 100644 index 00000000000..8615ac77cdc --- /dev/null +++ b/ext/soap/tests/bug69280.wsdl @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +