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);
        }
    }
}

 

原文地址:https://www.cnblogs.com/taek/p/4133080.html