#include "zend_API.h"
#include "zend_modules.h"
#include "zend_constants.h"
+#include "zend_exceptions.h"
#ifdef HAVE_STDARG_H
#include <stdarg.h>
}
/* }}} */
-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++;
}
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;
{
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;
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";
+ }
}
}
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;
}
/* }}} */
-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;
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;
* 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);
*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;
}
} 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;
}
}
}
/* }}} */
-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;
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;
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:
{
}
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;
}
zval_copy_ctor(callable_name);
convert_to_text(callable_name);
}
+ if (error) zend_spprintf(error, 0, "no array or string given");
return 0;
}
}
{
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;
}