Skip to content

Commit 436cffc

Browse files
committed
Merge branch 'PHP-8.1'
2 parents 7a45dcf + abd56ae commit 436cffc

File tree

12 files changed

+325
-99
lines changed

12 files changed

+325
-99
lines changed

Zend/zend_inheritance.c

Lines changed: 113 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2764,101 +2764,113 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
27642764
#endif
27652765

27662766
bool orig_record_errors = EG(record_errors);
2767-
if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
2768-
if (is_cacheable) {
2769-
if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
2770-
zend_class_entry *ret = zend_inheritance_cache_get(ce, parent, traits_and_interfaces);
2771-
if (ret) {
2772-
if (traits_and_interfaces) {
2773-
free_alloca(traits_and_interfaces, use_heap);
2774-
}
2775-
zv = zend_hash_find_known_hash(CG(class_table), key);
2776-
Z_CE_P(zv) = ret;
2777-
return ret;
2778-
}
27792767

2780-
/* Make sure warnings (such as deprecations) thrown during inheritance
2781-
* will be recoreded in the inheritance cache. */
2782-
zend_begin_record_errors();
2783-
} else {
2784-
is_cacheable = 0;
2768+
if (ce->ce_flags & ZEND_ACC_IMMUTABLE && is_cacheable) {
2769+
if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
2770+
zend_class_entry *ret = zend_inheritance_cache_get(ce, parent, traits_and_interfaces);
2771+
if (ret) {
2772+
if (traits_and_interfaces) {
2773+
free_alloca(traits_and_interfaces, use_heap);
2774+
}
2775+
zv = zend_hash_find_known_hash(CG(class_table), key);
2776+
Z_CE_P(zv) = ret;
2777+
return ret;
27852778
}
2786-
proto = ce;
2779+
2780+
/* Make sure warnings (such as deprecations) thrown during inheritance
2781+
* will be recorded in the inheritance cache. */
2782+
zend_begin_record_errors();
2783+
} else {
2784+
is_cacheable = 0;
27872785
}
2788-
/* Lazy class loading */
2789-
ce = zend_lazy_class_load(ce);
2790-
zv = zend_hash_find_known_hash(CG(class_table), key);
2791-
Z_CE_P(zv) = ce;
2792-
} else if (ce->ce_flags & ZEND_ACC_FILE_CACHED) {
2793-
/* Lazy class loading */
2794-
ce = zend_lazy_class_load(ce);
2795-
ce->ce_flags &= ~ZEND_ACC_FILE_CACHED;
2796-
zv = zend_hash_find_known_hash(CG(class_table), key);
2797-
Z_CE_P(zv) = ce;
2786+
proto = ce;
27982787
}
27992788

2800-
if (CG(unlinked_uses)) {
2801-
zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t) ce);
2802-
}
2789+
zend_try {
2790+
if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
2791+
/* Lazy class loading */
2792+
ce = zend_lazy_class_load(ce);
2793+
zv = zend_hash_find_known_hash(CG(class_table), key);
2794+
Z_CE_P(zv) = ce;
2795+
} else if (ce->ce_flags & ZEND_ACC_FILE_CACHED) {
2796+
/* Lazy class loading */
2797+
ce = zend_lazy_class_load(ce);
2798+
ce->ce_flags &= ~ZEND_ACC_FILE_CACHED;
2799+
zv = zend_hash_find_known_hash(CG(class_table), key);
2800+
Z_CE_P(zv) = ce;
2801+
}
28032802

2804-
orig_linking_class = CG(current_linking_class);
2805-
CG(current_linking_class) = is_cacheable ? ce : NULL;
2803+
if (CG(unlinked_uses)) {
2804+
zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t) ce);
2805+
}
28062806

2807-
if (ce->ce_flags & ZEND_ACC_ENUM) {
2808-
/* Only register builtin enum methods during inheritance to avoid persisting them in
2809-
* opcache. */
2810-
zend_enum_register_funcs(ce);
2811-
}
2807+
orig_linking_class = CG(current_linking_class);
2808+
CG(current_linking_class) = is_cacheable ? ce : NULL;
28122809

