-
Notifications
You must be signed in to change notification settings - Fork 7.9k
ext/soap: Refactor userland function calling code #19055
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Girgias
wants to merge
5
commits into
php:master
Choose a base branch
from
Girgias:soap-call-func-directly
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+114
−106
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
8e7ff9a
ext/sopa: call_user_function API doesn't care about the function table
Girgias 249168b
ext/soap: Use bool type instead of int type for functions_all field
Girgias a01ff38
ext/soap: store zend_function pointers in soap_functions.ft field
Girgias a955ed1
ext/soap: call userland functions directly
Girgias cdb029d
ext/soap: throw an Error for undefined functions/methods
Girgias File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,59 @@ 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); | ||
zend_function *header_fn = zend_hash_find_ptr_lc(function_table, Z_STR(h->function_name)); | ||
if (UNEXPECTED(header_fn == NULL)) { | ||
if (soap_obj_ce && soap_obj_ce->__call) { | ||
header_fn = zend_get_call_trampoline_func(soap_obj_ce, Z_STR(function_name), false); | ||
goto soap_header_func_call; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you structure the code in such a way to avoid this goto? |
||
} | ||
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);} | ||
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; | ||
} | ||
|
||
soap_header_func_call: | ||
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 +1614,27 @@ PHP_METHOD(SoapServer, handle) | |
goto fail; | ||
} | ||
|
||
if (call_status == SUCCESS) { | ||
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; | ||
} | ||
char *response_name; | ||
|
||
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)) { | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When using
zend_get_call_trampoline_func
you should also release the trampoline function when you're done with it. This usually doesn't cause problems because as long as you're outside of a trampoline call, this function will store the trampoline inEG(trampoline)
. It should be possible to test this by making a soap call from within a trampoline.