diff --git a/Zend/tests/internal_class_variance.phpt b/Zend/tests/internal_class_variance.phpt new file mode 100644 index 0000000000000..8c446c2a72c1f --- /dev/null +++ b/Zend/tests/internal_class_variance.phpt @@ -0,0 +1,16 @@ +--TEST-- +Internal class variance +--EXTENSIONS-- +zend_test +--FILE-- +returnsThrowable(); +} catch (\Error) { + echo "OK"; +} +?> +--EXPECT-- +OK diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 04ec5dc3c61ab..9f5aa511023df 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -200,22 +200,45 @@ static bool class_visible(zend_class_entry *ce) { } } +static zend_always_inline void register_unresolved_class(zend_string *name) { + /* We'll autoload this class and process delayed variance obligations later. */ + if (!CG(delayed_autoloads)) { + ALLOC_HASHTABLE(CG(delayed_autoloads)); + zend_hash_init(CG(delayed_autoloads), 0, NULL, NULL, 0); + } + zend_hash_add_empty_element(CG(delayed_autoloads), name); +} + static zend_class_entry *lookup_class( zend_class_entry *scope, zend_string *name, bool register_unresolved) { - uint32_t flags = ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD; - zend_class_entry *ce = zend_lookup_class_ex(name, NULL, flags); + zend_class_entry *ce; + + if (UNEXPECTED(!EG(active))) { + zend_string *lc_name = zend_string_tolower(name); + + ce = zend_hash_find_ptr(CG(class_table), lc_name); + + zend_string_release(lc_name); + + if (register_unresolved && !ce) { + zend_error_noreturn( + E_COMPILE_ERROR, "%s must be registered before %s", + ZSTR_VAL(name), ZSTR_VAL(scope->name)); + } + + return ce; + } + + ce = zend_lookup_class_ex( + name, NULL, ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD); + if (!CG(in_compilation)) { if (ce) { return ce; } if (register_unresolved) { - /* We'll autoload this class and process delayed variance obligations later. */ - if (!CG(delayed_autoloads)) { - ALLOC_HASHTABLE(CG(delayed_autoloads)); - zend_hash_init(CG(delayed_autoloads), 0, NULL, NULL, 0); - } - zend_hash_add_empty_element(CG(delayed_autoloads), name); + register_unresolved_class(name); } } else { if (ce && class_visible(ce)) { diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index b6f6737f086ed..54e70a9ab25b3 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -257,22 +257,25 @@ static zend_object *zend_test_class_new(zend_class_entry *class_type) /* {{{ */ /* }}} */ static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key) /* {{{ */ { - zend_internal_function *fptr; - - if (EXPECTED(EG(trampoline).common.function_name == NULL)) { - fptr = (zend_internal_function *) &EG(trampoline); - } else { - fptr = emalloc(sizeof(zend_internal_function)); - } - memset(fptr, 0, sizeof(zend_internal_function)); - fptr->type = ZEND_INTERNAL_FUNCTION; - fptr->num_args = 1; - fptr->scope = (*object)->ce; - fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; - fptr->function_name = zend_string_copy(name); - fptr->handler = ZEND_FN(zend_test_func); - - return (zend_function*)fptr; + if (zend_string_equals_literal_ci(name, "test")) { + zend_internal_function *fptr; + + if (EXPECTED(EG(trampoline).common.function_name == NULL)) { + fptr = (zend_internal_function *) &EG(trampoline); + } else { + fptr = emalloc(sizeof(zend_internal_function)); + } + memset(fptr, 0, sizeof(zend_internal_function)); + fptr->type = ZEND_INTERNAL_FUNCTION; + fptr->num_args = 1; + fptr->scope = (*object)->ce; + fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; + fptr->function_name = zend_string_copy(name); + fptr->handler = ZEND_FN(zend_test_func); + + return (zend_function*)fptr; + } + return zend_std_get_method(object, name, key); } /* }}} */ @@ -322,6 +325,16 @@ static ZEND_METHOD(_ZendTestClass, returnsStatic) { object_init_ex(return_value, zend_get_called_scope(execute_data)); } +static ZEND_METHOD(_ZendTestClass, returnsThrowable) { + ZEND_PARSE_PARAMETERS_NONE(); + zend_throw_error(NULL, "Dummy"); +} + +static ZEND_METHOD(_ZendTestChildClass, returnsThrowable) { + ZEND_PARSE_PARAMETERS_NONE(); + zend_throw_error(NULL, "Dummy"); +} + static ZEND_METHOD(_ZendTestTrait, testMethod) { ZEND_PARSE_PARAMETERS_NONE(); RETURN_TRUE; diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 624ba1195f3cd..3571556d238ca 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -24,10 +24,13 @@ public static function is_object(): int {} public function __toString(): string {} public function returnsStatic(): static {} + + public function returnsThrowable(): Throwable {} } class _ZendTestChildClass extends _ZendTestClass { + public function returnsThrowable(): Exception {} } trait _ZendTestTrait { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index dc040a568043f..d4d77e125dd8d 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: cf8958513064fb7257203b3304c8dc67c8e008b9 */ + * Stub hash: 70374ed7b55604eb98e85148d7ff19e79258ce92 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -63,6 +63,12 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_returnsStatic, 0, 0, IS_STATIC, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class__ZendTestClass_returnsThrowable, 0, 0, Throwable, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class__ZendTestChildClass_returnsThrowable, 0, 0, Exception, 0) +ZEND_END_ARG_INFO() + #define arginfo_class__ZendTestTrait_testMethod arginfo_ZendTestNS2_ZendSubNS_namespaced_func #define arginfo_class_ZendTestNS_Foo_method arginfo_zend_test_void_return @@ -89,6 +95,8 @@ static ZEND_FUNCTION(namespaced_func); static ZEND_METHOD(_ZendTestClass, is_object); static ZEND_METHOD(_ZendTestClass, __toString); static ZEND_METHOD(_ZendTestClass, returnsStatic); +static ZEND_METHOD(_ZendTestClass, returnsThrowable); +static ZEND_METHOD(_ZendTestChildClass, returnsThrowable); static ZEND_METHOD(_ZendTestTrait, testMethod); static ZEND_METHOD(ZendTestNS_Foo, method); static ZEND_METHOD(ZendTestNS2_Foo, method); @@ -123,11 +131,13 @@ static const zend_function_entry class__ZendTestClass_methods[] = { ZEND_ME(_ZendTestClass, is_object, arginfo_class__ZendTestClass_is_object, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(_ZendTestClass, __toString, arginfo_class__ZendTestClass___toString, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) ZEND_ME(_ZendTestClass, returnsStatic, arginfo_class__ZendTestClass_returnsStatic, ZEND_ACC_PUBLIC) + ZEND_ME(_ZendTestClass, returnsThrowable, arginfo_class__ZendTestClass_returnsThrowable, ZEND_ACC_PUBLIC) ZEND_FE_END }; static const zend_function_entry class__ZendTestChildClass_methods[] = { + ZEND_ME(_ZendTestChildClass, returnsThrowable, arginfo_class__ZendTestChildClass_returnsThrowable, ZEND_ACC_PUBLIC) ZEND_FE_END };