Skip to content

Commit fcd1875

Browse files
kocsismatekrakjoe
andauthored
Add reproducer for possible issue with object return type inheritance (#6961)
Fix early inheritance Co-authored-by: Joe Watkins <krakjoe@php.net>
1 parent f71bfe4 commit fcd1875

File tree

5 files changed

+90
-25
lines changed

5 files changed

+90
-25
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
Internal class variance
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
$test = new _ZendTestChildClass;
8+
9+
try {
10+
$test->returnsThrowable();
11+
} catch (\Error) {
12+
echo "OK";
13+
}
14+
?>
15+
--EXPECT--
16+
OK

Zend/zend_inheritance.c

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,22 +200,45 @@ static bool class_visible(zend_class_entry *ce) {
200200
}
201201
}
202202

203+
static zend_always_inline void register_unresolved_class(zend_string *name) {
204+
/* We'll autoload this class and process delayed variance obligations later. */
205+
if (!CG(delayed_autoloads)) {
206+
ALLOC_HASHTABLE(CG(delayed_autoloads));
207+
zend_hash_init(CG(delayed_autoloads), 0, NULL, NULL, 0);
208+
}
209+
zend_hash_add_empty_element(CG(delayed_autoloads), name);
210+
}
211+
203212
static zend_class_entry *lookup_class(
204213
zend_class_entry *scope, zend_string *name, bool register_unresolved) {
205-
uint32_t flags = ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD;
206-
zend_class_entry *ce = zend_lookup_class_ex(name, NULL, flags);
214+
zend_class_entry *ce;
215+
216+
if (UNEXPECTED(!EG(active))) {
217+
zend_string *lc_name = zend_string_tolower(name);
218+
219+
ce = zend_hash_find_ptr(CG(class_table), lc_name);
220+
221+
zend_string_release(lc_name);
222+
223+
if (register_unresolved && !ce) {
224+
zend_error_noreturn(
225+
E_COMPILE_ERROR, "%s must be registered before %s",
226+
ZSTR_VAL(name), ZSTR_VAL(scope->name));
227+
}
228+
229+
return ce;
230+
}
231+
232+
ce = zend_lookup_class_ex(
233+
name, NULL, ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD);
234+
207235
if (!CG(in_compilation)) {
208236
if (ce) {
209237
return ce;
210238
}
211239

212240
if (register_unresolved) {
213-
/* We'll autoload this class and process delayed variance obligations later. */
214-
if (!CG(delayed_autoloads)) {
215-
ALLOC_HASHTABLE(CG(delayed_autoloads));
216-
zend_hash_init(CG(delayed_autoloads), 0, NULL, NULL, 0);
217-
}
218-
zend_hash_add_empty_element(CG(delayed_autoloads), name);
241+
register_unresolved_class(name);
219242
}
220243
} else {
221244
if (ce && class_visible(ce)) {

ext/zend_test/test.c

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -257,22 +257,25 @@ static zend_object *zend_test_class_new(zend_class_entry *class_type) /* {{{ */
257257
/* }}} */
258258

259259
static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key) /* {{{ */ {
260-
zend_internal_function *fptr;
261-
262-
if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
263-
fptr = (zend_internal_function *) &EG(trampoline);
264-
} else {
265-
fptr = emalloc(sizeof(zend_internal_function));
266-
}
267-
memset(fptr, 0, sizeof(zend_internal_function));
268-
fptr->type = ZEND_INTERNAL_FUNCTION;
269-
fptr->num_args = 1;
270-
fptr->scope = (*object)->ce;
271-
fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
272-
fptr->function_name = zend_string_copy(name);
273-
fptr->handler = ZEND_FN(zend_test_func);
274-
275-
return (zend_function*)fptr;
260+
if (zend_string_equals_literal_ci(name, "test")) {
261+
zend_internal_function *fptr;
262+
263+
if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
264+
fptr = (zend_internal_function *) &EG(trampoline);
265+
} else {
266+
fptr = emalloc(sizeof(zend_internal_function));
267+
}
268+
memset(fptr, 0, sizeof(zend_internal_function));
269+
fptr->type = ZEND_INTERNAL_FUNCTION;
270+
fptr->num_args = 1;
271+
fptr->scope = (*object)->ce;
272+
fptr->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
273+
fptr->function_name = zend_string_copy(name);
274+
fptr->handler = ZEND_FN(zend_test_func);
275+
276+
return (zend_function*)fptr;
277+
}
278+
return zend_std_get_method(object, name, key);
276279
}
277280
/* }}} */
278281