2813-
if (parent) {
2814-
if (!(parent->ce_flags & ZEND_ACC_LINKED)) {
2815-
add_dependency_obligation(ce, parent);
2810+
if (ce->ce_flags & ZEND_ACC_ENUM) {
2811+
/* Only register builtin enum methods during inheritance to avoid persisting them in
2812+
* opcache. */
2813+
zend_enum_register_funcs(ce);
28162814
}
2817-
zend_do_inheritance(ce, parent);
2818-
}
2819-
if (ce->num_traits) {
2820-
zend_do_bind_traits(ce, traits_and_interfaces);
2821-
}
2822-
if (ce->num_interfaces) {
2823-
/* Also copy the parent interfaces here, so we don't need to reallocate later. */
2824-
uint32_t num_parent_interfaces = parent ? parent->num_interfaces : 0;
2825-
zend_class_entry **interfaces = emalloc(
2826-
sizeof(zend_class_entry *) * (ce->num_interfaces + num_parent_interfaces));
28272815

2828-
if (num_parent_interfaces) {
2829-
memcpy(interfaces, parent->interfaces,
2830-
sizeof(zend_class_entry *) * num_parent_interfaces);
2816+
if (parent) {
2817+
if (!(parent->ce_flags & ZEND_ACC_LINKED)) {
2818+
add_dependency_obligation(ce, parent);
2819+
}
2820+
zend_do_inheritance(ce, parent);
2821+
}
2822+
if (ce->num_traits) {
2823+
zend_do_bind_traits(ce, traits_and_interfaces);
28312824
}
2832-
memcpy(interfaces + num_parent_interfaces, traits_and_interfaces + ce->num_traits,
2833-
sizeof(zend_class_entry *) * ce->num_interfaces);
2825+
if (ce->num_interfaces) {
2826+
/* Also copy the parent interfaces here, so we don't need to reallocate later. */
2827+
uint32_t num_parent_interfaces = parent ? parent->num_interfaces : 0;
2828+
zend_class_entry **interfaces = emalloc(
2829+
sizeof(zend_class_entry *) * (ce->num_interfaces + num_parent_interfaces));
28342830

2835-
zend_do_implement_interfaces(ce, interfaces);
2836-
} else if (parent && parent->num_interfaces) {
2837-
zend_do_inherit_interfaces(ce, parent);
2838-
}
2839-
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT))
2840-
&& (ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))
2841-
) {
2842-
zend_verify_abstract_class(ce);
2843-
}
2844-
if (ce->ce_flags & ZEND_ACC_ENUM) {
2845-
zend_verify_enum(ce);
2846-
}
2831+
if (num_parent_interfaces) {
2832+
memcpy(interfaces, parent->interfaces,
2833+
sizeof(zend_class_entry *) * num_parent_interfaces);
2834+
}
2835+
memcpy(interfaces + num_parent_interfaces, traits_and_interfaces + ce->num_traits,
2836+
sizeof(zend_class_entry *) * ce->num_interfaces);
28472837

2848-
/* Normally Stringable is added during compilation. However, if it is imported from a trait,
2849-
* we need to explicilty add the interface here. */
2850-
if (ce->__tostring && !(ce->ce_flags & ZEND_ACC_TRAIT)
2838+
zend_do_implement_interfaces(ce, interfaces);
2839+
} else if (parent && parent->num_interfaces) {
2840+
zend_do_inherit_interfaces(ce, parent);
2841+
}
2842+
if (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT))
2843+
&& (ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))
2844+
) {
2845+
zend_verify_abstract_class(ce);
2846+
}
2847+
if (ce->ce_flags & ZEND_ACC_ENUM) {
2848+
zend_verify_enum(ce);
2849+
}
2850+
2851+
/* Normally Stringable is added during compilation. However, if it is imported from a trait,
2852+
* we need to explicilty add the interface here. */
2853+
if (ce->__tostring && !(ce->ce_flags & ZEND_ACC_TRAIT)
28512854
&& !zend_class_implements_interface(ce, zend_ce_stringable)) {
2852-
ZEND_ASSERT(ce->__tostring->common.fn_flags & ZEND_ACC_TRAIT_CLONE);
2853-
ce->ce_flags |= ZEND_ACC_RESOLVED_INTERFACES;
2854-
ce->num_interfaces++;
2855-
ce->interfaces = perealloc(ce->interfaces,
2856-
sizeof(zend_class_entry *) * ce->num_interfaces, ce->type == ZEND_INTERNAL_CLASS);
2857-
ce->interfaces[ce->num_interfaces - 1] = zend_ce_stringable;
2858-
do_interface_implementation(ce, zend_ce_stringable);
2859-
}
2855+
ZEND_ASSERT(ce->__tostring->common.fn_flags & ZEND_ACC_TRAIT_CLONE);
2856+
ce->ce_flags |= ZEND_ACC_RESOLVED_INTERFACES;
2857+
ce->num_interfaces++;
2858+
ce->interfaces = perealloc(ce->interfaces,
2859+
sizeof(zend_class_entry *) * ce->num_interfaces, ce->type == ZEND_INTERNAL_CLASS);
2860+
ce->interfaces[ce->num_interfaces - 1] = zend_ce_stringable;
2861+
do_interface_implementation(ce, zend_ce_stringable);
2862+
}
2863+
2864+
zend_build_properties_info_table(ce);
2865+
} zend_catch {
2866+
/* Do not leak recorded errors to the next linked class. */
2867+
if (!orig_record_errors) {
2868+
EG(record_errors) = false;
2869+
zend_free_recorded_errors();
2870+
}
2871+
zend_bailout();
2872+
} zend_end_try();
28602873

2861-
zend_build_properties_info_table(ce);
28622874
EG(record_errors) = orig_record_errors;
28632875

