mirror of
https://github.com/php/php-src.git
synced 2025-08-16 14:08:47 +02:00
Fixed is_callable/call_user_func mess that had done different things for very similar arguments e.g. array("A","B") and "A::B"
This commit is contained in:
parent
77baec3f6d
commit
af05ce0af6
17 changed files with 223 additions and 417 deletions
|
@ -685,15 +685,8 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS
|
|||
zend_class_entry *current_called_scope;
|
||||
zend_class_entry *calling_scope = NULL;
|
||||
zend_class_entry *called_scope = NULL;
|
||||
zend_class_entry *check_scope_or_static = NULL;
|
||||
zval *current_this;
|
||||
zend_execute_data execute_data;
|
||||
zval *method_name;
|
||||
zval *params_array;
|
||||
int call_via_handler = 0;
|
||||
unsigned int clen;
|
||||
int fname_len;
|
||||
char *colon, *fname, *cname, *lcname;
|
||||
|
||||
*fci->retval_ptr_ptr = NULL;
|
||||
|
||||
|
@ -728,261 +721,40 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS
|
|||
}
|
||||
|
||||
if (!fci_cache || !fci_cache->initialized) {
|
||||
if (Z_TYPE_P(fci->function_name) == IS_ARRAY) { /* assume array($obj, $name) couple */
|
||||
zval **tmp_object_ptr, **tmp_real_function_name;
|
||||
zend_fcall_info_cache fci_cache_local;
|
||||
char *callable_name;
|
||||
char *error = NULL;
|
||||
|
||||
if (zend_hash_index_find(Z_ARRVAL_P(fci->function_name), 0, (void **) &tmp_object_ptr) == FAILURE) {
|
||||
return FAILURE;
|
||||
}
|
||||
if (zend_hash_index_find(Z_ARRVAL_P(fci->function_name), 1, (void **) &tmp_real_function_name) == FAILURE) {
|
||||
return FAILURE;
|
||||
}
|
||||
fci->function_name = *tmp_real_function_name;
|
||||
SEPARATE_ZVAL_IF_NOT_REF(tmp_object_ptr);
|
||||
fci->object_pp = tmp_object_ptr;
|
||||
Z_SET_ISREF_PP(fci->object_pp);
|
||||
if (!fci_cache) {
|
||||
fci_cache = &fci_cache_local;
|
||||
}
|
||||
|
||||
if (fci->object_pp && !*fci->object_pp) {
|
||||
fci->object_pp = NULL;
|
||||
}
|
||||
|
||||
if (fci->object_pp) {
|
||||
if (Z_TYPE_PP(fci->object_pp) == IS_OBJECT
|
||||
&& (!EG(objects_store).object_buckets || !EG(objects_store).object_buckets[Z_OBJ_HANDLE_PP(fci->object_pp)].valid)) {
|
||||
return FAILURE;
|
||||
if (!zend_is_callable_ex(fci->function_name, fci->object_pp, IS_CALLABLE_CHECK_SILENT, &callable_name, NULL, fci_cache, &error TSRMLS_CC)) {
|
||||
if (error) {
|
||||
zend_error(E_WARNING, "Invalid callback %s, %s", callable_name, error);
|
||||
efree(error);
|
||||
}
|
||||
/* TBI!! new object handlers */
|
||||
if (Z_TYPE_PP(fci->object_pp) == IS_OBJECT) {
|
||||
if (!IS_ZEND_STD_OBJECT(**fci->object_pp)) {
|
||||
zend_error(E_WARNING, "Cannot use call_user_function on objects without a class entry");
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
calling_scope = called_scope = Z_OBJCE_PP(fci->object_pp);
|
||||
fci->function_table = &calling_scope->function_table;
|
||||
EX(object) = *fci->object_pp;
|
||||
} else if (Z_TYPE_PP(fci->object_pp) == IS_STRING) {
|
||||
zend_class_entry **ce;
|
||||
int found = FAILURE;
|
||||
|
||||
if (strcmp(Z_STRVAL_PP(fci->object_pp), "self") == 0) {
|
||||
if (!EG(active_op_array) || !EG(active_op_array)->scope) {
|
||||
zend_error(E_ERROR, "Cannot access self:: when no class scope is active");
|
||||
}
|
||||
ce = &(EG(active_op_array)->scope);
|
||||
found = (*ce != NULL?SUCCESS:FAILURE);
|
||||
fci->object_pp = EG(This)?&EG(This):NULL;
|
||||
EX(object) = EG(This);
|
||||
calling_scope = *ce;
|
||||
called_scope = EG(called_scope) ? EG(called_scope) : calling_scope;
|
||||
} else if (strcmp(Z_STRVAL_PP(fci->object_pp), "parent") == 0 && EG(active_op_array)) {
|
||||
|
||||
if (!EG(active_op_array) || !EG(active_op_array)->scope) {
|
||||
zend_error(E_ERROR, "Cannot access parent:: when no class scope is active");
|
||||
}
|
||||
if (!EG(active_op_array)->scope->parent) {
|
||||
zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent");
|
||||
}
|
||||
ce = &(EG(active_op_array)->scope->parent);
|
||||
found = (*ce != NULL?SUCCESS:FAILURE);
|
||||
fci->object_pp = EG(This)?&EG(This):NULL;
|
||||
EX(object) = EG(This);
|
||||
calling_scope = *ce;
|
||||
called_scope = EG(called_scope) ? EG(called_scope) : calling_scope;
|
||||
} else if (Z_STRLEN_PP(fci->object_pp) == sizeof("static") - 1 &&
|
||||
!memcmp(Z_STRVAL_PP(fci->object_pp), "static", sizeof("static") - 1)
|
||||
) {
|
||||
if (!EG(called_scope)) {
|
||||
zend_error(E_ERROR, "Cannot access static:: when no class scope is active");
|
||||
}
|
||||
ce = &(EG(called_scope));
|
||||
found = (EG(called_scope) != NULL?SUCCESS:FAILURE);
|
||||
fci->object_pp = EG(This)?&EG(This):NULL;
|
||||
EX(object) = EG(This);
|
||||
calling_scope = called_scope = EG(called_scope);
|
||||
} else {
|
||||
zend_class_entry *scope;
|
||||
scope = EG(active_op_array) ? EG(active_op_array)->scope : NULL;
|
||||
|
||||
found = zend_lookup_class(Z_STRVAL_PP(fci->object_pp), Z_STRLEN_PP(fci->object_pp), &ce TSRMLS_CC);
|
||||
if (found == FAILURE) {
|
||||
zend_error(E_ERROR, "Class '%s' not found", Z_STRVAL_PP(fci->object_pp));
|
||||
}
|
||||
if (scope && EG(This) &&
|
||||
instanceof_function(Z_OBJCE_P(EG(This)), scope TSRMLS_CC) &&
|
||||
instanceof_function(scope, *ce TSRMLS_CC)) {
|
||||
fci->object_pp = &EG(This);
|
||||
EX(object) = EG(This);
|
||||
} else {
|
||||
fci->object_pp = NULL;
|
||||
}
|
||||
calling_scope = called_scope = *ce;
|
||||
}
|
||||
if (found == FAILURE)
|
||||
return FAILURE;
|
||||
|
||||
fci->function_table = &(*ce)->function_table;
|
||||
} else {
|
||||
zend_error(E_NOTICE, "Non-callable array passed to zend_call_function()");
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (fci->function_table == NULL) {
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(fci->function_name) == IS_OBJECT) {
|
||||
if (zend_get_closure(fci->function_name, &calling_scope, &EX(function_state).function, NULL, &fci->object_pp TSRMLS_CC) == SUCCESS) {
|
||||
called_scope = calling_scope;
|
||||
goto init_fci_cache;
|
||||
}
|
||||
} else if (Z_TYPE_P(fci->function_name) != IS_STRING) {
|
||||
efree(callable_name);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
fname = Z_STRVAL_P(fci->function_name);
|
||||
fname_len = Z_STRLEN_P(fci->function_name);
|
||||
if (fname[0] == ':' && fname[1] == ':') {
|
||||
fname += 2;
|
||||
fname_len -=2;
|
||||
}
|
||||
EX(function_state).function = NULL;
|
||||
lcname = zend_str_tolower_dup(fname, fname_len);
|
||||
|
||||
if (!fci->object_pp &&
|
||||
zend_hash_find(fci->function_table, lcname, fname_len + 1, (void **)&EX(function_state).function) == SUCCESS
|
||||
) {
|
||||
efree(lcname);
|
||||
} else {
|
||||
efree(lcname);
|
||||
|
||||
cname = fname;
|
||||
|
||||
if ((colon = zend_memrchr(fname, ':', fname_len)) != NULL &&
|
||||
colon > fname &&
|
||||
*(colon - 1) == ':'
|
||||
) {
|
||||
zend_class_entry **pce, *ce_child = NULL;
|
||||
|
||||
clen = colon - fname - 1;
|
||||
fname_len -= (clen + 2);
|
||||
fname = colon + 1;
|
||||
|
||||
lcname = zend_str_tolower_dup(cname, clen);
|
||||
/* caution: lcname is not '\0' terminated */
|
||||
if (clen == sizeof("self") - 1 &&
|
||||
memcmp(lcname, "self", sizeof("self") - 1) == 0
|
||||
) {
|
||||
if (!EG(active_op_array) || !EG(active_op_array)->scope) {
|
||||
zend_error(E_ERROR, "Cannot access self:: when no class scope is active");
|
||||
}
|
||||
ce_child = EG(active_op_array) ? EG(active_op_array)->scope : NULL;
|
||||
called_scope = EG(called_scope) ? EG(called_scope) : ce_child;
|
||||
} else if (clen == sizeof("parent") - 1 &&
|
||||
memcmp(lcname, "parent", sizeof("parent") - 1) == 0
|
||||
) {
|
||||
if (!EG(active_op_array) || !EG(active_op_array)->scope) {
|
||||
zend_error(E_ERROR, "Cannot access parent:: when no class scope is active");
|
||||
}
|
||||
if (!EG(active_op_array)->scope->parent) {
|
||||
zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent");
|
||||
}
|
||||
ce_child = EG(active_op_array) && EG(active_op_array)->scope ? EG(active_op_array)->scope->parent : NULL;
|
||||
called_scope = EG(called_scope) ? EG(called_scope) : ce_child;
|
||||
} else if (clen == sizeof("static") - 1 &&
|
||||
memcmp(lcname, "static", sizeof("static") - 1)
|
||||
) {
|
||||
if (!EG(called_scope)) {
|
||||
zend_error(E_ERROR, "Cannot access static:: when no class scope is active");
|
||||
}
|
||||
called_scope = ce_child = EG(called_scope);
|
||||
} else if (zend_lookup_class(lcname, clen, &pce TSRMLS_CC) == SUCCESS) {
|
||||
called_scope = ce_child = *pce;
|
||||
}
|
||||
efree(lcname);
|
||||
|
||||
if (!ce_child) {
|
||||
zend_error(E_ERROR, "Cannot call method %s() or method does not exist", Z_STRVAL_P(fci->function_name));
|
||||
return FAILURE;
|
||||
}
|
||||
check_scope_or_static = calling_scope;
|
||||
fci->function_table = &ce_child->function_table;
|
||||
calling_scope = ce_child;
|
||||
}
|
||||
|
||||
if (fci->object_pp) {
|
||||
if (Z_OBJ_HT_PP(fci->object_pp)->get_method == NULL) {
|
||||
zend_error(E_ERROR, "Object does not support method calls");
|
||||
}
|
||||
EX(function_state).function =
|
||||
Z_OBJ_HT_PP(fci->object_pp)->get_method(fci->object_pp, fname, fname_len TSRMLS_CC);
|
||||
if (EX(function_state).function && calling_scope != EX(function_state).function->common.scope) {
|
||||
char *function_name_lc = zend_str_tolower_dup(fname, fname_len);
|
||||
if (zend_hash_find(&calling_scope->function_table, function_name_lc, fname_len + 1, (void **) &EX(function_state).function) == FAILURE) {
|
||||
efree(function_name_lc);
|
||||
zend_error(E_ERROR, "Cannot call method %s::%s() or method does not exist", calling_scope->name, fname);
|
||||
}
|
||||
efree(function_name_lc);
|
||||
}
|
||||
} else if (calling_scope) {
|
||||
if (calling_scope->get_static_method) {
|
||||
EX(function_state).function = calling_scope->get_static_method(calling_scope, fname, fname_len TSRMLS_CC);
|
||||
} else {
|
||||
EX(function_state).function = zend_std_get_static_method(calling_scope, fname, fname_len TSRMLS_CC);
|
||||
}
|
||||
|
||||
if (((zend_internal_function*)EX(function_state).function)->handler == zend_std_call_user_call) {
|
||||
fci->object_pp = &EG(This);
|
||||
}
|
||||
|
||||
if (check_scope_or_static && EX(function_state).function
|
||||
&& !(EX(function_state).function->common.fn_flags & ZEND_ACC_STATIC)
|
||||
&& !instanceof_function(check_scope_or_static, calling_scope TSRMLS_CC)) {
|
||||
zend_error(E_ERROR, "Cannot call method %s() of class %s which is not a derived from %s", Z_STRVAL_P(fci->function_name), calling_scope->name, check_scope_or_static->name);
|
||||
return FAILURE;
|
||||
}
|
||||
} else if (error) {
|
||||
/* Capitalize the first latter of the error message */
|
||||
if (error[0] >= 'a' && error[0] <= 'z') {
|
||||
error[0] += ('A' - 'a');
|
||||
}
|
||||
zend_error(E_STRICT, "%s", error);
|
||||
efree(error);
|
||||
}
|
||||
efree(callable_name);
|
||||
}
|
||||
|
||||
if (EX(function_state).function == NULL) {
|
||||
/* try calling __call */
|
||||
if (calling_scope && calling_scope->__call) {
|
||||
EX(function_state).function = calling_scope->__call;
|
||||
/* prepare params */
|
||||
ALLOC_INIT_ZVAL(method_name);
|
||||
ZVAL_STRINGL(method_name, fname, fname_len, 0);
|
||||
|
||||
ALLOC_INIT_ZVAL(params_array);
|
||||
array_init(params_array);
|
||||
call_via_handler = 1;
|
||||
} else {
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
init_fci_cache:
|
||||
if (fci_cache &&
|
||||
(EX(function_state).function->type != ZEND_INTERNAL_FUNCTION ||
|
||||
((zend_internal_function*)EX(function_state).function)->handler != zend_std_call_user_call)
|
||||
) {
|
||||
fci_cache->function_handler = EX(function_state).function;
|
||||
fci_cache->object_pp = fci->object_pp;
|
||||
fci_cache->calling_scope = calling_scope;
|
||||
fci_cache->called_scope = called_scope;
|
||||
fci_cache->initialized = 1;
|
||||
}
|
||||
} else {
|
||||
EX(function_state).function = fci_cache->function_handler;
|
||||
calling_scope = fci_cache->calling_scope;
|
||||
called_scope = fci_cache->called_scope;
|
||||
fci->object_pp = fci_cache->object_pp;
|
||||
EX(object) = fci->object_pp ? *fci->object_pp : NULL;
|
||||
if (fci->object_pp && *fci->object_pp && Z_TYPE_PP(fci->object_pp) == IS_OBJECT
|
||||
&& (!EG(objects_store).object_buckets || !EG(objects_store).object_buckets[Z_OBJ_HANDLE_PP(fci->object_pp)].valid)) {
|
||||
return FAILURE;
|
||||
}
|
||||
EX(function_state).function = fci_cache->function_handler;
|
||||
calling_scope = fci_cache->calling_scope;
|
||||
called_scope = fci_cache->called_scope;
|
||||
fci->object_pp = fci_cache->object_pp;
|
||||
EX(object) = fci->object_pp ? *fci->object_pp : NULL;
|
||||
if (fci->object_pp && *fci->object_pp && Z_TYPE_PP(fci->object_pp) == IS_OBJECT &&
|
||||
(!EG(objects_store).object_buckets || !EG(objects_store).object_buckets[Z_OBJ_HANDLE_PP(fci->object_pp)].valid)) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (EX(function_state).function->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) {
|
||||
|
@ -997,11 +769,7 @@ init_fci_cache:
|
|||
}
|
||||
}
|
||||
|
||||
if (call_via_handler) {
|
||||
ZEND_VM_STACK_GROW_IF_NEEDED(2 + 1);
|
||||
} else {
|
||||
ZEND_VM_STACK_GROW_IF_NEEDED(fci->param_count + 1);
|
||||
}
|
||||
ZEND_VM_STACK_GROW_IF_NEEDED(fci->param_count + 1);
|
||||
|
||||
for (i=0; i<fci->param_count; i++) {
|
||||
zval *param;
|
||||
|
@ -1019,10 +787,6 @@ init_fci_cache:
|
|||
zend_vm_stack_clear_multiple(TSRMLS_C);
|
||||
}
|
||||
|
||||
if (call_via_handler) {
|
||||
zval_ptr_dtor(&method_name);
|
||||
zval_ptr_dtor(¶ms_array);
|
||||
}
|
||||
zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given",
|
||||
i+1,
|
||||
EX(function_state).function->common.scope ? EX(function_state).function->common.scope->name : "",
|
||||
|
@ -1049,17 +813,7 @@ init_fci_cache:
|
|||
*param = **(fci->params[i]);
|
||||
INIT_PZVAL(param);
|
||||
}
|
||||
if (call_via_handler) {
|
||||
add_next_index_zval(params_array, param);
|
||||
} else {
|
||||
zend_vm_stack_push_nocheck(param TSRMLS_CC);
|
||||
}
|
||||
}
|
||||
|
||||
if (call_via_handler) {
|
||||
zend_vm_stack_push_nocheck(method_name TSRMLS_CC);
|
||||
zend_vm_stack_push_nocheck(params_array TSRMLS_CC);
|
||||
fci->param_count = 2;
|
||||
zend_vm_stack_push_nocheck(param TSRMLS_CC);
|
||||
}
|
||||
|
||||
EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C);
|
||||
|
@ -1097,19 +851,6 @@ init_fci_cache:
|
|||
}
|
||||
} else {
|
||||
EG(This) = NULL;
|
||||
if (calling_scope && !(EX(function_state).function->common.fn_flags & ZEND_ACC_STATIC)) {
|
||||
int severity;
|
||||
char *verb;
|
||||
if (EX(function_state).function->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
|
||||
severity = E_STRICT;
|
||||
verb = "should not";
|
||||
} else {
|
||||
/* An internal function assumes $this is present and won't check that. So PHP would crash by allowing the call. */
|
||||
severity = E_ERROR;
|
||||
verb = "cannot";
|
||||
}
|
||||
zend_error(severity, "Non-static method %s::%s() %s be called statically", calling_scope->name, EX(function_state).function->common.function_name, verb);
|
||||
}
|
||||
}
|
||||
|
||||
EX(prev_execute_data) = EG(current_execute_data);
|
||||
|
@ -1146,6 +887,8 @@ init_fci_cache:
|
|||
EG(return_value_ptr_ptr)=original_return_value;
|
||||
EG(opline_ptr) = original_opline_ptr;
|
||||
} else {
|
||||
int call_via_handler = (EX(function_state).function->common.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0;
|
||||
|
||||
ALLOC_INIT_ZVAL(*fci->retval_ptr_ptr);
|
||||
if (EX(function_state).function->common.scope) {
|
||||
EG(scope) = EX(function_state).function->common.scope;
|
||||
|
@ -1161,12 +904,13 @@ init_fci_cache:
|
|||
zval_ptr_dtor(fci->retval_ptr_ptr);
|
||||
*fci->retval_ptr_ptr = NULL;
|
||||
}
|
||||
|
||||
if (call_via_handler) {
|
||||
/* We must re-initialize function again */
|
||||
fci_cache->initialized = 0;
|
||||
}
|
||||
}
|
||||
zend_vm_stack_clear_multiple(TSRMLS_C);
|
||||
if (call_via_handler) {
|
||||
zval_ptr_dtor(&method_name);
|
||||
zval_ptr_dtor(¶ms_array);
|
||||
}
|
||||
|
||||
if (EG(This)) {
|
||||
zval_ptr_dtor(&EG(This));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue