zend_string *lc_parent_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
zend_class_entry *parent_ce = zend_hash_find_ex_ptr(EG(class_table), lc_parent_name, 1);
- if (parent_ce && zend_can_early_bind(ce, parent_ce)) {
- zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, Z_STR_P(lcname));
- if (UNEXPECTED(!zv)) {
- zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
- } else {
- zend_do_inheritance(ce, parent_ce);
- zend_build_properties_info_table(ce);
- if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
- zend_verify_abstract_class(ce);
- }
- ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE));
- ce->ce_flags |= ZEND_ACC_LINKED;
- }
+ if (parent_ce) {
+ zend_try_early_bind(ce, parent_ce, Z_STR_P(lcname), zv);
}
}
opline_num = op_array->opcodes[opline_num].result.opline_num;
if (parent_ce
&& !(CG(compiler_options) & ZEND_COMPILE_PRELOAD) /* delay inheritance till preloading */
&& ((parent_ce->type != ZEND_INTERNAL_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES))
- && ((parent_ce->type != ZEND_USER_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) || (parent_ce->info.user.filename == ce->info.user.filename))
- && zend_can_early_bind(ce, parent_ce)
- ) {
- if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
- CG(zend_lineno) = decl->end_lineno;
- zend_do_inheritance(ce, parent_ce);
- zend_build_properties_info_table(ce);
- if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
- zend_verify_abstract_class(ce);
- }
- ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE));
- ce->ce_flags |= ZEND_ACC_LINKED;
+ && ((parent_ce->type != ZEND_USER_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) || (parent_ce->info.user.filename == ce->info.user.filename))) {
+
+ CG(zend_lineno) = decl->end_lineno;
+ if (zend_try_early_bind(ce, parent_ce, lcname, NULL)) {
CG(zend_lineno) = ast->lineno;
zend_string_release(lcname);
return NULL;
}
+ CG(zend_lineno) = ast->lineno;
}
} else {
if (EXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL)) {
}
}
-static void do_inheritance_check_on_method(zend_function *child, zend_function *parent, zend_class_entry *ce, zval *child_zv) /* {{{ */
+static zend_always_inline inheritance_status do_inheritance_check_on_method_ex(zend_function *child, zend_function *parent, zend_class_entry *ce, zval *child_zv, zend_bool check_only, zend_bool checked) /* {{{ */
{
uint32_t child_flags;
uint32_t parent_flags = parent->common.fn_flags;
zend_function *proto;
- if (UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) {
+ if (!checked && UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) {
+ if (check_only) {
+ return INHERITANCE_ERROR;
+ }
zend_error_at_noreturn(E_COMPILE_ERROR, NULL, func_lineno(child),
"Cannot override final method %s::%s()",
ZEND_FN_SCOPE_NAME(parent), ZSTR_VAL(child->common.function_name));
child_flags = child->common.fn_flags;
/* You cannot change from static to non static and vice versa.
*/
- if (UNEXPECTED((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC))) {
+ if (!checked && UNEXPECTED((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC))) {
+ if (check_only) {
+ return INHERITANCE_ERROR;
+ }
if (child_flags & ZEND_ACC_STATIC) {
zend_error_at_noreturn(E_COMPILE_ERROR, NULL, func_lineno(child),
"Cannot make non static method %s::%s() static in class %s",
}
/* Disallow making an inherited method abstract. */
- if (UNEXPECTED((child_flags & ZEND_ACC_ABSTRACT) > (parent_flags & ZEND_ACC_ABSTRACT))) {
+ if (!checked && UNEXPECTED((child_flags & ZEND_ACC_ABSTRACT) > (parent_flags & ZEND_ACC_ABSTRACT))) {
+ if (check_only) {
+ return INHERITANCE_ERROR;
+ }
zend_error_at_noreturn(E_COMPILE_ERROR, NULL, func_lineno(child),
"Cannot make non abstract method %s::%s() abstract in class %s",
ZEND_FN_SCOPE_NAME(parent), ZSTR_VAL(child->common.function_name), ZEND_FN_SCOPE_NAME(child));
}
- if (parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED)) {
+ if (!check_only && (parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED))) {
child->common.fn_flags |= ZEND_ACC_CHANGED;
}
if (parent_flags & ZEND_ACC_PRIVATE) {
- return;
+ return INHERITANCE_SUCCESS;
}
proto = parent->common.prototype ?
/* ctors only have a prototype if is abstract (or comes from an interface) */
/* and if that is the case, we want to check inheritance against it */
if (!(proto->common.fn_flags & ZEND_ACC_ABSTRACT)) {
- return;
+ return INHERITANCE_SUCCESS;
}
parent = proto;
}
- if (child_zv && child->common.prototype != proto) {
+ if (!check_only && child_zv && child->common.prototype != proto) {
do {
if (child->common.scope != ce
&& child->type == ZEND_USER_FUNCTION
}
/* Prevent derived classes from restricting access that was available in parent classes (except deriving from non-abstract ctors) */
- if ((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) {
+ if (!checked && (child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) {
+ if (check_only) {
+ return INHERITANCE_ERROR;
+ }
zend_error_at_noreturn(E_COMPILE_ERROR, NULL, func_lineno(child),
"Access level to %s::%s() must be %s (as in class %s)%s",
ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker");
}
- perform_delayable_implementation_check(
- ce, child, parent, /*always_error*/0);
+ if (!checked) {
+ if (check_only) {
+ zend_string *unresolved_class;
+
+ return zend_do_perform_implementation_check(
+ &unresolved_class, child, parent);
+ }
+ perform_delayable_implementation_check(
+ ce, child, parent, /*always_error*/0);
+ }
+ return INHERITANCE_SUCCESS;
+}
+/* }}} */
+
+static void do_inheritance_check_on_method(zend_function *child, zend_function *parent, zend_class_entry *ce, zval *child_zv) /* {{{ */
+{
+ do_inheritance_check_on_method_ex(child, parent, ce, child_zv, 0, 0);
}
/* }}} */
}
/* }}} */
+static void do_inherit_checked_methods(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
+{
+ zend_string *key;
+ zend_function *parent;
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent) {
+ zval *child = zend_hash_find_ex(&ce->function_table, key, 1);
+
+ if (child) {
+ zend_function *func = (zend_function*)Z_PTR_P(child);
+ do_inheritance_check_on_method_ex(func, parent, ce, child, 0, 1);
+ } else {
+
+ if (parent->common.fn_flags & (ZEND_ACC_ABSTRACT)) {
+ ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
+ }
+
+ parent = zend_duplicate_function(parent, ce);
+ _zend_hash_append_ptr(&ce->function_table, key, parent);
+ }
+ } ZEND_HASH_FOREACH_END();
+}
+/* }}} */
+
zend_string* zend_resolve_property_type(zend_string *type, zend_class_entry *scope) /* {{{ */
{
if (zend_string_equals_literal_ci(type, "parent")) {
} ZEND_HASH_FOREACH_END();
}
-ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
+ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, zend_bool checked) /* {{{ */
{
zend_property_info *property_info;
zend_function *func;
zend_hash_num_elements(&ce->function_table) +
zend_hash_num_elements(&parent_ce->function_table), 0);
- ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) {
- zend_function *new_func = do_inherit_method(key, func, ce);
+ if (checked) {
+ do_inherit_checked_methods(ce, parent_ce);
+ } else {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) {
+ zend_function *new_func = do_inherit_method(key, func, ce);
- if (new_func) {
- _zend_hash_append_ptr(&ce->function_table, key, new_func);
- }
- } ZEND_HASH_FOREACH_END();
+ if (new_func) {
+ _zend_hash_append_ptr(&ce->function_table, key, new_func);
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
}
do_inherit_parent_constructor(ce);
}
}
}
+/* }}} */
/* Check whether early binding is prevented due to unresolved types in inheritance checks. */
-zend_bool zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) {
+static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
+{
+ inheritance_status ret = INHERITANCE_SUCCESS;
zend_string *key;
zend_function *parent_func;
- ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent_func) {
- uint32_t parent_flags = parent_func->common.fn_flags;
- zval *zv;
- zend_string *unresolved_class;
- if (parent_flags & ZEND_ACC_PRIVATE) {
- continue;
- } else if (parent_flags & ZEND_ACC_CTOR) {
- zend_function *proto = parent_func->common.prototype ?
- parent_func->common.prototype : parent_func;
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent_func) {
+ zval *zv = zend_hash_find_ex(&ce->function_table, key, 1);
+ if (zv) {
+ zend_function *child_func = Z_FUNC_P(zv);
+ inheritance_status status =
+ do_inheritance_check_on_method_ex(child_func, parent_func, ce, NULL, 1, 0);
- /* ctors only have a prototype if is abstract (or comes from an interface) */
- /* and if that is the case, we want to check inheritance against it */
- if (!(proto->common.fn_flags & ZEND_ACC_ABSTRACT)) {
- continue;
+ if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
+ if (EXPECTED(status == INHERITANCE_UNRESOLVED)) {
+ return INHERITANCE_UNRESOLVED;
+ }
+ ZEND_ASSERT(status == INHERITANCE_ERROR);
+ ret = INHERITANCE_ERROR;
}
- parent_func = proto;
}
+ } ZEND_HASH_FOREACH_END();
- zv = zend_hash_find_ex(&ce->function_table, key, 1);
- if (zv) {
- zend_function *child_func = Z_FUNC_P(zv);
- inheritance_status status;
+ return ret;
+}
+/* }}} */
- status = zend_do_perform_implementation_check(
- &unresolved_class, child_func, parent_func);
+zend_bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */
+{
+ inheritance_status status = zend_can_early_bind(ce, parent_ce);
- if (UNEXPECTED(status == INHERITANCE_UNRESOLVED)) {
+ if (EXPECTED(status != INHERITANCE_UNRESOLVED)) {
+ if (delayed_early_binding) {
+ if (UNEXPECTED(zend_hash_set_bucket_key(EG(class_table), (Bucket*)delayed_early_binding, lcname) == NULL)) {
+ zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
+ return 0;
+ }
+ } else {
+ if (UNEXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) == NULL)) {
return 0;
}
}
- } ZEND_HASH_FOREACH_END();
- return 1;
+ zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
+ zend_build_properties_info_table(ce);
+ if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
+ zend_verify_abstract_class(ce);
+ }
+ ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE));
+ ce->ce_flags |= ZEND_ACC_LINKED;
+ return 1;
+ }
+ return 0;
}
/* }}} */