mirror of
https://github.com/php/php-src.git
synced 2024-12-05 07:46:06 +08:00
Merge branch 'PHP-8.3'
* PHP-8.3: Fix bug #69280: SoapClient classmap doesn't support fully qualified class name (#14398)
This commit is contained in:
commit
61191dc313
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
44
ext/soap/tests/bug69280.phpt
Normal file
44
ext/soap/tests/bug69280.phpt
Normal file
@ -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--
|
||||
<?php
|
||||
abstract class AbstractClass {
|
||||
public $prop;
|
||||
}
|
||||
|
||||
class RealClass1 extends AbstractClass {
|
||||
public $prop1;
|
||||
}
|
||||
|
||||
class TestWS extends \SoapClient {
|
||||
public function TestMethod($parameters) {
|
||||
return $this->__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--
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>
|
46
ext/soap/tests/bug69280.wsdl
Normal file
46
ext/soap/tests/bug69280.wsdl
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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/">
|
||||
<types>
|
||||
<xs:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:complexType name="AbstractClass">
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="prop" type="xs:string" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="RealClass1">
|
||||
<xs:complexContent mixed="false">
|
||||
<xs:extension base="s0:AbstractClass">
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="prop1" type="xs:string" />
|
||||
</xs:sequence>
|
||||
</xs:extension>
|
||||
</xs:complexContent>
|
||||
</xs:complexType>
|
||||
</xs:schema>
|
||||
</types>
|
||||
<message name="TestMethodSoapIn">
|
||||
<part name="parameters" element="RealClass1" />
|
||||
</message>
|
||||
<portType name="TestWSSoap">
|
||||
<operation name="TestMethod">
|
||||
<input message="s0:TestMethodSoapIn" />
|
||||
</operation>
|
||||
</portType>
|
||||
<binding name="TestWSSoap" type="s0:TestWSSoap">
|
||||
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
|
||||
<operation name="TestMethod">
|
||||
<soap:operation soapAction="http://tempuri.org/TestMethod" style="document" />
|
||||
<input>
|
||||
<soap:body use="literal" />
|
||||
</input>
|
||||
<output>
|
||||
<soap:body use="literal" />
|
||||
</output>
|
||||
</operation>
|
||||
</binding>
|
||||
<service name="TestWS">
|
||||
<port name="TestWSSoap" binding="s0:TestWSSoap">
|
||||
<soap:address location="http://tempuri.org/" />
|
||||
</port>
|
||||
</service>
|
||||
</definitions>
|
Loading…
Reference in New Issue
Block a user