Skip to content

Commit 1c4fbed

Browse files
committed
soap: call userland functions directly
1 parent 57001e6 commit 1c4fbed

3 files changed

Lines changed: 114 additions & 72 deletions

File tree

‎ext/soap/soap.c‎

Lines changed: 97 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,12 +1383,11 @@ PHP_METHOD(SoapServer, handle)
13831383
sdlPtr old_sdl = NULL;
13841384
soapServicePtr service;
13851385
xmlDocPtr doc_request = NULL, doc_return = NULL;
1386-
zval function_name, *params, *soap_obj, retval;
1386+
zval function_name, *params, retval;
13871387
char cont_len[30];
13881388
uint32_t num_params = 0;
1389-
int size, i, call_status = 0;
1389+
int size, i;
13901390
xmlChar *buf;
1391-
HashTable *function_table;
13921391
soapHeader *soap_headers = NULL;
13931392
sdlFunctionPtr function;
13941393
char *arg = NULL;
@@ -1563,10 +1562,10 @@ PHP_METHOD(SoapServer, handle)
15631562

15641563
service->soap_headers_ptr = &soap_headers;
15651564

1566-
soap_obj = NULL;
1565+
zval *soap_obj = NULL;
1566+
HashTable *function_table = NULL;
15671567
if (service->type == SOAP_OBJECT) {
15681568
soap_obj = &service->soap_object;
1569-
function_table = &((Z_OBJCE_P(soap_obj))->function_table);
15701569
} else if (service->type == SOAP_CLASS) {
15711570
/* If persistent then set soap_obj from the previous created session (if available) */
15721571
#ifdef SOAP_HAS_SESSION_SUPPORT
@@ -1590,7 +1589,7 @@ PHP_METHOD(SoapServer, handle)
15901589
}
15911590
#endif
15921591

