Commit 1c4fbed740d for php
commit 1c4fbed740d1389a6d8a4ceffa735ea458de434f
Author: Gina Peter Banyard <girgias@php.net>
Date: Sun Jul 6 17:36:45 2025 +0100
soap: call userland functions directly
diff --git a/ext/soap/soap.c b/ext/soap/soap.c
index 8c0cff80d90..7266b06f829 100644
--- a/ext/soap/soap.c
+++ b/ext/soap/soap.c
@@ -1383,12 +1383,11 @@ PHP_METHOD(SoapServer, handle)
sdlPtr old_sdl = NULL;
soapServicePtr service;
xmlDocPtr doc_request = NULL, doc_return = NULL;
- zval function_name, *params, *soap_obj, retval;
+ zval function_name, *params, retval;
char cont_len[30];
uint32_t num_params = 0;
- int size, i, call_status = 0;
+ int size, i;
xmlChar *buf;
- HashTable *function_table;
soapHeader *soap_headers = NULL;
sdlFunctionPtr function;
char *arg = NULL;
@@ -1563,10 +1562,10 @@ PHP_METHOD(SoapServer, handle)
service->soap_headers_ptr = &soap_headers;
- soap_obj = NULL;
+ zval *soap_obj = NULL;
+ HashTable *function_table = NULL;
if (service->type == SOAP_OBJECT) {
soap_obj = &service->soap_object;
- function_table = &((Z_OBJCE_P(soap_obj))->function_table);
} else if (service->type == SOAP_CLASS) {
/* If persistent then set soap_obj from the previous created session (if available) */
#ifdef SOAP_HAS_SESSION_SUPPORT
@@ -1590,7 +1589,7 @@ PHP_METHOD(SoapServer, handle)
}
#endif
- /* If new session or something weird happned */
+ /* If new session or something weird happened */
if (soap_obj == NULL) {
if (UNEXPECTED(object_init_ex(&tmp_soap, service->soap_class.ce) != SUCCESS)) {
php_output_discard();
@@ -1628,7 +1627,6 @@ PHP_METHOD(SoapServer, handle)
soap_obj = &tmp_soap;
}
}
- function_table = &((Z_OBJCE_P(soap_obj))->function_table);
} else {
if (service->soap_functions.functions_all) {
function_table = EG(function_table);
@@ -1653,41 +1651,89 @@ PHP_METHOD(SoapServer, handle)
}
}
#endif
- if (zend_hash_find_ptr_lc(function_table, Z_STR(h->function_name)) != NULL ||
- ((service->type == SOAP_CLASS || service->type == SOAP_OBJECT) &&
- zend_hash_str_exists(function_table, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)-1))) {
- call_status = call_user_function(NULL, soap_obj, &h->function_name, &h->retval, h->num_params, h->parameters);
- if (call_status != SUCCESS) {
- php_error_docref(NULL, E_WARNING, "Function '%s' call failed", Z_STRVAL(h->function_name));
- return;
+ if (soap_obj) {
+ /* This is because the object might define a __call() magic method */
+ zend_result method_call_result = zend_call_method_if_exists(
+ Z_OBJ_P(soap_obj),
+ Z_STR(h->function_name),
+ &h->retval,
+ h->num_params,
+ h->parameters
+ );
+ if (UNEXPECTED(method_call_result == FAILURE)) {
+ if (h->mustUnderstand) {
+ soap_server_fault_en("MustUnderstand","Header not understood");
+ goto fail;
+ }
+ ZVAL_NULL(&h->retval);
+ continue;
}
- if (Z_TYPE(h->retval) == IS_OBJECT &&
- instanceof_function(Z_OBJCE(h->retval), soap_fault_class_entry)) {
- php_output_discard();
- soap_server_fault_ex(function, &h->retval, h);
- soap_free_server_object(service, soap_obj);
- goto fail;
- } else if (EG(exception)) {
- php_output_discard();
- _soap_server_exception(service, function, ZEND_THIS);
- soap_free_server_object(service, soap_obj);
- goto fail;
+ } else {
+ zend_function *header_fn = zend_hash_find_ptr_lc(function_table, Z_STR(h->function_name));
+ if (UNEXPECTED(header_fn == NULL)) {
+ if (h->mustUnderstand) {
+ soap_server_fault_en("MustUnderstand","Header not understood");
+ goto fail;
+ }
+ ZVAL_NULL(&h->retval);
+ continue;
}
- } else if (h->mustUnderstand) {
- soap_server_fault_en("MustUnderstand","Header not understood");
+ zend_call_known_function(header_fn, NULL, NULL, &h->retval, h->num_params, h->parameters, NULL);
+ }
+
+ if (Z_TYPE(h->retval) == IS_OBJECT &&
+ instanceof_function(Z_OBJCE(h->retval), soap_fault_class_entry)) {
+ php_output_discard();
+ soap_server_fault_ex(function, &h->retval, h);
+ soap_free_server_object(service, soap_obj);
+ goto fail;
+ } else if (EG(exception)) {
+ php_output_discard();
+ _soap_server_exception(service, function, ZEND_THIS);
+ soap_free_server_object(service, soap_obj);
+ goto fail;
}
}
}
- if (zend_hash_find_ptr_lc(function_table, Z_STR(function_name)) != NULL ||
- ((service->type == SOAP_CLASS || service->type == SOAP_OBJECT) &&
- zend_hash_str_exists(function_table, ZEND_CALL_FUNC_NAME, sizeof(ZEND_CALL_FUNC_NAME)-1))) {
- call_status = call_user_function(NULL, soap_obj, &function_name, &retval, num_params, params);
- soap_free_server_object(service, soap_obj);
+ if (soap_obj) {
+ char *error = NULL;
+ /* This is because the object might define a __call() magic method */
+ zend_result method_call_result = zend_call_method_if_exists_ex(
+ Z_OBJ_P(soap_obj),
+ Z_STR(function_name),
+ &retval,
+ num_params,
+ params,
+ NULL,
+ &error
+ );
+ if (UNEXPECTED(method_call_result == FAILURE)) {
+ ZEND_ASSERT(error != NULL);
+ zend_throw_error(NULL, "Cannot call method %s::%s(): %s",
+ ZSTR_VAL(Z_OBJCE_P(soap_obj)->name),
+ Z_STRVAL(function_name),
+ error
+ );
+ php_output_discard();
+ _soap_server_exception(service, function, ZEND_THIS);
+ soap_free_server_object(service, soap_obj);
+ goto fail;
+ }
} else {
- php_error(E_ERROR, "Function '%s' doesn't exist", Z_STRVAL(function_name));
+ zend_function *fn = zend_hash_find_ptr_lc(function_table, Z_STR(function_name));
+ if (UNEXPECTED(fn == NULL)) {
+ zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL(function_name));
+ php_output_discard();
+ _soap_server_exception(service, function, ZEND_THIS);
+ soap_free_server_object(service, soap_obj);
+ goto fail;
+ }
+ zend_call_known_function(fn, NULL, NULL, &retval, num_params, params, NULL);
}
+ soap_free_server_object(service, soap_obj);
+
if (EG(exception)) {
if (!zend_is_unwind_exit(EG(exception))) {
php_output_discard();
@@ -1697,32 +1743,27 @@ PHP_METHOD(SoapServer, handle)
goto fail;
}
- if (call_status == SUCCESS) {
- char *response_name;
+ char *response_name;
- if (Z_TYPE(retval) == IS_OBJECT &&
- instanceof_function(Z_OBJCE(retval), soap_fault_class_entry)) {
- php_output_discard();
- soap_server_fault_ex(function, &retval, NULL);
- goto fail;
- }
-
- bool has_response_name = function && function->responseName;
- if (has_response_name) {
- response_name = function->responseName;
- } else {
- response_name = emalloc(Z_STRLEN(function_name) + sizeof("Response"));
- memcpy(response_name,Z_STRVAL(function_name),Z_STRLEN(function_name));
- memcpy(response_name+Z_STRLEN(function_name),"Response",sizeof("Response"));
- }
- doc_return = serialize_response_call(function, response_name, service->uri, &retval, soap_headers, soap_version);
+ if (Z_TYPE(retval) == IS_OBJECT &&
+ instanceof_function(Z_OBJCE(retval), soap_fault_class_entry)) {
+ php_output_discard();
+ soap_server_fault_ex(function, &retval, NULL);
+ goto fail;
+ }
- if (!has_response_name) {
- efree(response_name);
- }
+ bool has_response_name = function && function->responseName;
+ if (has_response_name) {
+ response_name = function->responseName;
} else {
- php_error_docref(NULL, E_WARNING, "Function '%s' call failed", Z_STRVAL(function_name));
- return;
+ response_name = emalloc(Z_STRLEN(function_name) + sizeof("Response"));
+ memcpy(response_name,Z_STRVAL(function_name),Z_STRLEN(function_name));
+ memcpy(response_name+Z_STRLEN(function_name),"Response",sizeof("Response"));
+ }
+ doc_return = serialize_response_call(function, response_name, service->uri, &retval, soap_headers, soap_version);
+
+ if (!has_response_name) {
+ efree(response_name);
}
if (EG(exception)) {
diff --git a/ext/soap/tests/bugs/bug73037.phpt b/ext/soap/tests/bugs/bug73037.phpt
index 6d94fca04e0..4ef8a7a25e6 100644
--- a/ext/soap/tests/bugs/bug73037.phpt
+++ b/ext/soap/tests/bugs/bug73037.phpt
@@ -136,42 +136,42 @@ function get_data($max)
--EXPECT--
Iteration 0
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
Iteration 1
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
Iteration 2
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
Iteration 3
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
Iteration 4
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
Iteration 5
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
Iteration 6
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
-Function 'CATALOG' doesn't exist
+Cannot call method stdClass::CATALOG(): class stdClass does not have a method "CATALOG"
diff --git a/ext/soap/tests/gh22218.phpt b/ext/soap/tests/gh22218.phpt
index 5b0714037a0..acbaf685e38 100644
--- a/ext/soap/tests/gh22218.phpt
+++ b/ext/soap/tests/gh22218.phpt
@@ -21,5 +21,6 @@
$server = new SoapServer(null, ['uri' => 'http://test-uri']);
$server->handle();
?>
---EXPECTF--
-%AFunction 'test' doesn't exist%A
+--EXPECT--
+<?xml version="1.0" encoding="UTF-8"?>
+<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>