28642876
if (!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE)) {
@@ -3027,22 +3039,29 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
30273039
orig_linking_class = CG(current_linking_class);
30283040
CG(current_linking_class) = is_cacheable ? ce : NULL;
30293041

3030-
if (is_cacheable) {
3031-
zend_begin_record_errors();
3032-
}
3042+
zend_try{
3043+
if (is_cacheable) {
3044+
zend_begin_record_errors();
3045+
}
30333046

3034-
zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
3035-
if (parent_ce && parent_ce->num_interfaces) {
3036-
zend_do_inherit_interfaces(ce, parent_ce);
3037-
}
3038-
zend_build_properties_info_table(ce);
3039-
if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
3040-
zend_verify_abstract_class(ce);
3041-
}
3042-
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE));
3043-
ce->ce_flags |= ZEND_ACC_LINKED;
3047+
zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
3048+
if (parent_ce && parent_ce->num_interfaces) {
3049+
zend_do_inherit_interfaces(ce, parent_ce);
3050+
}
3051+
zend_build_properties_info_table(ce);
3052+
if ((ce->ce_flags & (ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) == ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
3053+
zend_verify_abstract_class(ce);
3054+
}
3055+
ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE));
3056+
ce->ce_flags |= ZEND_ACC_LINKED;
3057+
3058+
CG(current_linking_class) = orig_linking_class;
3059+
} zend_catch {
3060+
EG(record_errors) = false;
3061+
zend_free_recorded_errors();
3062+
zend_bailout();
3063+
} zend_end_try();
30443064

3045-
CG(current_linking_class) = orig_linking_class;
30463065
EG(record_errors) = false;
30473066

30483067
if (is_cacheable) {

ext/opcache/tests/gh8063-001.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Bug GH-8063 (Opcache breaks autoloading after E_COMPILE_ERROR) 001
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.record_warnings=0
7+
--EXTENSIONS--
8+
opcache
9+
--FILE--
10+
<?php
11+
12+
spl_autoload_register(function ($class) {
13+
printf("Autoloading %s\n", $class);
14+
include __DIR__.DIRECTORY_SEPARATOR.'gh8063'.DIRECTORY_SEPARATOR.$class.'.inc';
15+
});
16+
17+
register_shutdown_function(function () {
18+
new Bar();
19+
new Baz();
20+
print "Finished\n";
21+
});
22+
23+
new BadClass();
24+
--EXPECTF--
25+
Autoloading BadClass
26+
Autoloading Foo
27+
28+
Fatal error: Declaration of BadClass::dummy() must be compatible with Foo::dummy(): void in %sBadClass.inc on line 5
29+
Autoloading Bar
30+
Autoloading Baz
31+
Finished

ext/opcache/tests/gh8063-002.phpt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Bug GH-8063 (Opcache breaks autoloading after E_COMPILE_ERROR) 002
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.record_warnings=1
7+
--EXTENSIONS--
8+
opcache
9+
--FILE--
10+
<?php
11+
12+
spl_autoload_register(function ($class) {
13+
printf("Autoloading %s\n", $class);
14+
include __DIR__.DIRECTORY_SEPARATOR.'gh8063'.DIRECTORY_SEPARATOR.$class.'.inc';
15+
});
16+
17+
register_shutdown_function(function () {
18+
new Bar();
19+
new Baz();
20+
print "Finished\n";
21+
});
22+
23+
new BadClass();
24+
--EXPECTF--
25+
Autoloading BadClass
26+
Autoloading Foo
27+
28+
Fatal error: Declaration of BadClass::dummy() must be compatible with Foo::dummy(): void in %sBadClass.inc on line 5
29+
Autoloading Bar
30+
Autoloading Baz
31+
Finished

ext/opcache/tests/gh8063-003.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Bug GH-8063 (Opcache breaks autoloading after E_COMPILE_ERROR) 003
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.record_warnings=0
7+
--EXTENSIONS--
8+
opcache
9+
--FILE--
10+
<?php
11+
12+
spl_autoload_register(function ($class) {
13+
printf("Autoloading %s\n", $class);
14+
include __DIR__.DIRECTORY_SEPARATOR.'gh8063'.DIRECTORY_SEPARATOR.$class.'.inc';
15+
});
16+
17+
register_shutdown_function(function () {
18+
new Bar();
19+
new Baz();
20+
print "Finished\n";
21+
});
22+
23+
new BadClass2();
24+
--EXPECTF--
25+
Autoloading BadClass2
26+
27+
Fatal error: Declaration of BadClass2::dummy() must be compatible with Foo2::dummy(): void in %sBadClass2.inc on line %d
28+
Autoloading Bar
29+
Autoloading Baz
30+
Finished

ext/opcache/tests/gh8063/BadClass.inc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
class BadClass extends Foo
4+
{
5+
function dummy()
6+
{
7+
}
8+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
class Foo2
4+
{
5+
function dummy(): void
6+
{
7+
}
8+
}
9+
10+
class BadClass2 extends Foo2
11+
{
12+
function dummy()
13+
{
14+
}
15+
}

ext/opcache/tests/gh8063/Bar.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
class Bar {}

ext/opcache/tests/gh8063/Baz.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
class Baz {}

ext/opcache/tests/gh8063/Foo.inc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
class Foo
4+
{
5+
function dummy(): void
6+
{
7+
}
8+
}

0 commit comments

Comments
 (0)