1593-
/* If new session or something weird happned */
1592+
/* If new session or something weird happened */
15941593
if (soap_obj == NULL) {
15951594
if (UNEXPECTED(object_init_ex(&tmp_soap, service->soap_class.ce) != SUCCESS)) {
15961595
php_output_discard();
@@ -1628,7 +1627,6 @@ PHP_METHOD(SoapServer, handle)
16281627
soap_obj = &tmp_soap;
16291628
}
16301629
}
1631-
function_table = &((Z_OBJCE_P(soap_obj))->function_table);
16321630
} else {
16331631
if (service->soap_functions.functions_all) {
16341632
function_table = EG(function_table);
@@ -1653,41 +1651,89 @@ PHP_METHOD(SoapServer, handle)
16531651
}
16541652
}
16551653
#endif
1656-
if (zend_hash_find_ptr_lc(function_table, Z_STR(h->function_name)) != NULL ||
1657-
((service->type == SOAP_CLASS || service->type == SOAP_OBJECT) &&
1658-
zend_hash_str_exists(function_table, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)-1))) {
1659-
call_status = call_user_function(NULL, soap_obj, &h->function_name, &h->retval, h->num_params, h->parameters);
1660-
if (call_status != SUCCESS) {
1661-
php_error_docref(NULL, E_WARNING, "Function '%s' call failed", Z_STRVAL(h->function_name));
1662-
return;
1654+
if (soap_obj) {
1655+
/* This is because the object might define a __call() magic method */
1656+
zend_result method_call_result = zend_call_method_if_exists(
1657+
Z_OBJ_P(soap_obj),
1658+
Z_STR(h->function_name),
1659+
&h->retval,
1660+
h->num_params,
1661+
h->parameters
1662+
);
1663+
if (UNEXPECTED(method_call_result == FAILURE)) {
1664+
if (h->mustUnderstand) {
1665+
soap_server_fault_en("MustUnderstand","Header not understood");
1666+
goto fail;
1667+
}
1668+
ZVAL_NULL(&h->retval);
1669+
continue;
16631670
}
1664-
if (Z_TYPE(h->retval) == IS_OBJECT &&
1665-
instanceof_function(Z_OBJCE(h->retval), soap_fault_class_entry)) {
1666-
php_output_discard();
1667-
soap_server_fault_ex(function, &h->retval, h);
1668-
soap_free_server_object(service, soap_obj);
1669-
goto fail;
1670-
} else if (EG(exception)) {
1671-
php_output_discard();
1672-
_soap_server_exception(service, function, ZEND_THIS);
1673-
soap_free_server_object(service, soap_obj);
1674-
goto fail;
1671+
} else {
1672+
zend_function *header_fn = zend_hash_find_ptr_lc(function_table, Z_STR(h->function_name));
1673+
if (UNEXPECTED(header_fn == NULL)) {
1674+
if (h->mustUnderstand) {
1675+
soap_server_fault_en("MustUnderstand","Header not understood");
1676+
goto fail;
1677+
}
1678+
ZVAL_NULL(&h->retval);
1679+
continue;
16751680
}
1676-
} else if (h->mustUnderstand) {
1677-
soap_server_fault_en("MustUnderstand","Header not understood");
1681+
zend_call_known_function(header_fn, NULL, NULL, &h->retval, h->num_params, h->parameters, NULL);
1682+
}
1683+
1684+
if (Z_TYPE(h->retval) == IS_OBJECT &&
1685+
instanceof_function(Z_OBJCE(h->retval), soap_fault_class_entry)) {
1686+
php_output_discard();
1687+
soap_server_fault_ex(function, &h->retval, h);
1688+
soap_free_server_object(service, soap_obj);
1689+
goto fail;
1690+
} else if (EG(exception)) {
1691+
php_output_discard();
1692+
_soap_server_exception(service, function, ZEND_THIS);
1693+
soap_free_server_object(service, soap_obj);
1694+
goto fail;
16781695
}
16791696
}
16801697
}
16811698

1682-
if (zend_hash_find_ptr_lc(function_table, Z_STR(function_name)) != NULL ||
1683-
((service->type == SOAP_CLASS || service->type == SOAP_OBJECT) &&
1684-
zend_hash_str_exists(function_table, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)-1))) {
1685-
call_status = call_user_function(NULL, soap_obj, &function_name, &retval, num_params, params);
1686-
soap_free_server_object(service, soap_obj);
1699+
if (soap_obj) {
1700+
char *error = NULL;
1701+
/* This is because the object might define a __call() magic method */
1702+
zend_result method_call_result = zend_call_method_if_exists_ex(
1703+
Z_OBJ_P(soap_obj),
1704+
Z_STR(function_name),
1705+
&retval,
1706+
num_params,
1707+
params,
1708+
NULL,
1709+
&error
1710+
);
1711+
if (UNEXPECTED(method_call_result == FAILURE)) {
1712+
ZEND_ASSERT(error != NULL);
1713+
zend_throw_error(NULL, "Cannot call method %s::%s(): %s",
1714+
ZSTR_VAL(Z_OBJCE_P(soap_obj)->name),
1715+
Z_STRVAL(function_name),
1716+
error
1717+
);
1718+
php_output_discard();
1719+
_soap_server_exception(service, function, ZEND_THIS);
1720+
soap_free_server_object(service, soap_obj);
1721+
goto fail;
1722+
}
16871723
} else {
1688-
php_error(E_ERROR, "Function '%s' doesn't exist", Z_STRVAL(function_name));
1724+
zend_function *fn = zend_hash_find_ptr_lc(function_table, Z_STR(function_name));
1725+
if (UNEXPECTED(fn == NULL)) {
1726+
zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL(function_name));
1727+
php_output_discard();
1728+
_soap_server_exception(service, function, ZEND_THIS);
1729+
soap_free_server_object(service, soap_obj);
1730+
goto fail;
1731+
}
1732+
zend_call_known_function(fn, NULL, NULL, &retval, num_params, params, NULL);
16891733
}
16901734

