diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index d12199a6d62..a80838666b4 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -212,7 +212,7 @@ typedef struct _zend_oparray_context { /* Final class or method | | | */ #define ZEND_ACC_FINAL (1 << 5) /* X | X | | */ /* | | | */ -/* Abstarct method | | | */ +/* Abstract method | | | */ #define ZEND_ACC_ABSTRACT (1 << 6) /* X | X | | */ #define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS (1 << 6) /* X | | | */ /* | | | */ @@ -260,6 +260,9 @@ typedef struct _zend_oparray_context { /* User class has methods with static variables | | | */ #define ZEND_HAS_STATIC_IN_METHODS (1 << 15) /* X | | | */ /* | | | */ +/* Whether all property types are resolved to CEs | | | */ +#define ZEND_ACC_PROPERTY_TYPES_RESOLVED (1 << 16) /* X | | | */ +/* | | | */ /* Function Flags (unused: 28...30) | | | */ /* ============== | | | */ /* | | | */ diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index ad9e3aa5a9d..303034a4ba9 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3408,6 +3408,42 @@ static void preload_link(void) } while (changed); EG(exception) = NULL; + /* Resolve property types */ + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + if ((ce->ce_flags & ZEND_ACC_LINKED) + && !(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + zend_bool ok = 1; + zend_property_info *prop; + if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) { + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + zend_string *name, *lcname; + if (!ZEND_TYPE_IS_NAME(prop->type)) { + continue; + } + + name = ZEND_TYPE_NAME(prop->type); + lcname = zend_string_tolower(name); + p = zend_hash_find_ptr(EG(class_table), lcname); + zend_string_release(lcname); + if (!p) { + ok = 0; + continue; + } + + zend_string_release(name); + prop->type = ZEND_TYPE_ENCODE_CE(p, ZEND_TYPE_ALLOW_NULL(prop->type)); + } ZEND_HASH_FOREACH_END(); + } + if (ok) { + ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED; + } + } + } ZEND_HASH_FOREACH_END(); + /* Move unlinked clases (and with unresilved constants) back to scripts */ orig_dtor = EG(class_table)->pDestructor; EG(class_table)->pDestructor = NULL; @@ -3420,6 +3456,8 @@ static void preload_link(void) zend_error(E_WARNING, "Can't preload unlinked class %s at %s:%d\n", ZSTR_VAL(ce->name), ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start); } else if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { zend_error(E_WARNING, "Can't preload class %s with unresolved constants at %s:%d\n", ZSTR_VAL(ce->name), ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start); + } else if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + zend_error(E_WARNING, "Can't preload class %s with unresolved property types at %s:%d\n", ZSTR_VAL(ce->name), ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start); } else { continue; } diff --git a/ext/opcache/tests/preload.inc b/ext/opcache/tests/preload.inc index 0e09ad366c3..6a4df58c5f7 100644 --- a/ext/opcache/tests/preload.inc +++ b/ext/opcache/tests/preload.inc @@ -40,4 +40,5 @@ class Y { class Z { public $foo; + public a $bar; } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index d22b855f588..1a22f12b4db 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -745,10 +745,6 @@ static void zend_persist_property_info(zval *zv) zend_string *class_name = ZEND_TYPE_NAME(prop->type); zend_accel_store_interned_string(class_name); prop->type = ZEND_TYPE_ENCODE_CLASS(class_name, ZEND_TYPE_ALLOW_NULL(prop->type)); - } else if (ZEND_TYPE_IS_CE(prop->type)) { - zend_class_entry *ce = ZEND_TYPE_CE(prop->type); - ce = zend_shared_alloc_get_xlat_entry(ce); - prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type)); } } @@ -790,16 +786,6 @@ static void zend_persist_class_constant(zval *zv) } } -static zend_bool has_unresolved_property_types(zend_class_entry *ce) { - zend_property_info *prop; - ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { - if (ZEND_TYPE_IS_NAME(prop->type)) { - return 1; - } - } ZEND_HASH_FOREACH_END(); - return 0; -} - static void zend_persist_class_entry(zval *zv) { zend_class_entry *ce = Z_PTR_P(zv); @@ -807,7 +793,7 @@ static void zend_persist_class_entry(zval *zv) if (ce->type == ZEND_USER_CLASS) { if ((ce->ce_flags & ZEND_ACC_LINKED) && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) - && !has_unresolved_property_types(ce) + && (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) && !ZCG(current_persistent_script)->corrupted) { ZCG(is_immutable_class) = 1; ce = Z_PTR_P(zv) = zend_shared_memdup_put(ce, sizeof(zend_class_entry)); @@ -953,14 +939,6 @@ static void zend_persist_class_entry(zval *zv) } } -//static int zend_update_property_info_ce(zval *zv) -//{ -// zend_property_info *prop = Z_PTR_P(zv); -// -// prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce); -// return 0; -//} - static void zend_update_parent_ce(zend_class_entry *ce) { if (ce->ce_flags & ZEND_ACC_LINKED) { @@ -1018,6 +996,20 @@ static void zend_update_parent_ce(zend_class_entry *ce) } } + if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) { + zend_property_info *prop; + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + if (ZEND_TYPE_IS_CE(prop->type)) { + zend_class_entry *ce = ZEND_TYPE_CE(prop->type); + if (ce->type == ZEND_USER_CLASS) { + ce = zend_shared_alloc_get_xlat_entry(ce); + ZEND_ASSERT(ce); + prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type)); + } + } + } ZEND_HASH_FOREACH_END(); + } + /* update methods */ if (ce->constructor) { zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->constructor); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 7daac7b51cf..774f42185f1 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -331,14 +331,21 @@ static void zend_persist_class_constant_calc(zval *zv) } } -static zend_bool has_unresolved_property_types(zend_class_entry *ce) { +static void check_property_type_resolution(zend_class_entry *ce) { zend_property_info *prop; - ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { - if (ZEND_TYPE_IS_NAME(prop->type)) { - return 1; - } - } ZEND_HASH_FOREACH_END(); - return 0; + if (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) { + /* Preloading might have computed this already. */ + return; + } + + if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) { + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + if (ZEND_TYPE_IS_NAME(prop->type)) { + return; + } + } ZEND_HASH_FOREACH_END(); + } + ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED; } static void zend_persist_class_entry_calc(zval *zv) @@ -346,10 +353,12 @@ static void zend_persist_class_entry_calc(zval *zv) zend_class_entry *ce = Z_PTR_P(zv); if (ce->type == ZEND_USER_CLASS) { + check_property_type_resolution(ce); + ZCG(is_immutable_class) = (ce->ce_flags & ZEND_ACC_LINKED) && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) && - !has_unresolved_property_types(ce) && + (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) && !ZCG(current_persistent_script)->corrupted; ADD_SIZE_EX(sizeof(zend_class_entry));