From f7a0a44b8451fb27065264e908727a009d730ea8 Mon Sep 17 00:00:00 2001 From: Marcus Boerger Date: Sat, 2 Feb 2008 15:23:22 +0000 Subject: [PATCH] - MFB static callable message mess --- Zend/zend_API.c | 169 ++++++++++++++++++++++++++-------------- Zend/zend_API.h | 5 +- Zend/zend_execute_API.c | 5 +- Zend/zend_vm_def.h | 11 ++- 4 files changed, 129 insertions(+), 61 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 07e47ded71..3613c9eec9 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -25,6 +25,7 @@ #include "zend_API.h" #include "zend_modules.h" #include "zend_constants.h" +#include "zend_exceptions.h" #ifdef HAVE_STDARG_H #include @@ -313,7 +314,7 @@ static int parse_arg_object_to_string(zval **arg, char **p, int *pl, int type TS } /* }}} */ -static char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, char **spec, char T_arg_type, int* ret_type TSRMLS_DC) /* {{{ */ +static char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, char **spec, char T_arg_type, int* ret_type, char **error, int *severity TSRMLS_DC) /* {{{ */ { char *spec_walk = *spec; char c = *spec_walk++; @@ -770,21 +771,15 @@ static char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, char **sp } if (ce_base) { if ((!*pce || !instanceof_function(*pce, ce_base TSRMLS_CC))) { - char *space; - zstr class_name = get_active_class_name(&space TSRMLS_CC); - zend_error(E_WARNING, "%v%s%v() expects parameter %d to be a class name derived from %v, '%v' given", - class_name, space, get_active_function_name(TSRMLS_C), - arg_num, ce_base->name, Z_STRVAL_PP(arg)); + zend_spprintf(error, 0, "to be a class name derived from %s, '%s' given", + ce_base->name, Z_STRVAL_PP(arg)); *pce = NULL; return ""; } } if (!*pce) { - char *space; - zstr class_name = get_active_class_name(&space TSRMLS_CC); - zend_error(E_WARNING, "%v%s%v() expects parameter %d to be a valid class name, '%v' given", - class_name, space, get_active_function_name(TSRMLS_C), - arg_num, Z_STRVAL_PP(arg)); + zend_spprintf(error, 0, "to be a valid class name, '%s' given", + Z_STRVAL_PP(arg)); return ""; } break; @@ -796,6 +791,7 @@ static char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, char **sp { zend_fcall_info *fci = va_arg(*va, zend_fcall_info *); zend_fcall_info_cache *fcc = va_arg(*va, zend_fcall_info_cache *); + char *is_callable_error = NULL; if (return_null) { fci->size = 0; @@ -803,10 +799,24 @@ static char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, char **sp break; } - if (zend_fcall_info_init(*arg, fci, fcc, NULL TSRMLS_CC) == SUCCESS) { + if (zend_fcall_info_init(*arg, 0, fci, fcc, NULL, &is_callable_error TSRMLS_CC) == SUCCESS) { + if (is_callable_error) { + *severity = E_STRICT; + zend_spprintf(error, 0, "to be a valid callback, %s", is_callable_error); + efree(is_callable_error); + *spec = spec_walk; + return ""; + } break; } else { - return "valid callback"; + if (is_callable_error) { + *severity = E_WARNING; + zend_spprintf(error, 0, "to be a valid callback, %s", is_callable_error); + efree(is_callable_error); + return ""; + } else { + return "valid callback"; + } } } @@ -844,20 +854,29 @@ static char *zend_parse_arg_impl(int arg_num, zval **arg, va_list *va, char **sp static int zend_parse_arg(int arg_num, zval **arg, va_list *va, char **spec, int quiet, char T_arg_type TSRMLS_DC) /* {{{ */ { - char *expected_type = NULL; + char *expected_type = NULL, *error = NULL; int ret_type = IS_STRING; + int severity = E_WARNING; - expected_type = zend_parse_arg_impl(arg_num, arg, va, spec, T_arg_type, &ret_type TSRMLS_CC); + expected_type = zend_parse_arg_impl(arg_num, arg, va, spec, T_arg_type, &ret_type, &error, &severity TSRMLS_CC); if (expected_type) { - if (!quiet && *expected_type) { + if (!quiet && (*expected_type || error)) { char *space; zstr class_name = get_active_class_name(&space TSRMLS_CC); - zend_error(E_WARNING, "%v%s%v() expects parameter %d to be %R, %s given", - class_name, space, get_active_function_name(TSRMLS_C), arg_num, ret_type, expected_type, - zend_zval_type_name(*arg)); + if (error) { + zend_error(severity, "%v%s%v() expects parameter %d %s", + class_name, space, get_active_function_name(TSRMLS_C), arg_num, error); + efree(error); + } else { + zend_error(severity, "%v%s%v() expects parameter %d to be %R, %s given", + class_name, space, get_active_function_name(TSRMLS_C), arg_num, ret_type, expected_type, + zend_zval_type_name(*arg)); + } + } + if (severity != E_STRICT) { + return FAILURE; } - return FAILURE; } return SUCCESS; @@ -2625,7 +2644,7 @@ ZEND_API int zend_disable_class(char *class_name, uint class_name_length TSRMLS_ } /* }}} */ -static int zend_is_callable_check_func(int check_flags, zval ***zobj_ptr_ptr, zend_class_entry *ce_org, zval *callable, zend_class_entry **ce_ptr, zend_function **fptr_ptr TSRMLS_DC) /* {{{ */ +static int zend_is_callable_check_func(int check_flags, zval ***zobj_ptr_ptr, zend_class_entry *ce_org, zval *callable, zend_class_entry **ce_ptr, zend_function **fptr_ptr, char **error TSRMLS_DC) /* {{{ */ { int retval; zstr lmname, mname, colon = NULL_ZSTR; @@ -2686,6 +2705,7 @@ static int zend_is_callable_check_func(int check_flags, zval ***zobj_ptr_ptr, ze colon.s--; clen = colon.s - Z_STRVAL_P(callable); if (clen == 0) { + if (error) zend_spprintf(error, 0, "invalid function name"); return 0; } mlen = Z_STRLEN_P(callable) - clen - 2; @@ -2699,10 +2719,14 @@ static int zend_is_callable_check_func(int check_flags, zval ***zobj_ptr_ptr, ze * Try to fetch class and then find static method. */ *ce_ptr = zend_u_fetch_class(Z_TYPE_P(callable), Z_UNIVAL_P(callable), clen, ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_SILENT TSRMLS_CC); if (!*ce_ptr) { + char *cname = estrndup(Z_STRVAL_P(callable), clen); + if (error) zend_spprintf(error, 0, "class '%s' not found", cname); + efree(cname); return 0; } ftable = &(*ce_ptr)->function_table; if (ce_org && !instanceof_function(ce_org, *ce_ptr TSRMLS_CC)) { + if (error) zend_spprintf(error, 0, "class '%s' is not a subclass of '%s'", ce_org->name, (*ce_ptr)->name); return 0; } lmname = zend_u_str_case_fold(Z_TYPE_P(callable), mname, mlen, 1, &mlen); @@ -2713,6 +2737,7 @@ static int zend_is_callable_check_func(int check_flags, zval ***zobj_ptr_ptr, ze *ce_ptr = ce_org; } else { /* We already checked for plain function before. */ + if (error) zend_spprintf(error, 0, "function '%s' not found or invalid function name", Z_STRVAL_P(callable)); return 0; } @@ -2725,24 +2750,53 @@ static int zend_is_callable_check_func(int check_flags, zval ***zobj_ptr_ptr, ze } else if (!*zobj_ptr_ptr && *ce_ptr && (*ce_ptr)->__callstatic) { retval = 1; *fptr_ptr = (*ce_ptr)->__callstatic; + } else { + if (*ce_ptr) { + if (error) zend_spprintf(error, 0, "class '%s' does not have a method '%s'", (*ce_ptr)->name, lmname); + } else { + if (error) zend_spprintf(error, 0, "function '%s' does not exist", lmname); + } } } else { *fptr_ptr = fptr; if (*ce_ptr) { if (!*zobj_ptr_ptr && !(fptr->common.fn_flags & ZEND_ACC_STATIC)) { + int severity; + char *verb; + if (fptr->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { + severity = E_STRICT; + verb = "should not"; + } else { + severity = E_ERROR; + verb = "cannot"; + } if ((check_flags & IS_CALLABLE_CHECK_IS_STATIC) != 0) { retval = 0; + } + if (EG(This) && instanceof_function(Z_OBJCE_P(EG(This)), *ce_ptr TSRMLS_CC)) { + *zobj_ptr_ptr = &EG(This); + if (error) { + zend_spprintf(error, 0, "non-static method %s::%s() %s be called statically, assuming $this from compatible context %s", (*ce_ptr)->name, fptr->common.function_name, verb, Z_OBJCE_P(EG(This))->name); + } else if (retval) { + zend_error(severity, "Non-static method %s::%s() %s be called statically, assuming $this from compatible context %s", (*ce_ptr)->name, fptr->common.function_name, verb, Z_OBJCE_P(EG(This))->name); + } } else { - zend_error(E_STRICT, "Non-static method %v::%v() cannot be called statically", (*ce_ptr)->name, fptr->common.function_name); + if (error) { + zend_spprintf(error, 0, "non-static method %s::%s() %s be called statically", (*ce_ptr)->name, fptr->common.function_name, verb); + } else if (retval) { + zend_error(severity, "Non-static method %s::%s() %s be called statically", (*ce_ptr)->name, fptr->common.function_name, verb); + } } } if (retval && (check_flags & IS_CALLABLE_CHECK_NO_ACCESS) == 0) { if (fptr->op_array.fn_flags & ZEND_ACC_PRIVATE) { if (!zend_check_private(fptr, *zobj_ptr_ptr ? Z_OBJCE_PP(*zobj_ptr_ptr) : EG(scope), lmname, mlen TSRMLS_CC)) { + if (error) zend_spprintf(error, 0, "cannot access private method %s::%s()", (*ce_ptr)->name, fptr->common.function_name); retval = 0; } } else if ((fptr->common.fn_flags & ZEND_ACC_PROTECTED)) { if (!zend_check_protected(fptr->common.scope, EG(scope))) { + if (error) zend_spprintf(error, 0, "cannot access protected method %s::%s()", (*ce_ptr)->name, fptr->common.function_name); retval = 0; } } @@ -2754,7 +2808,7 @@ static int zend_is_callable_check_func(int check_flags, zval ***zobj_ptr_ptr, ze } /* }}} */ -ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, zval *callable_name, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval ***zobj_ptr_ptr TSRMLS_DC) /* {{{ */ +ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, zval *callable_name, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval ***zobj_ptr_ptr, char **error TSRMLS_DC) /* {{{ */ { zstr lcname; unsigned int lcname_len; @@ -2774,6 +2828,9 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, zval *c if (zobj_ptr_ptr == NULL) { zobj_ptr_ptr = &zobj_ptr_local; } + if (error) { + *error = NULL; + } *ce_ptr = NULL; *fptr_ptr = NULL; *zobj_ptr_ptr = NULL; @@ -2790,7 +2847,7 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, zval *c return 1; } - return zend_is_callable_check_func(check_flags|IS_CALLABLE_CHECK_IS_STATIC, zobj_ptr_ptr, NULL, callable, ce_ptr, fptr_ptr TSRMLS_CC); + return zend_is_callable_check_func(check_flags, zobj_ptr_ptr, NULL, callable, ce_ptr, fptr_ptr, error TSRMLS_CC); case IS_ARRAY: { @@ -2935,10 +2992,23 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, zval *c } if (ce) { - return zend_is_callable_check_func(check_flags, zobj_ptr_ptr, ce, *method, ce_ptr, fptr_ptr TSRMLS_CC); + return zend_is_callable_check_func(check_flags, zobj_ptr_ptr, ce, *method, ce_ptr, fptr_ptr, error TSRMLS_CC); + } else { + if (error) zend_spprintf(error, 0, "first array member is not a valid %s", Z_TYPE_PP(obj) == IS_STRING ? "class name" : "object"); + } + } else { + if (zend_hash_num_elements(Z_ARRVAL_P(callable)) == 2) { + if (!obj) { + if (error) zend_spprintf(error, 0, "first array member is not a valid class name or object"); + } else { + if (error) zend_spprintf(error, 0, "second array member is not a valid method"); + } + } else { + if (error) zend_spprintf(error, 0, "array must have exactly two members"); + } + if (callable_name) { + ZVAL_ASCII_STRINGL(callable_name, "Array", sizeof("Array")-1, 1); } - } else if (callable_name) { - ZVAL_ASCII_STRINGL(callable_name, "Array", sizeof("Array")-1, 1); } *ce_ptr = ce; } @@ -2950,6 +3020,7 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, zval *c zval_copy_ctor(callable_name); convert_to_text(callable_name); } + if (error) zend_spprintf(error, 0, "no array or string given"); return 0; } } @@ -2959,52 +3030,36 @@ ZEND_API zend_bool zend_is_callable(zval *callable, uint check_flags, zval *call { TSRMLS_FETCH(); - return zend_is_callable_ex(callable, check_flags, callable_name, NULL, NULL, NULL TSRMLS_CC); + return zend_is_callable_ex(callable, check_flags, callable_name, NULL, NULL, NULL, NULL TSRMLS_CC); } /* }}} */ ZEND_API zend_bool zend_make_callable(zval *callable, zval *callable_name TSRMLS_DC) /* {{{ */ { - char *lcname, *func, *class_name; - zend_bool retval = 0; - zend_class_entry **pce; - int class_name_len; + zend_class_entry *ce; + zend_function *fptr; + zval **zobj_ptr; - if (zend_is_callable_ex(callable, IS_CALLABLE_STRICT, callable_name, NULL, NULL, NULL TSRMLS_CC)) { + if (zend_is_callable_ex(callable, IS_CALLABLE_STRICT, callable_name, &ce, &fptr, &zobj_ptr, NULL TSRMLS_CC)) { + if ((Z_TYPE_P(callable) == IS_STRING || Z_TYPE_P(callable) == IS_UNICODE) && ce) { + zval_dtor(callable); + array_init(callable); + add_next_index_text(callable, ce->name, 1); + add_next_index_text(callable, fptr->common.function_name, 1); + } return 1; } - switch (Z_TYPE_P(callable)) { - case IS_STRING: - lcname = zend_str_tolower_dup(Z_STRVAL_P(callable), Z_STRLEN_P(callable)); - - if ((func = strstr(lcname, "::")) != NULL) { - *func = '\0'; - class_name_len = func - lcname; - class_name = estrndup(Z_STRVAL_P(callable), class_name_len); - if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC) == SUCCESS) { - zval_dtor(callable); - array_init(callable); - add_next_index_stringl(callable, lcname, class_name_len, 1); - func += 2; - add_next_index_stringl(callable, func, strlen(func), 1); - retval = 1; - } - efree(class_name); - } - efree(lcname); - break; - } - return retval; + return 0; } /* }}} */ -ZEND_API int zend_fcall_info_init(zval *callable, zend_fcall_info *fci, zend_fcall_info_cache *fcc, zval *callable_name TSRMLS_DC) /* {{{ */ +ZEND_API int zend_fcall_info_init(zval *callable, uint check_flags, zend_fcall_info *fci, zend_fcall_info_cache *fcc, zval *callable_name, char **error TSRMLS_DC) /* {{{ */ { zend_class_entry *ce; zend_function *func; zval **obj; - if (!zend_is_callable_ex(callable, IS_CALLABLE_STRICT, callable_name, &ce, &func, &obj TSRMLS_CC)) { + if (!zend_is_callable_ex(callable, check_flags, callable_name, &ce, &func, &obj, error TSRMLS_CC)) { return FAILURE; } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 7aa24150a2..7d0c8895a3 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -233,7 +233,7 @@ ZEND_API void zend_wrong_param_count(TSRMLS_D); #define IS_CALLABLE_STRICT (IS_CALLABLE_CHECK_IS_STATIC) -ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, zval *callable_name, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval ***zobj_ptr_ptr TSRMLS_DC); +ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, zval *callable_name, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval ***zobj_ptr_ptr, char **error TSRMLS_DC); ZEND_API zend_bool zend_is_callable(zval *callable, uint check_flags, zval *callable_name); ZEND_API zend_bool zend_make_callable(zval *callable, zval *callable_name TSRMLS_DC); ZEND_API const char *zend_get_module_version(const char *module_name); @@ -1534,8 +1534,9 @@ ZEND_API extern zend_fcall_info_cache empty_fcall_info_cache; * fci->param_count = 0; * fci->params = NULL; * The callable_name argument may be NULL. + * Set check_flags to IS_CALLABLE_STRICT for every new usage! */ -ZEND_API int zend_fcall_info_init(zval *callable, zend_fcall_info *fci, zend_fcall_info_cache *fcc, zval *callable_name TSRMLS_DC); +ZEND_API int zend_fcall_info_init(zval *callable, uint check_flags, zend_fcall_info *fci, zend_fcall_info_cache *fcc, zval *callable_name, char **error TSRMLS_DC); /** Clear argumens connected with zend_fcall_info *fci * If free_mem is not zero then the params array gets free'd as well diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index ee0fbaa62e..cec985c7be 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1145,12 +1145,15 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS 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 { severity = E_ERROR; + verb = "cannot"; } - zend_error(severity, "Non-static method %v::%v() cannot be called statically", calling_scope->name, EX(function_state).function->common.function_name); + zend_error(severity, "Non-static method %v::%v() %s be called statically", calling_scope->name, EX(function_state).function->common.function_name, verb); } } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index e2f8e853c1..9c58613124 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2026,7 +2026,16 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMP|VAR|UNUS !instanceof_function(Z_OBJCE_P(EG(This)), ce TSRMLS_CC)) { /* We are calling method of the other (incompatible) class, but passing $this. This is done for compatibility with php-4. */ - zend_error(E_STRICT, "Non-static method %v::%v() should not be called statically, assuming $this from incompatible context", EX(fbc)->common.scope->name, EX(fbc)->common.function_name); + int severity; + char *verb; + if (EX(fbc)->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { + severity = E_STRICT; + verb = "should not"; + } else { + severity = E_ERROR; + verb = "cannot"; + } + zend_error(severity, "Non-static method %v::%v() %s be called statically, assuming $this from incompatible context", EX(fbc)->common.scope->name, EX(fbc)->common.function_name, verb); } if ((EX(object) = EG(This))) { -- 2.50.1