@@ -322,6 +325,16 @@ static ZEND_METHOD(_ZendTestClass, returnsStatic) {
322325
object_init_ex(return_value, zend_get_called_scope(execute_data));
323326
}
324327

328+
static ZEND_METHOD(_ZendTestClass, returnsThrowable) {
329+
ZEND_PARSE_PARAMETERS_NONE();
330+
zend_throw_error(NULL, "Dummy");
331+
}
332+
333+
static ZEND_METHOD(_ZendTestChildClass, returnsThrowable) {
334+
ZEND_PARSE_PARAMETERS_NONE();
335+
zend_throw_error(NULL, "Dummy");
336+
}
337+
325338
static ZEND_METHOD(_ZendTestTrait, testMethod) {
326339
ZEND_PARSE_PARAMETERS_NONE();
327340
RETURN_TRUE;

ext/zend_test/test.stub.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@ public static function is_object(): int {}
2424
public function __toString(): string {}
2525

2626
public function returnsStatic(): static {}
27+
28+
public function returnsThrowable(): Throwable {}
2729
}
2830

2931
class _ZendTestChildClass extends _ZendTestClass
3032
{
33+
public function returnsThrowable(): Exception {}
3134
}
3235

3336
trait _ZendTestTrait {

ext/zend_test/test_arginfo.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: cf8958513064fb7257203b3304c8dc67c8e008b9 */
2+
* Stub hash: 70374ed7b55604eb98e85148d7ff19e79258ce92 */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
55
ZEND_END_ARG_INFO()
@@ -63,6 +63,12 @@ ZEND_END_ARG_INFO()
6363
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_returnsStatic, 0, 0, IS_STATIC, 0)
6464
ZEND_END_ARG_INFO()
6565

66+
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class__ZendTestClass_returnsThrowable, 0, 0, Throwable, 0)
67+
ZEND_END_ARG_INFO()
68+
69+
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class__ZendTestChildClass_returnsThrowable, 0, 0, Exception, 0)
70+
ZEND_END_ARG_INFO()
71+
6672
#define arginfo_class__ZendTestTrait_testMethod arginfo_ZendTestNS2_ZendSubNS_namespaced_func
6773

6874
#define arginfo_class_ZendTestNS_Foo_method arginfo_zend_test_void_return
@@ -89,6 +95,8 @@ static ZEND_FUNCTION(namespaced_func);
8995
static ZEND_METHOD(_ZendTestClass, is_object);
9096
static ZEND_METHOD(_ZendTestClass, __toString);
9197
static ZEND_METHOD(_ZendTestClass, returnsStatic);
98+
static ZEND_METHOD(_ZendTestClass, returnsThrowable);
99+
static ZEND_METHOD(_ZendTestChildClass, returnsThrowable);
92100
static ZEND_METHOD(_ZendTestTrait, testMethod);
93101
static ZEND_METHOD(ZendTestNS_Foo, method);
94102
static ZEND_METHOD(ZendTestNS2_Foo, method);
@@ -123,11 +131,13 @@ static const zend_function_entry class__ZendTestClass_methods[] = {
123131
ZEND_ME(_ZendTestClass, is_object, arginfo_class__ZendTestClass_is_object, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
124132
ZEND_ME(_ZendTestClass, __toString, arginfo_class__ZendTestClass___toString, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
125133
ZEND_ME(_ZendTestClass, returnsStatic, arginfo_class__ZendTestClass_returnsStatic, ZEND_ACC_PUBLIC)
134+
ZEND_ME(_ZendTestClass, returnsThrowable, arginfo_class__ZendTestClass_returnsThrowable, ZEND_ACC_PUBLIC)
126135
ZEND_FE_END
127136
};
128137

129138

130139
static const zend_function_entry class__ZendTestChildClass_methods[] = {
140+
ZEND_ME(_ZendTestChildClass, returnsThrowable, arginfo_class__ZendTestChildClass_returnsThrowable, ZEND_ACC_PUBLIC)
131141
ZEND_FE_END
132142
};
133143

0 commit comments

Comments
 (0)