Skip to content

Instantly share code, notes, and snippets.

@dstogov
Created June 26, 2019 07:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dstogov/0bcb35ed093f99d325f2f44e78a3add6 to your computer and use it in GitHub Desktop.
Save dstogov/0bcb35ed093f99d325f2f44e78a3add6 to your computer and use it in GitHub Desktop.
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 6721612b4c..de0ac1fe13 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -278,6 +278,9 @@ typedef struct _zend_oparray_context {
/* Class has unresolved variance obligations. | | | */
#define ZEND_ACC_UNRESOLVED_VARIANCE (1 << 21) /* X | | | */
/* | | | */
+/* Method compatibility already checked. | | | */
+#define ZEND_ACC_CHECKED (1 << 22) /* X | | | */
+/* | | | */
/* Function Flags (unused: 28...30) | | | */
/* ============== | | | */
/* | | | */
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index e568e53514..697417d31a 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -804,6 +804,65 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
}
/* }}} */
+static zend_function *do_inherit_checked_method(zend_string *key, zend_function *parent, zend_class_entry *ce) /* {{{ */
+{
+ zval *child = zend_hash_find_ex(&ce->function_table, key, 1);
+
+ if (child) {
+ zend_function *func = (zend_function*)Z_PTR_P(child);
+ uint32_t parent_flags;
+
+ if (UNEXPECTED(func == parent)) {
+ /* The same method in interface may be inherited few times */
+ return NULL;
+ }
+
+ parent_flags = parent->common.fn_flags;
+
+ if (parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED)) {
+ func->common.fn_flags |= ZEND_ACC_CHANGED;
+ }
+
+ if (!(parent_flags & ZEND_ACC_PRIVATE)) {
+ zend_function *proto = parent->common.prototype ?
+ parent->common.prototype : parent;
+
+ if (parent_flags & ZEND_ACC_CTOR) {
+ /* 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 NULL;
+ }
+ }
+
+ if (func->common.prototype != proto) {
+ if (func->common.scope != ce
+ && func->type == ZEND_USER_FUNCTION
+ && !func->op_array.static_variables) {
+ if (ce->ce_flags & ZEND_ACC_INTERFACE) {
+ /* Few parent interfaces contain the same method */
+ return NULL;
+ } else {
+ /* op_array wasn't duplicated yet */
+ zend_function *new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
+ memcpy(new_function, func, sizeof(zend_op_array));
+ Z_PTR_P(child) = func = new_function;
+ }
+ }
+ func->common.prototype = proto;
+ }
+ }
+ return NULL;
+ }
+
+ if (parent->common.fn_flags & (ZEND_ACC_ABSTRACT)) {
+ ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
+ }
+
+ return zend_duplicate_function(parent, ce);
+}
+/* }}} */
+
static zend_function *do_inherit_method(zend_string *key, zend_function *parent, zend_class_entry *ce) /* {{{ */
{
zval *child = zend_hash_find_ex(&ce->function_table, key, 1);
@@ -1258,13 +1317,23 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
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 (ce->ce_flags & ZEND_ACC_CHECKED) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) {
+ zend_function *new_func = do_inherit_checked_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();
+ } 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();
+ }
}
do_inherit_parent_constructor(ce);
@@ -2387,6 +2456,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na
/* 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) {
+ zend_bool has_errors = 0;
zend_string *key;
zend_function *parent_func;
ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent_func) {
@@ -2411,16 +2481,42 @@ zend_bool zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce)
zv = zend_hash_find_ex(&ce->function_table, key, 1);
if (zv) {
zend_function *child_func = Z_FUNC_P(zv);
+ uint32_t child_flags = child_func->common.fn_flags;
inheritance_status status;
+ if (UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) {
+ return 1;
+ }
+
+ if (UNEXPECTED((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC))) {
+ return 1;
+ }
+
+ if (UNEXPECTED((child_flags & ZEND_ACC_ABSTRACT) > (parent_flags & ZEND_ACC_ABSTRACT))) {
+ return 1;
+ }
+
+ if ((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) {
+ return 1;
+ }
+
status = zend_do_perform_implementation_check(
&unresolved_class, child_func, parent_func);
- if (UNEXPECTED(status == INHERITANCE_UNRESOLVED)) {
- return 0;
+ if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
+ if (EXPECTED(status == INHERITANCE_UNRESOLVED)) {
+ return 0;
+ }
+ ZEND_ASSERT(status == INHERITANCE_ERROR);
+ has_errors = 1;
}
}
} ZEND_HASH_FOREACH_END();
+
+ if (!has_errors) {
+ ce->ce_flags |= ZEND_ACC_CHECKED;
+ }
+
return 1;
}
/* }}} */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment