Skip to content

Commit d11a3c6

Browse files
committed
Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2: Fix bug #69280: SoapClient classmap doesn't support fully qualified class name (#14398)
2 parents d98586b + 4767061 commit d11a3c6

File tree

7 files changed

+146
-14
lines changed

7 files changed

+146
-14
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ PHP NEWS
3535
. Fix memory leak if calling SoapServer::setClass() twice. (nielsdos)
3636
. Fix reading zlib ini settings in ext-soap. (nielsdos)
3737
. Fix memory leaks with string function name lookups. (nielsdos)
38+
. Fixed bug #69280 (SoapClient classmap doesn't support fully qualified class
39+
name). (nielsdos)
3840

3941
- Sodium:
4042
. Fix memory leaks in ext/sodium on failure of some functions. (nielsdos)

ext/soap/php_encoding.c

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -449,11 +449,8 @@ static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xml
449449
zend_string *type_name;
450450

451451
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(SOAP_GLOBAL(class_map), type_name, tmp) {
452-
ZVAL_DEREF(tmp);
453-
if (Z_TYPE_P(tmp) == IS_STRING &&
454-
ZSTR_LEN(ce->name) == Z_STRLEN_P(tmp) &&
455-
zend_binary_strncasecmp(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), Z_STRVAL_P(tmp), ZSTR_LEN(ce->name), ZSTR_LEN(ce->name)) == 0 &&
456-
type_name) {
452+
if (ZSTR_LEN(ce->name) == Z_STRLEN_P(tmp) &&
453+
zend_binary_strncasecmp(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), Z_STRVAL_P(tmp), ZSTR_LEN(ce->name), ZSTR_LEN(ce->name)) == 0) {
457454

458455
/* TODO: namespace isn't stored */
459456
encodePtr enc = NULL;
@@ -1382,7 +1379,6 @@ static zval *to_zval_object_ex(zval *ret, encodeTypePtr type, xmlNodePtr data, z
13821379
zend_class_entry *tmp;
13831380

13841381
if ((classname = zend_hash_str_find_deref(SOAP_GLOBAL(class_map), type->type_str, strlen(type->type_str))) != NULL &&
1385-
Z_TYPE_P(classname) == IS_STRING &&
13861382
(tmp = zend_fetch_class(Z_STR_P(classname), ZEND_FETCH_CLASS_AUTO)) != NULL) {
13871383
ce = tmp;
13881384
}
@@ -3636,3 +3632,48 @@ void delete_encoder_persistent(zval *zv)
36363632
assert(t->details.map == NULL);
36373633
free(t);
36383634
}
3635+
3636+
/* Normalize leading backslash similarly to how the engine strips it away. */
3637+
static inline zend_string *drop_leading_backslash(zend_string *str) {
3638+
if (ZSTR_VAL(str)[0] == '\\') {
3639+
return zend_string_init(ZSTR_VAL(str) + 1, ZSTR_LEN(str) - 1, false);
3640+
} else {
3641+
return zend_string_copy(str);
3642+
}
3643+
}
3644+
3645+
static HashTable *create_normalized_classmap_copy(HashTable *class_map)
3646+
{
3647+
HashTable *normalized = zend_new_array(zend_hash_num_elements(class_map));
3648+
3649+
zend_string *key;
3650+
zval *value;
3651+
ZEND_HASH_FOREACH_STR_KEY_VAL(class_map, key, value) {
3652+
ZVAL_DEREF(value);
3653+
3654+
if (key != NULL && Z_TYPE_P(value) == IS_STRING) {
3655+
zval zv;
3656+
ZVAL_STR(&zv, drop_leading_backslash(Z_STR_P(value)));
3657+
zend_hash_add_new(normalized, key, &zv);
3658+
}
3659+
} ZEND_HASH_FOREACH_END();
3660+
3661+
return normalized;
3662+
}
3663+
3664+
void create_normalized_classmap(zval *return_value, zval *class_map)
3665+
{
3666+
/* Check if we need to make a copy. */
3667+
zend_string *key;
3668+
zval *value;
3669+
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARR_P(class_map), key, value) {
3670+
if (key == NULL || Z_TYPE_P(value) != IS_STRING || ZSTR_VAL(Z_STR_P(value))[0] == '\\') {
3671+
/* TODO: should probably throw in some of these cases to indicate programmer error,
3672+
* e.g. in the case where a non-string (after dereferencing) is provided. */
3673+
RETURN_ARR(create_normalized_classmap_copy(Z_ARR_P(class_map)));
3674+
}
3675+
} ZEND_HASH_FOREACH_END();
3676+
3677+
/* We didn't have to make an actual copy, just increment the refcount. */
3678+
RETURN_COPY(class_map);
3679+
}

ext/soap/php_encoding.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ encodePtr get_conversion(int encode);
214214
void delete_encoder(zval *zv);
215215
void delete_encoder_persistent(zval *zv);
216216

217+
void create_normalized_classmap(zval *return_value, zval *class_map);
218+
217219
extern const encode defaultEncoding[];
218220
extern int numDefaultEncodings;
219221

ext/soap/php_soap.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ struct _soapService {
9898
char *actor;
9999
char *uri;
100100
xmlCharEncodingHandlerPtr encoding;
101-
HashTable *class_map;
101+
zval class_map;
102102
int features;
103103
struct _soapHeader **soap_headers_ptr;
104104
int send_errors;

ext/soap/soap.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ PHP_METHOD(SoapServer, __construct)
816816

817817
if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL &&
818818
Z_TYPE_P(tmp) == IS_ARRAY) {
819-
service->class_map = zend_array_dup(Z_ARRVAL_P(tmp));
819+
create_normalized_classmap(&service->class_map, tmp);
820820
}
821821

