From: Marcus Boerger Date: Fri, 16 Dec 2005 22:15:41 +0000 (+0000) Subject: - Simplify and synch is_callable_ex() with actual execution code X-Git-Tag: php-5.1.2RC1~98 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0cd997d79c1601e72047f8faea8614aaeaebc053;p=php - Simplify and synch is_callable_ex() with actual execution code . Allow array($this, 'parent::method') for function 'pointers' . Spit out E_STRICT in case of erroneous use of function 'pointers' --- diff --git a/Zend/zend_API.c b/Zend/zend_API.c index bae16c5492..81921ff574 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1974,15 +1974,94 @@ ZEND_API int zend_disable_class(char *class_name, uint class_name_length TSRMLS_ return 1; } -ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, char **callable_name, int *callable_name_len, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval ***zobj_ptr_ptr TSRMLS_DC) +static int zend_is_callable_check_func(int check_flags, zval **zobj_ptr, zend_class_entry *ce_org, zval *callable, zend_class_entry **ce_ptr, zend_function **fptr_ptr TSRMLS_DC) { + int retval; char *lcname, *lmname, *colon; + int clen, mlen; + zend_function *fptr; + zend_class_entry **pce; + HashTable *ftable; + + *ce_ptr = NULL; + *fptr_ptr = NULL; + + if ((colon = strstr(Z_STRVAL_P(callable), "::")) != NULL) { + clen = colon - Z_STRVAL_P(callable); + mlen = Z_STRLEN_P(callable) - clen - 2; + if (zend_lookup_class(Z_STRVAL_P(callable), clen, &pce TSRMLS_CC) == SUCCESS) { + *ce_ptr = *pce; + } else { + lcname = zend_str_tolower_dup(Z_STRVAL_P(callable), clen); + /* caution: lcname is not '\0' terminated */ + if (clen == sizeof("self") - 1 && memcmp(lcname, "self", sizeof("self") - 1) == 0) { + *ce_ptr = EG(scope); + } else if (clen == sizeof("parent") - 1 && memcmp(lcname, "parent", sizeof("parent") - 1) == 0 && EG(active_op_array)->scope) { + *ce_ptr = EG(scope) ? EG(scope)->parent : NULL; + } + efree(lcname); + } + if (!*ce_ptr) { + return 0; + } + ftable = &(*ce_ptr)->function_table; + if (ce_org && !instanceof_function(ce_org, *ce_ptr TSRMLS_CC)) { + return 0; + } + lmname = zend_str_tolower_dup(Z_STRVAL_P(callable) + clen + 2, mlen); + } else { + mlen = Z_STRLEN_P(callable); + lmname = zend_str_tolower_dup(Z_STRVAL_P(callable), mlen); + if (ce_org) { + ftable = &ce_org->function_table; + *ce_ptr = ce_org; + } else { + ftable = EG(function_table); + } + } + + retval = zend_hash_find(ftable, lmname, mlen+1, (void**)&fptr) == SUCCESS ? 1 : 0; + + if (!retval) { + if (zobj_ptr && *ce_ptr && (*ce_ptr)->__call != 0) { + retval = (*ce_ptr)->__call != NULL; + *fptr_ptr = (*ce_ptr)->__call; + } + } else { + *fptr_ptr = fptr; + if (*ce_ptr) { + if (!zobj_ptr && !(fptr->common.fn_flags & ZEND_ACC_STATIC)) { + if ((check_flags & IS_CALLABLE_CHECK_IS_STATIC) != 0) { + retval = 0; + } else { + zend_error(E_STRICT, "Non-static method %s::%s() cannot be called statically", (*ce_ptr)->name, fptr->common.function_name); + } + } + 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 ? Z_OBJCE_PP(zobj_ptr) : EG(scope), lmname, mlen TSRMLS_CC)) { + retval = 0; + } + } else if ((fptr->common.fn_flags & ZEND_ACC_PROTECTED)) { + if (!zend_check_protected(fptr->common.scope, EG(scope))) { + retval = 0; + } + } + } + } + } + efree(lmname); + return retval; +} + +ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, char **callable_name, int *callable_name_len, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval ***zobj_ptr_ptr TSRMLS_DC) +{ + char *lcname; zend_bool retval = 0; - int callable_name_len_local, clen, mlen; + int callable_name_len_local; zend_class_entry *ce_local, **pce; zend_function *fptr_local; zval **zobj_ptr_local; - HashTable *ftable; if (callable_name_len == NULL) { callable_name_len = &callable_name_len_local; @@ -2010,33 +2089,7 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, char ** return 1; } - if ((colon = strstr(Z_STRVAL_P(callable), "::")) != NULL) { - clen = colon - Z_STRVAL_P(callable); - mlen = Z_STRLEN_P(callable) - clen - 2; - lcname = estrndup(Z_STRVAL_P(callable), clen); - lcname[clen] = '\0'; - lmname = zend_str_tolower_dup(Z_STRVAL_P(callable) + clen + 2, mlen); - if (zend_lookup_class(lcname, clen, &pce TSRMLS_CC) == FAILURE) { - efree(lcname); - efree(lmname); - return 0; - } - efree(lcname); - *ce_ptr = *pce; - ftable = &(*ce_ptr)->function_table; - } else { - mlen = Z_STRLEN_P(callable); - lmname = zend_str_tolower_dup(Z_STRVAL_P(callable), mlen); - ftable = EG(function_table); - } - - if (zend_hash_find(ftable, lmname, mlen+1, (void**)fptr_ptr) == SUCCESS) { - retval = 1; - } - if (*ce_ptr && *fptr_ptr && !((*fptr_ptr)->common.fn_flags & ZEND_ACC_STATIC)) { - retval = 0; - } - efree(lmname); + retval = zend_is_callable_check_func(check_flags|IS_CALLABLE_CHECK_IS_STATIC, NULL, NULL, callable, ce_ptr, fptr_ptr TSRMLS_CC); break; case IS_ARRAY: @@ -2065,7 +2118,6 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, char ** } if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) { - *ce_ptr = ce; return 1; } @@ -2080,6 +2132,12 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, char ** } efree(lcname); } + if (EG(This)) { + if (instanceof_function(Z_OBJCE_P(EG(This)), ce TSRMLS_CC)) { + *zobj_ptr_ptr = &EG(This); + zend_error(E_STRICT, "Non-static method %s::%s() canot be called statically, assuming $this from compatible context %s", ce->name, Z_STRVAL_PP(method), Z_OBJCE_P(EG(This))->name); + } + } } else { ce = Z_OBJCE_PP(obj); /* TBFixed: what if it's overloaded? */ @@ -2104,38 +2162,12 @@ ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, char ** } if (ce) { - zend_function *fbc; - - lcname = zend_str_tolower_dup(Z_STRVAL_PP(method), Z_STRLEN_PP(method)); - if (zend_hash_find(&ce->function_table, lcname, Z_STRLEN_PP(method)+1, (void **)&fbc) == SUCCESS) { - retval = 1; - if ((check_flags & IS_CALLABLE_CHECK_NO_ACCESS) == 0) { - if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) { - if (!zend_check_private(fbc, (Z_TYPE_PP(obj) == IS_STRING)?EG(scope):(*obj)->value.obj.handlers->get_class_entry(*obj TSRMLS_CC), lcname, Z_STRLEN_PP(method) TSRMLS_CC)) { - retval = 0; - } - } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) { - if (!zend_check_protected(fbc->common.scope, EG(scope))) { - retval = 0; - } - } - } - *fptr_ptr = fbc; - } - /* check for __call too */ - if (retval == 0 && *zobj_ptr_ptr && ce->__call != 0) { - retval = 1; - *fptr_ptr = ce->__call; - } - efree(lcname); + retval = zend_is_callable_check_func(check_flags, *zobj_ptr_ptr, ce, *method, ce_ptr, fptr_ptr TSRMLS_CC); } } else if (callable_name) { *callable_name = estrndup("Array", sizeof("Array")-1); *callable_name_len = sizeof("Array") - 1; } - if ((check_flags & IS_CALLABLE_CHECK_IS_STATIC) != 0 && !*zobj_ptr_ptr && (!*fptr_ptr || !((*fptr_ptr)->common.fn_flags & ZEND_ACC_STATIC))) { - retval = 0; - } *ce_ptr = ce; } break; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index d30d7f7499..61f4d880d0 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -204,6 +204,8 @@ ZEND_API void zend_wrong_param_count(TSRMLS_D); #define IS_CALLABLE_CHECK_NO_ACCESS (1<<1) #define IS_CALLABLE_CHECK_IS_STATIC (1<<2) +#define IS_CALLABLE_STRICT (IS_CALLABLE_CHECK_IS_STATIC) + ZEND_API zend_bool zend_is_callable_ex(zval *callable, uint check_flags, char **callable_name, int *callable_name_len, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval ***zobj_ptr_ptr TSRMLS_DC); ZEND_API zend_bool zend_is_callable(zval *callable, uint check_flags, char **callable_name); ZEND_API zend_bool zend_make_callable(zval *callable, char **callable_name TSRMLS_DC); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 865f566994..4618d7b85e 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -587,6 +587,8 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS zval *method_name; zval *params_array; int call_via_handler = 0; + char *fname, *colon; + int fname_len; if (EG(exception)) { return FAILURE; /* we would result in an instable executor otherwise */ @@ -706,30 +708,62 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS return FAILURE; } + fname = Z_STRVAL_P(fci->function_name); + fname_len = Z_STRLEN_P(fci->function_name); + if (calling_scope && (colon = strstr(fname, "::")) != NULL) { + int clen = colon - fname; + int mlen = fname_len - clen - 2; + zend_class_entry **pce, *ce_child; + if (zend_lookup_class(fname, clen, &pce TSRMLS_CC) == SUCCESS) { + ce_child = *pce; + } else { + char *lcname = zend_str_tolower_dup(fname, clen); + /* caution: lcname is not '\0' terminated */ + if (clen == sizeof("self") - 1 && memcmp(lcname, "self", sizeof("self") - 1) == 0) { + ce_child = EG(active_op_array) ? EG(active_op_array)->scope : NULL; + } else if (clen == sizeof("parent") - 1 && memcmp(lcname, "parent", sizeof("parent") - 1) == 0 && EG(active_op_array)->scope) { + ce_child = EG(active_op_array) && EG(active_op_array)->scope ? EG(scope)->parent : NULL; + } + efree(lcname); + } + if (!ce_child) { + zend_error(E_ERROR, "Cannot call method %s() or method does not exist", fname); + return FAILURE; + } + if (!instanceof_function(calling_scope, ce_child TSRMLS_CC)) { + zend_error(E_ERROR, "Cannot call method %s() of class %s which is not a derived from %s", fname, ce_child->name, calling_scope->name); + return 0; + } + fci->function_table = &ce_child->function_table; + calling_scope = ce_child; + fname = fname + clen + 2; + fname_len = mlen; + } + 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, Z_STRVAL_P(fci->function_name), Z_STRLEN_P(fci->function_name) TSRMLS_CC); + 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(Z_STRVAL_P(fci->function_name), Z_STRLEN_P(fci->function_name)); - if (zend_hash_find(&calling_scope->function_table, function_name_lc, fci->function_name->value.str.len+1, (void **) &EX(function_state).function)==FAILURE) { + 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, Z_STRVAL_P(fci->function_name)); + 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) { - char *function_name_lc = zend_str_tolower_dup(Z_STRVAL_P(fci->function_name), Z_STRLEN_P(fci->function_name)); + char *function_name_lc = zend_str_tolower_dup(fname, fname_len); EX(function_state).function = - zend_std_get_static_method(calling_scope, function_name_lc, Z_STRLEN_P(fci->function_name) TSRMLS_CC); + zend_std_get_static_method(calling_scope, function_name_lc, fname_len TSRMLS_CC); efree(function_name_lc); } else { - char *function_name_lc = zend_str_tolower_dup(Z_STRVAL_P(fci->function_name), Z_STRLEN_P(fci->function_name)); + char *function_name_lc = zend_str_tolower_dup(fname, fname_len); - if (zend_hash_find(fci->function_table, function_name_lc, fci->function_name->value.str.len+1, (void **) &EX(function_state).function)==FAILURE) { + if (zend_hash_find(fci->function_table, function_name_lc, fname_len+1, (void **) &EX(function_state).function)==FAILURE) { EX(function_state).function = NULL; } efree(function_name_lc);