diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index 1eea30c62e90..f97a3fe75110 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -76,7 +76,7 @@ struct _soapService { struct _soap_functions { HashTable *ft; - int functions_all; + bool functions_all; } soap_functions; struct _soap_class { diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 0786ac811059..681c7b72d559 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -1008,8 +1008,10 @@ PHP_METHOD(SoapServer, __construct) service->version = version; service->type = SOAP_FUNCTIONS; - service->soap_functions.functions_all = FALSE; - service->soap_functions.ft = zend_new_array(0); + service->soap_functions.functions_all = false; + ALLOC_HASHTABLE(service->soap_functions.ft); + /* This hashtable contains zend_function pointers so doesn't need a destructor */ + zend_hash_init(service->soap_functions.ft, 0, NULL, NULL, false); if (wsdl) { service->sdl = get_sdl(ZEND_THIS, ZSTR_VAL(wsdl), cache_wsdl); @@ -1123,7 +1125,7 @@ PHP_METHOD(SoapServer, setObject) PHP_METHOD(SoapServer, getFunctions) { soapServicePtr service; - HashTable *ft = NULL; + const HashTable *ft = NULL; if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); @@ -1136,14 +1138,10 @@ PHP_METHOD(SoapServer, getFunctions) ft = &(Z_OBJCE(service->soap_object)->function_table); } else if (service->type == SOAP_CLASS) { ft = &service->soap_class.ce->function_table; - } else if (service->soap_functions.functions_all == TRUE) { + } else if (service->soap_functions.functions_all) { ft = EG(function_table); } else if (service->soap_functions.ft != NULL) { - zval *name; - - ZEND_HASH_MAP_FOREACH_VAL(service->soap_functions.ft, name) { - add_next_index_str(return_value, zend_string_copy(Z_STR_P(name))); - } ZEND_HASH_FOREACH_END(); + ft = service->soap_functions.ft; } if (ft != NULL) { zend_function *f; @@ -1162,7 +1160,7 @@ PHP_METHOD(SoapServer, getFunctions) PHP_METHOD(SoapServer, addFunction) { soapServicePtr service; - zval *function_name, function_copy; + zval *function_name; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &function_name) == FAILURE) { RETURN_THROWS(); @@ -1177,8 +1175,10 @@ PHP_METHOD(SoapServer, addFunction) zval *tmp_function; if (service->soap_functions.ft == NULL) { - service->soap_functions.functions_all = FALSE; - service->soap_functions.ft = zend_new_array(zend_hash_num_elements(Z_ARRVAL_P(function_name))); + service->soap_functions.functions_all = false; + ALLOC_HASHTABLE(service->soap_functions.ft); + /* This hashtable contains zend_function pointers so doesn't need a destructor */ + zend_hash_init(service->soap_functions.ft, zend_hash_num_elements(Z_ARRVAL_P(function_name)), NULL, NULL, false); } ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(function_name), tmp_function) { @@ -1191,15 +1191,15 @@ PHP_METHOD(SoapServer, addFunction) } key = zend_string_tolower(Z_STR_P(tmp_function)); + f = zend_hash_find_ptr(EG(function_table), key); - if ((f = zend_hash_find_ptr(EG(function_table), key)) == NULL) { + if (f == NULL) { zend_string_release_ex(key, false); zend_type_error("SoapServer::addFunction(): Function \"%s\" not found", Z_STRVAL_P(tmp_function)); RETURN_THROWS(); } - ZVAL_STR_COPY(&function_copy, f->common.function_name); - zend_hash_update(service->soap_functions.ft, key, &function_copy); + zend_hash_update_ptr(service->soap_functions.ft, key, f); zend_string_release_ex(key, 0); } ZEND_HASH_FOREACH_END(); @@ -1209,19 +1209,20 @@ PHP_METHOD(SoapServer, addFunction) zend_function *f; key = zend_string_tolower(Z_STR_P(function_name)); - - if ((f = zend_hash_find_ptr(EG(function_table), key)) == NULL) { + f = zend_hash_find_ptr(EG(function_table), key); + if (f == NULL) { zend_string_release_ex(key, false); zend_argument_type_error(1, "must be a valid function name, function \"%s\" not found", Z_STRVAL_P(function_name)); RETURN_THROWS(); } if (service->soap_functions.ft == NULL) { - service->soap_functions.functions_all = FALSE; - service->soap_functions.ft = zend_new_array(0); + service->soap_functions.functions_all = false; + ALLOC_HASHTABLE(service->soap_functions.ft); + /* This hashtable contains zend_function pointers so doesn't need a destructor */ + zend_hash_init(service->soap_functions.ft, 0, NULL, NULL, false); } - ZVAL_STR_COPY(&function_copy, f->common.function_name); - zend_hash_update(service->soap_functions.ft, key, &function_copy); + zend_hash_update_ptr(service->soap_functions.ft, key, f); zend_string_release_ex(key, 0); } else if (Z_TYPE_P(function_name) == IS_LONG) { if (Z_LVAL_P(function_name) == SOAP_FUNCTIONS_ALL) { @@ -1235,7 +1236,7 @@ PHP_METHOD(SoapServer, addFunction) efree(service->soap_functions.ft); service->soap_functions.ft = NULL; } - service->soap_functions.functions_all = TRUE; + service->soap_functions.functions_all = true; } else { zend_argument_value_error(1, "must be SOAP_FUNCTIONS_ALL when an integer is passed"); } @@ -1273,12 +1274,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; @@ -1453,10 +1453,15 @@ PHP_METHOD(SoapServer, handle) service->soap_headers_ptr = &soap_headers; - soap_obj = NULL; + zval *soap_obj = NULL; + zend_object *soap_zobj = NULL; + zend_class_entry *soap_obj_ce = NULL; + HashTable *function_table; if (service->type == SOAP_OBJECT) { soap_obj = &service->soap_object; - function_table = &((Z_OBJCE_P(soap_obj))->function_table); + soap_zobj = Z_OBJ_P(soap_obj); + soap_obj_ce = Z_OBJCE_P(soap_obj); + function_table = &soap_obj_ce->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 @@ -1480,7 +1485,7 @@ PHP_METHOD(SoapServer, handle) } #endif - /* If new session or something weird happned */ + /* If new session or something weird happened */ if (soap_obj == NULL) { object_init_ex(&tmp_soap, service->soap_class.ce); @@ -1514,9 +1519,11 @@ PHP_METHOD(SoapServer, handle) soap_obj = &tmp_soap; } } - function_table = &((Z_OBJCE_P(soap_obj))->function_table); + soap_zobj = Z_OBJ_P(soap_obj); + soap_obj_ce = Z_OBJCE_P(soap_obj); + function_table = &soap_obj_ce->function_table; } else { - if (service->soap_functions.functions_all == TRUE) { + if (service->soap_functions.functions_all) { function_table = EG(function_table); } else { function_table = service->soap_functions.ft; @@ -1539,52 +1546,58 @@ 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))) { - if (service->type == SOAP_CLASS || service->type == SOAP_OBJECT) { - call_status = call_user_function(NULL, soap_obj, &h->function_name, &h->retval, h->num_params, h->parameters); - } else { - call_status = call_user_function(EG(function_table), NULL, &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 (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); - if (service->type == SOAP_CLASS && soap_obj) {zval_ptr_dtor(soap_obj);} - goto fail; - } else if (EG(exception)) { - php_output_discard(); - _soap_server_exception(service, function, ZEND_THIS); - if (service->type == SOAP_CLASS && soap_obj) {zval_ptr_dtor(soap_obj);} + zend_function *header_fn = zend_hash_find_ptr_lc(function_table, Z_STR(h->function_name)); + /* If object has a __call() magic method use it */ + if (header_fn == NULL && soap_obj_ce && soap_obj_ce->__call) { + header_fn = zend_get_call_trampoline_func(soap_obj_ce, Z_STR(function_name), false); + } + if (UNEXPECTED(header_fn == NULL)) { + if (h->mustUnderstand) { + soap_server_fault_en("MustUnderstand","Header not understood", NULL, NULL, NULL); goto fail; } - } else if (h->mustUnderstand) { - soap_server_fault_en("MustUnderstand","Header not understood", NULL, NULL, NULL); + continue; + } + + zend_call_known_function(header_fn, soap_zobj, soap_obj_ce, &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); + if (service->type == SOAP_CLASS && soap_obj) {zval_ptr_dtor(soap_obj);} + goto fail; + } else if (EG(exception)) { + php_output_discard(); + _soap_server_exception(service, function, ZEND_THIS); + if (service->type == SOAP_CLASS && soap_obj) {zval_ptr_dtor(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))) { - if (service->type == SOAP_CLASS || service->type == SOAP_OBJECT) { - call_status = call_user_function(NULL, soap_obj, &function_name, &retval, num_params, params); - if (service->type == SOAP_CLASS) { - if (service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) { - zval_ptr_dtor(soap_obj); - soap_obj = NULL; - } - } + zend_function *fn = zend_hash_find_ptr_lc(function_table, Z_STR(function_name)); + if (UNEXPECTED(fn == NULL)) { + if (soap_obj_ce && soap_obj_ce->__call) { + fn = zend_get_call_trampoline_func(soap_obj_ce, Z_STR(function_name), false); } else { - call_status = call_user_function(EG(function_table), NULL, &function_name, &retval, num_params, params); + if (soap_obj_ce) { + zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(soap_obj_ce->name), Z_STRVAL(function_name)); + } else { + zend_throw_error(NULL, "Call to undefined function %s()", Z_STRVAL(function_name)); + } + php_output_discard(); + _soap_server_exception(service, function, ZEND_THIS); + if (service->type == SOAP_CLASS && soap_obj) {zval_ptr_dtor(soap_obj);} + goto fail; + } + } + zend_call_known_function(fn, soap_zobj, soap_obj_ce, &retval, num_params, params, NULL); + + if (service->type == SOAP_CLASS) { + if (service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) { + zval_ptr_dtor(soap_obj); + soap_obj = NULL; } - } else { - php_error(E_ERROR, "Function '%s' doesn't exist", Z_STRVAL(function_name)); } if (EG(exception)) { @@ -1600,32 +1613,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 7a5b99776772..cfa6d7fd54e8 100644 --- a/ext/soap/tests/bugs/bug73037.phpt +++ b/ext/soap/tests/bugs/bug73037.phpt @@ -136,43 +136,42 @@ cleanup: --EXPECT-- Iteration 0 -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() Iteration 1 -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() Iteration 2 -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() Iteration 3 -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() Iteration 4 -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() Iteration 5 -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() Iteration 6 -Function 'CATALOG' doesn't exist - -Function 'CATALOG' doesn't exist +Call to undefined method stdClass::CATALOG() +Call to undefined method stdClass::CATALOG()