822822
if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL &&
@@ -1282,7 +1282,7 @@ PHP_METHOD(SoapServer, handle)
12821282
old_encoding = SOAP_GLOBAL(encoding);
12831283
SOAP_GLOBAL(encoding) = service->encoding;
12841284
old_class_map = SOAP_GLOBAL(class_map);
1285-
SOAP_GLOBAL(class_map) = service->class_map;
1285+
SOAP_GLOBAL(class_map) = Z_ARR(service->class_map);
12861286
old_typemap = SOAP_GLOBAL(typemap);
12871287
SOAP_GLOBAL(typemap) = service->typemap;
12881288
old_features = SOAP_GLOBAL(features);
@@ -1982,7 +1982,7 @@ PHP_METHOD(SoapClient, __construct)
19821982
}
19831983
if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL &&
19841984
Z_TYPE_P(tmp) == IS_ARRAY) {
1985-
ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp);
1985+
create_normalized_classmap(Z_CLIENT_CLASSMAP_P(this_ptr), tmp);
19861986
}
19871987

19881988
if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL &&
@@ -4384,10 +4384,7 @@ static void delete_service(void *data) /* {{{ */
43844384
if (service->encoding) {
43854385
xmlCharEncCloseFunc(service->encoding);
43864386
}
4387-
if (service->class_map) {
4388-
zend_hash_destroy(service->class_map);
4389-
FREE_HASHTABLE(service->class_map);
4390-
}
4387+
zval_ptr_dtor(&service->class_map);
43914388
zval_ptr_dtor(&service->soap_object);
43924389
efree(service);
43934390
}

ext/soap/tests/bug69280.phpt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
Bug #69280 (SoapClient classmap doesn't support fully qualified class name)
3+
--EXTENSIONS--
4+
soap
5+
--INI--
6+
soap.wsdl_cache_enabled=0
7+
--CREDITS--
8+
champetier dot etienne at gmail dot com
9+
--FILE--
10+
<?php
11+
abstract class AbstractClass {
12+
public $prop;
13+
}
14+
15+
class RealClass1 extends AbstractClass {
16+
public $prop1;
17+
}
18+
19+
class TestWS extends \SoapClient {
20+
public function TestMethod($parameters) {
21+
return $this->__soapCall('TestMethod', [$parameters], [
22+
'uri' => 'http://tempuri.org/',
23+
'soapaction' => ''
24+
]
25+
);
26+
}
27+
28+
public function __doRequest(string $request, string $location, string $action, int $version, bool $oneWay = false): ?string {
29+
die($request);
30+
}
31+
}
32+
33+
$a = new TestWS(__DIR__ . '/bug69280.wsdl', ['classmap' => [
34+
'AbstractClass' => '\AbstractClass',
35+
'RealClass1' => '\RealClass1',
36+
]]);
37+
$r1 = new \RealClass1();
38+
$r1->prop = "prop";
39+
$r1->prop1 = "prop1";
40+
$a->TestMethod($r1);
41+
?>
42+
--EXPECT--
43+
<?xml version="1.0" encoding="UTF-8"?>
44+
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Body><parameters xsi:type="ns1:RealClass1"><ns1:prop>prop</ns1:prop><ns1:prop1>prop1</ns1:prop1></parameters></SOAP-ENV:Body></SOAP-ENV:Envelope>

ext/soap/tests/bug69280.wsdl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:s0="http://tempuri.org/" name="TestWS" targetNamespace="http://tempuri.org/" xmlns="http://schemas.xmlsoap.org/wsdl/">
3+
<types>
4+
<xs:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
5+
<xs:complexType name="AbstractClass">
6+
<xs:sequence>
7+
<xs:element minOccurs="0" maxOccurs="1" name="prop" type="xs:string" />
8+
</xs:sequence>
9+
</xs:complexType>
10+
<xs:complexType name="RealClass1">
11+
<xs:complexContent mixed="false">
12+
<xs:extension base="s0:AbstractClass">
13+
<xs:sequence>
14+
<xs:element minOccurs="0" maxOccurs="1" name="prop1" type="xs:string" />
15+
</xs:sequence>
16+
</xs:extension>
17+
</xs:complexContent>
18+
</xs:complexType>
19+
</xs:schema>
20+
</types>
21+
<message name="TestMethodSoapIn">
22+
<part name="parameters" element="RealClass1" />
23+
</message>
24+
<portType name="TestWSSoap">
25+
<operation name="TestMethod">
26+
<input message="s0:TestMethodSoapIn" />
27+
</operation>
28+
</portType>
29+
<binding name="TestWSSoap" type="s0:TestWSSoap">
30+
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
31+
<operation name="TestMethod">
32+
<soap:operation soapAction="http://tempuri.org/TestMethod" style="document" />
33+
<input>
34+
<soap:body use="literal" />
35+
</input>
36+
<output>
37+
<soap:body use="literal" />
38+
</output>
39+
</operation>
40+
</binding>
41+
<service name="TestWS">
42+
<port name="TestWSSoap" binding="s0:TestWSSoap">
43+
<soap:address location="http://tempuri.org/" />
44+
</port>
45+
</service>
46+
</definitions>

0 commit comments

Comments
 (0)