php 对象调用方法
static union _zend_function *zend_std_get_method(zval **object_ptr, char *method_name, int method_len, const zend_literal *key TSRMLS_DC) /* {{{ */
{
zend_function *fbc;
zval *object = *object_ptr;
zend_object *zobj = Z_OBJ_P(object);
ulong hash_value;
char *lc_method_name;
ALLOCA_FLAG(use_heap)
if (EXPECTED(key != NULL)) {
lc_method_name = Z_STRVAL(key->constant);
hash_value = key->hash_value;
} else {
lc_method_name = do_alloca(method_len+1, use_heap);
/* Create a zend_copy_str_tolower(dest, src, src_length); */
zend_str_tolower_copy(lc_method_name, method_name, method_len);
hash_value = zend_hash_func(lc_method_name, method_len+1);
}
if (UNEXPECTED(zend_hash_quick_find(&zobj->ce->function_table, lc_method_name, method_len+1, hash_value, (void **)&fbc) == FAILURE)) {
if (UNEXPECTED(!key)) {
free_alloca(lc_method_name, use_heap);
}
if (zobj->ce->__call) {//如果找不到,调用_call()方法
return zend_get_user_call_function(zobj->ce, method_name, method_len);
} else {
return NULL;
}
}
/* Check access level */
if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
zend_function *updated_fbc;
/* Ensure that if we're calling a private function, we're allowed to do so.
* If we're not and __call() handler exists, invoke it, otherwise error out.
*/
updated_fbc = zend_check_private_int(fbc, Z_OBJ_HANDLER_P(object, get_class_entry)(object TSRMLS_CC), lc_method_name, method_len, hash_value TSRMLS_CC);
if (EXPECTED(updated_fbc != NULL)) {
fbc = updated_fbc;
} else {
if (zobj->ce->__call) {
fbc = zend_get_user_call_function(zobj->ce, method_name, method_len);
} else {
zend_error_noreturn(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), method_name, EG(scope) ? EG(scope)->name : "");
}
}
} else {
/* Ensure that we haven't overridden a private function and end up calling
* the overriding public function...
*/
if (EG(scope) &&
is_derived_class(fbc->common.scope, EG(scope)) &&
fbc->op_array.fn_flags & ZEND_ACC_CHANGED) {
zend_function *priv_fbc;
if (zend_hash_quick_find(&EG(scope)->function_table, lc_method_name, method_len+1, hash_value, (void **) &priv_fbc)==SUCCESS
&& priv_fbc->common.fn_flags & ZEND_ACC_PRIVATE
&& priv_fbc->common.scope == EG(scope)) {
fbc = priv_fbc;
}
}
if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
/* Ensure that if we're calling a protected function, we're allowed to do so.
* If we're not and __call() handler exists, invoke it, otherwise error out.
*/
if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), EG(scope)))) {
if (zobj->ce->__call) {
fbc = zend_get_user_call_function(zobj->ce, method_name, method_len);
} else {
zend_error_noreturn(E_ERROR, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), method_name, EG(scope) ? EG(scope)->name : "");
}
}
}
}
if (UNEXPECTED(!key)) {
free_alloca(lc_method_name, use_heap);
}
return fbc;
}
static inline union _zend_function *zend_get_user_call_function(zend_class_entry *ce, const char *method_name, int method_len) /* {{{ */
{
zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function));
call_user_call->type = ZEND_INTERNAL_FUNCTION;
call_user_call->module = (ce->type == ZEND_INTERNAL_CLASS) ? ce->info.internal.module : NULL;
call_user_call->handler = zend_std_call_user_call;
call_user_call->arg_info = NULL;
call_user_call->num_args = 0;
call_user_call->scope = ce;
call_user_call->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
call_user_call->function_name = estrndup(method_name, method_len);
return (union _zend_function *)call_user_call;
}
ZEND_API void zend_std_call_user_call(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
{
zend_internal_function *func = (zend_internal_function *)EG(current_execute_data)->function_state.function;
zval *method_name_ptr, *method_args_ptr;
zval *method_result_ptr = NULL;
zend_class_entry *ce = Z_OBJCE_P(this_ptr);
ALLOC_ZVAL(method_args_ptr);
INIT_PZVAL(method_args_ptr);
array_init_size(method_args_ptr, ZEND_NUM_ARGS());
if (UNEXPECTED(zend_copy_parameters_array(ZEND_NUM_ARGS(), method_args_ptr TSRMLS_CC) == FAILURE)) {
zval_dtor(method_args_ptr);
zend_error_noreturn(E_ERROR, "Cannot get arguments for __call");
RETURN_FALSE;
}
ALLOC_ZVAL(method_name_ptr);
INIT_PZVAL(method_name_ptr);
ZVAL_STRING(method_name_ptr, func->function_name, 0); /* no dup - it's a copy */
/* __call handler is called with two arguments:
method name
array of method parameters
*/
zend_call_method_with_2_params(&this_ptr, ce, &ce->__call, ZEND_CALL_FUNC_NAME, &method_result_ptr, method_name_ptr, method_args_ptr);
if (method_result_ptr) {
if (Z_ISREF_P(method_result_ptr) || Z_REFCOUNT_P(method_result_ptr) > 1) {
RETVAL_ZVAL(method_result_ptr, 1, 1);
} else {
RETVAL_ZVAL(method_result_ptr, 0, 1);
}
}
/* now destruct all auxiliaries */
zval_ptr_dtor(&method_args_ptr);
zval_ptr_dtor(&method_name_ptr);
/* destruct the function also, then - we have allocated it in get_method */
efree(func);
}
/* Ensures that we're allowed to call a private method.
* Returns the function address that should be called, or NULL
* if no such function exists.
*/
static inline zend_function *zend_check_private_int(zend_function *fbc, zend_class_entry *ce, char *function_name_strval, int function_name_strlen, ulong hash_value TSRMLS_DC) /* {{{ */
{
if (!ce) {
return 0;
}
/* We may call a private function if:
* 1. The class of our object is the same as the scope, and the private
* function (EX(fbc)) has the same scope.
* 2. One of our parent classes are the same as the scope, and it contains
* a private function with the same name that has the same scope.
*/
if (fbc->common.scope == ce && EG(scope) == ce) {
/* rule #1 checks out ok, allow the function call */
return fbc;
}
/* Check rule #2 */
ce = ce->parent;
while (ce) {
if (ce == EG(scope)) {
if (zend_hash_quick_find(&ce->function_table, function_name_strval, function_name_strlen+1, hash_value, (void **) &fbc)==SUCCESS
&& fbc->op_array.fn_flags & ZEND_ACC_PRIVATE
&& fbc->common.scope == EG(scope)) {
return fbc;
}
break;
}
ce = ce->parent;
}
return NULL;
}
ZEND_API void zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, uint size, merge_checker_func_t pMergeSource, void *pParam)
{
Bucket *p;
void *t;
IS_CONSISTENT(source);
IS_CONSISTENT(target);
p = source->pListHead;
while (p) {
if (zend_hash_replace_checker_wrapper(target, p->pData, p, pParam, pMergeSource)) {
if (zend_hash_quick_update(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t)==SUCCESS && pCopyConstructor) {
pCopyConstructor(t);
}
}
p = p->pListNext;
}
target->pInternalPointer = target->pListHead;
}
static zend_bool zend_hash_replace_checker_wrapper(HashTable *target, void *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
{
zend_hash_key hash_key;
hash_key.arKey = p->arKey;
hash_key.nKeyLength = p->nKeyLength;
hash_key.h = p->h;
return merge_checker_func(target, source_data, &hash_key, pParam);
}
static zend_bool do_inherit_property_access_check(HashTable *target_ht, zend_property_info *parent_info, const zend_hash_key *hash_key, zend_class_entry *ce) /* {{{ */
{
zend_property_info *child_info;
zend_class_entry *parent_ce = ce->parent;
if (parent_info->flags & (ZEND_ACC_PRIVATE|ZEND_ACC_SHADOW)) { //如果父类中的属性是private的,则返回0,不拷贝
if (zend_hash_quick_find(&ce->properties_info, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void **) &child_info)==SUCCESS) {
child_info->flags |= ZEND_ACC_CHANGED;
} else {
zend_hash_quick_update(&ce->properties_info, hash_key->arKey, hash_key->nKeyLength, hash_key->h, parent_info, sizeof(zend_property_info), (void **) &child_info);
if(ce->type & ZEND_INTERNAL_CLASS) {
zend_duplicate_property_info_internal(child_info);
} else {
zend_duplicate_property_info(child_info);
}
child_info->flags &= ~ZEND_ACC_PRIVATE; /* it's not private anymore */
child_info->flags |= ZEND_ACC_SHADOW; /* but it's a shadow of private */
}
return 0; /* don't copy access information to child */
}
if (zend_hash_quick_find(&ce->properties_info, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void **) &child_info)==SUCCESS) {
if ((parent_info->flags & ZEND_ACC_STATIC) != (child_info->flags & ZEND_ACC_STATIC)) {
zend_error(E_COMPILE_ERROR, "Cannot redeclare %s%s::$%s as %s%s::$%s",
(parent_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", parent_ce->name, hash_key->arKey,
(child_info->flags & ZEND_ACC_STATIC) ? "static " : "non static ", ce->name, hash_key->arKey);
}
if(parent_info->flags & ZEND_ACC_CHANGED) {
child_info->flags |= ZEND_ACC_CHANGED;
}
if ((child_info->flags & ZEND_ACC_PPP_MASK) > (parent_info->flags & ZEND_ACC_PPP_MASK)) {
zend_error(E_COMPILE_ERROR, "Access level to %s::$%s must be %s (as in class %s)%s", ce->name, hash_key->arKey, zend_visibility_string(parent_info->flags), parent_ce->name, (parent_info->flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
} else if ((child_info->flags & ZEND_ACC_STATIC) == 0) {
zval_ptr_dtor(&(ce->default_properties_table[parent_info->offset]));
ce->default_properties_table[parent_info->offset] = ce->default_properties_table[child_info->offset];
ce->default_properties_table[child_info->offset] = NULL;
child_info->offset = parent_info->offset;
}
return 0; /* Don't copy from parent */
} else {
//子类中没有找到父类中的属性
return 1; /* Copy from parent */
}
}
static void do_inheritance_check_on_method(zend_function *child, zend_function *parent TSRMLS_DC) /* {{{ */
{
zend_uint child_flags;
zend_uint parent_flags = parent->common.fn_flags;
if ((parent->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0
&& parent->common.fn_flags & ZEND_ACC_ABSTRACT
&& parent->common.scope != (child->common.prototype ? child->common.prototype->common.scope : child->common.scope)
&& child->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_IMPLEMENTED_ABSTRACT)) {
zend_error(E_COMPILE_ERROR, "Can't inherit abstract function %s::%s() (previously declared abstract in %s)",
parent->common.scope->name,
child->common.function_name,
child->common.prototype ? child->common.prototype->common.scope->name : child->common.scope->name);
}
if (parent_flags & ZEND_ACC_FINAL) {
zend_error(E_COMPILE_ERROR, "Cannot override final method %s::%s()", ZEND_FN_SCOPE_NAME(parent), child->common.function_name);
}
child_flags = child->common.fn_flags;
/* You cannot change from static to non static and vice versa.
*/
if ((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC)) {
if (child->common.fn_flags & ZEND_ACC_STATIC) {
zend_error(E_COMPILE_ERROR, "Cannot make non static method %s::%s() static in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name, ZEND_FN_SCOPE_NAME(child));
} else {
zend_error(E_COMPILE_ERROR, "Cannot make static method %s::%s() non static in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name, ZEND_FN_SCOPE_NAME(child));
}
}
/* Disallow making an inherited method abstract. */
if ((child_flags & ZEND_ACC_ABSTRACT) && !(parent_flags & ZEND_ACC_ABSTRACT)) {
zend_error(E_COMPILE_ERROR, "Cannot make non abstract method %s::%s() abstract in class %s", ZEND_FN_SCOPE_NAME(parent), child->common.function_name, ZEND_FN_SCOPE_NAME(child));
}
if (parent_flags & ZEND_ACC_CHANGED) {
child->common.fn_flags |= ZEND_ACC_CHANGED;
} else {
/* Prevent derived classes from restricting access that was available in parent classes
*/
if ((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) {
zend_error(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
} else if (((child_flags & ZEND_ACC_PPP_MASK) < (parent_flags & ZEND_ACC_PPP_MASK))
&& ((parent_flags & ZEND_ACC_PPP_MASK) & ZEND_ACC_PRIVATE)) {
child->common.fn_flags |= ZEND_ACC_CHANGED;
}
}
if (parent_flags & ZEND_ACC_PRIVATE) {
child->common.prototype = NULL;
} else if (parent_flags & ZEND_ACC_ABSTRACT) {
child->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT;
child->common.prototype = parent;
} else if (!(parent->common.fn_flags & ZEND_ACC_CTOR) || (parent->common.prototype && (parent->common.prototype->common.scope->ce_flags & ZEND_ACC_INTERFACE))) {
/* ctors only have a prototype if it comes from an interface */
child->common.prototype = parent->common.prototype ? parent->common.prototype : parent;
}
if (child->common.prototype && (child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) {
if (!zend_do_perform_implementation_check(child, child->common.prototype TSRMLS_CC)) {
zend_error(E_COMPILE_ERROR, "Declaration of %s::%s() must be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, zend_get_function_declaration(child->common.prototype? child->common.prototype : parent TSRMLS_CC));
}
} else if (EG(error_reporting) & E_STRICT || EG(user_error_handler)) { /* Check E_STRICT (or custom error handler) before the check so that we save some time */
if (!zend_do_perform_implementation_check(child, parent TSRMLS_CC)) {
char *method_prototype = zend_get_function_declaration(child->common.prototype? child->common.prototype : parent TSRMLS_CC);
zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with %s", ZEND_FN_SCOPE_NAME(child), child->common.function_name, method_prototype);
efree(method_prototype);
}
}
}