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