From: Nikita Popov Date: Tue, 10 Dec 2019 12:07:04 +0000 (+0100) Subject: Merge branch 'PHP-7.4' X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5cdea8d5e87570fada17c3f7ed8c5886f81f0cc7;p=php Merge branch 'PHP-7.4' * PHP-7.4: Add support for class_alias to preloading Fixed bug #78935: Check that all linked classes can be preloaded --- 5cdea8d5e87570fada17c3f7ed8c5886f81f0cc7 diff --cc ext/opcache/ZendAccelerator.c index c203a06a74,ccd300390f..1b8080800a --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@@ -3935,6 -3850,108 +3935,117 @@@ static void preload_link(void } ZEND_HASH_FOREACH_END(); } + #ifdef ZEND_WIN32 + static void preload_check_windows_restriction(zend_class_entry *scope, zend_class_entry *ce) { + if (ce && ce->type == ZEND_INTERNAL_CLASS) { + zend_error_noreturn(E_ERROR, + "Class %s uses internal class %s during preloading, which is not supported on Windows", + ZSTR_VAL(scope->name), ZSTR_VAL(ce->name)); + } + } + + static void preload_check_windows_restrictions(zend_class_entry *scope) { + uint32_t i; + + preload_check_windows_restriction(scope, scope->parent); + + for (i = 0; i < scope->num_interfaces; i++) { + preload_check_windows_restriction(scope, scope->interfaces[i]); + } + } + #endif + + static zend_class_entry *preload_load_prop_type(zend_property_info *prop, zend_string *name) { + zend_class_entry *ce; + if (zend_string_equals_literal_ci(name, "self")) { + ce = prop->ce; + } else if (zend_string_equals_literal_ci(name, "parent")) { + ce = prop->ce->parent; + } else { + ce = zend_lookup_class(name); + } + if (ce) { + return ce; + } + + zend_error_noreturn(E_ERROR, + "Failed to load class %s used by typed property %s::$%s during preloading", + ZSTR_VAL(name), ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name)); + return ce; + } + + static void preload_ensure_classes_loadable() { + /* Run this in a loop, because additional classes may be loaded while updating constants etc. */ + uint32_t checked_classes_idx = 0; + while (1) { + zend_class_entry *ce; + uint32_t num_classes = zend_hash_num_elements(EG(class_table)); + if (num_classes == checked_classes_idx) { + return; + } + + ZEND_HASH_REVERSE_FOREACH_PTR(EG(class_table), ce) { + if (ce->type == ZEND_INTERNAL_CLASS || _idx == checked_classes_idx) { + break; + } + + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + /* Only require that already linked classes are loadable, we'll properly check + * things when linking additional classes. */ + continue; + } + + #ifdef ZEND_WIN32 + preload_check_windows_restrictions(ce); + #endif + + if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + int result = SUCCESS; + zend_try { + result = zend_update_class_constants(ce); + } zend_catch { + /* Provide some context for the generated error. */ + zend_error_noreturn(E_ERROR, + "Error generated while resolving initializers of class %s during preloading", + ZSTR_VAL(ce->name)); + } zend_end_try(); + if (result == FAILURE) { + /* Just present to be safe: We generally always throw some + * other fatal error as part of update_class_constants(). */ + zend_error_noreturn(E_ERROR, + "Failed to resolve initializers of class %s during preloading", + ZSTR_VAL(ce->name)); + } + ZEND_ASSERT(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED); + } + + if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + 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_NAME(prop->type)) { ++ if (ZEND_TYPE_HAS_LIST(prop->type)) { ++ void **entry; ++ ZEND_TYPE_LIST_FOREACH_PTR(ZEND_TYPE_LIST(prop->type), entry) { ++ if (ZEND_TYPE_LIST_IS_NAME(*entry)) { ++ zend_class_entry *ce = preload_load_prop_type( ++ prop, ZEND_TYPE_LIST_GET_NAME(*entry)); ++ *entry = ZEND_TYPE_LIST_ENCODE_CE(ce); ++ } ++ } ZEND_TYPE_LIST_FOREACH_END(); ++ } else if (ZEND_TYPE_HAS_NAME(prop->type)) { + zend_class_entry *ce = + preload_load_prop_type(prop, ZEND_TYPE_NAME(prop->type)); - prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type)); ++ ZEND_TYPE_SET_CE(prop->type, ce); + } + } ZEND_HASH_FOREACH_END(); + } + ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED; + } + } ZEND_HASH_FOREACH_END(); + checked_classes_idx = num_classes; + } + } + static zend_string *preload_resolve_path(zend_string *filename) { if (is_stream_path(ZSTR_VAL(filename))) { diff --cc ext/opcache/tests/preload_009.phpt index 84f444768c,1adbf5b006..d0c762c801 --- a/ext/opcache/tests/preload_009.phpt +++ b/ext/opcache/tests/preload_009.phpt @@@ -13,6 -13,6 +13,6 @@@ var_dump(trait_exists('T')) var_dump(class_exists('Foo')); ?> --EXPECTF-- - Warning: Can't preload class Foo with unresolved initializer for constant C in %spreload_undef_const_2.inc on line 8 -Warning: Use of undefined constant UNDEF - assumed 'UNDEF' (this will throw an Error in a future version of PHP) in Unknown on line 0 --bool(true) - bool(false) -bool(true) ++Fatal error: Undefined constant 'UNDEF' in Unknown on line 0 ++ ++Fatal error: Error generated while resolving initializers of class Foo during preloading in Unknown on line 0 diff --cc ext/opcache/tests/preload_loadable_classes_2.phpt index 0000000000,4a5d2b8a66..17100ff64f mode 000000,100644..100644 --- a/ext/opcache/tests/preload_loadable_classes_2.phpt +++ b/ext/opcache/tests/preload_loadable_classes_2.phpt @@@ -1,0 -1,17 +1,15 @@@ + --TEST-- + Preloading: Loadable class checking (2) + --INI-- + opcache.enable=1 + opcache.enable_cli=1 + opcache.optimization_level=-1 + opcache.preload={PWD}/preload_loadable_classes_2.inc + --SKIPIF-- + + --FILE-- + Unreachable + --EXPECTF-- -Warning: Use of undefined constant UNDEF - assumed 'UNDEF' (this will throw an Error in a future version of PHP) in Unknown on line 0 - -Fatal error: Class 'Foo' not found in Unknown on line 0 ++Fatal error: Undefined constant 'UNDEF' in Unknown on line 0 + + Fatal error: Error generated while resolving initializers of class Test during preloading in Unknown on line 0