1735+
soap_free_server_object(service, soap_obj);
1736+
16911737
if (EG(exception)) {
16921738
if (!zend_is_unwind_exit(EG(exception))) {
16931739
php_output_discard();
@@ -1697,32 +1743,27 @@ PHP_METHOD(SoapServer, handle)
16971743
goto fail;
16981744
}
16991745

1700-
if (call_status == SUCCESS) {
1701-
char *response_name;
1746+
char *response_name;
17021747

1703-
if (Z_TYPE(retval) == IS_OBJECT &&
1704-
instanceof_function(Z_OBJCE(retval), soap_fault_class_entry)) {
1705-
php_output_discard();
1706-
soap_server_fault_ex(function, &retval, NULL);
1707-
goto fail;
1708-
}
1709-
1710-
bool has_response_name = function && function->responseName;
1711-
if (has_response_name) {
1712-
response_name = function->responseName;
1713-
} else {
1714-
response_name = emalloc(Z_STRLEN(function_name) + sizeof("Response"));
1715-
memcpy(response_name,Z_STRVAL(function_name),Z_STRLEN(function_name));
1716-
memcpy(response_name+Z_STRLEN(function_name),"Response",sizeof("Response"));
1717-
}
1718-
doc_return = serialize_response_call(function, response_name, service->uri, &retval, soap_headers, soap_version);
1748+
if (Z_TYPE(retval) == IS_OBJECT &&
1749+
instanceof_function(Z_OBJCE(retval), soap_fault_class_entry)) {
1750+
php_output_discard();
1751+
soap_server_fault_ex(function, &retval, NULL);
1752+
goto fail;
1753+
}
17191754

1720-
if (!has_response_name) {
1721-
efree(response_name);
1722-
}
1755+
bool has_response_name = function && function->responseName;
1756+
if (has_response_name) {
1757+
response_name = function->responseName;
17231758
} else {
1724-
php_error_docref(NULL, E_WARNING, "Function '%s' call failed", Z_STRVAL(function_name));
1725-
return;
1759+
response_name = emalloc(Z_STRLEN(function_name) + sizeof("Response"));
1760+
memcpy(response_name,Z_STRVAL(function_name),Z_STRLEN(function_name));
1761+
memcpy(response_name+Z_STRLEN(function_name),"Response",sizeof("Response"));
1762+
}
1763+
doc_return = serialize_response_call(function, response_name, service->uri, &retval, soap_headers, soap_version);
1764+
1765+
if (!has_response_name) {
1766+
efree(response_name);
17261767
}
17271768

17281769
if (EG(exception)) {

‎ext/soap/tests/bugs/bug73037.phpt‎

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -136,42 +136,42 @@ cleanup:
136136
--EXPECT--
137137
Iteration 0
138138

139-
Function 'CATALOG' doesn't exist
139+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
140140

141-
Function 'CATALOG' doesn't exist
141+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
142142

143143
Iteration 1
144144

145-
Function 'CATALOG' doesn't exist
145+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
146146

147-
Function 'CATALOG' doesn't exist
147+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
148148

149149
Iteration 2
150150

151-
Function 'CATALOG' doesn't exist
151+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
152152

153-
Function 'CATALOG' doesn't exist
153+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
154154

155155
Iteration 3
156156

157-
Function 'CATALOG' doesn't exist
157+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
158158

159-
Function 'CATALOG' doesn't exist
159+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
160160

161161
Iteration 4
162162

163-
Function 'CATALOG' doesn't exist
163+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
164164

165-
Function 'CATALOG' doesn't exist
165+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
166166

167167
Iteration 5
168168

169-
Function 'CATALOG' doesn't exist
169+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
170170

171-
Function 'CATALOG' doesn't exist
171+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
172172

173173
Iteration 6
174174

175-
Function 'CATALOG' doesn't exist
175+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
176176

177-
Function 'CATALOG' doesn't exist
177+
Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"

‎ext/soap/tests/gh22218.phpt‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ $_SERVER = 79;
2121
$server = new SoapServer(null, ['uri' => 'http://test-uri']);
2222
$server->handle();
2323
?>
24-
--EXPECTF--
25-
%AFunction 'test' doesn't exist%A
24+
--EXPECT--
25+
<?xml version="1.0" encoding="UTF-8"?>
26+
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Server</faultcode><faultstring>Call to undefined function test()</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

0 commit comments

Comments
 (0)