Skip to content

Commit 7a2b88d

Browse files
committed
Always record errors during compilation
1 parent 1dd8bf4 commit 7a2b88d

10 files changed

+68
-30
lines changed

Zend/tests/inheritance/deprecation_to_exception_during_inheritance.phpt

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Deprecation promoted to exception should result in fatal error during inheritance
2+
Deprecation promoted to exception during inheritance
33
--SKIPIF--
44
<?php
55
if (getenv('SKIP_PRELOAD')) die('skip Error handler not active during preloading');
@@ -17,7 +17,8 @@ $class = new class extends DateTime {
1717

1818
?>
1919
--EXPECTF--
20-
Fatal error: During inheritance of DateTime: Uncaught Exception: Return type of DateTime@anonymous::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in %s:%d
20+
Fatal error: Uncaught Exception: Return type of DateTime@anonymous::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in %s:%d
2121
Stack trace:
2222
#0 %s(%d): {closure:%s:%d}(8192, 'Return type of ...', '%s', 8)
23-
#1 {main} in %s on line %d
23+
#1 {main}
24+
thrown in %s on line %d

Zend/tests/inheritance/gh15907.phpt

+5-2
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,8 @@ class C implements Serializable {
1414

1515
?>
1616
--EXPECTF--
17-
Fatal error: During inheritance of C, while implementing Serializable: Uncaught Exception: C implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in %s:%d
18-
%a
17+
Fatal error: Uncaught Exception: C implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in %s:%d
18+
Stack trace:
19+
#0 %s(%d): {closure:%s:%d}(8192, 'C implements th...', '/home/arnaud/de...', 7)
20+
#1 {main}
21+
thrown in %s on line %d

Zend/zend.c

+23
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,29 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
14641464
EG(num_errors)++;
14651465
EG(errors) = erealloc(EG(errors), sizeof(zend_error_info*) * EG(num_errors));
14661466
EG(errors)[EG(num_errors)-1] = info;
1467+
1468+
/* Fatal errors must be processed immediately */
1469+
if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL)) {
1470+
EG(record_errors) = false;
1471+
1472+
/* Disable user error handler before emitting recorded errors, as
1473+
* it's unsafe to execute user code after a fatal error. */
1474+
int orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
1475+
EG(user_error_handler_error_reporting) = 0;
1476+
1477+
zend_try {
1478+
zend_emit_recorded_errors();
1479+
} zend_catch {
1480+
} zend_end_try();
1481+
1482+
zend_free_recorded_errors();
1483+
EG(user_error_handler_error_reporting) = orig_user_error_handler_error_reporting;
1484+
1485+
zend_bailout();
1486+
}
1487+
1488+
/* Do not process recorded error */
1489+
return;
14671490
}
14681491

14691492
// Always clear the last backtrace.

Zend/zend_compile.c

-1
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,6 @@ ZEND_API zend_class_entry *zend_bind_class_in_slot(
13331333

13341334
ce = zend_do_link_class(ce, lc_parent_name, Z_STR_P(lcname));
13351335
if (ce) {
1336-
ZEND_ASSERT(!EG(exception));
13371336
zend_observer_class_linked_notify(ce, Z_STR_P(lcname));
13381337
return ce;
13391338
}

Zend/zend_inheritance.c

+3-4
Original file line numberDiff line numberDiff line change
@@ -1085,10 +1085,7 @@ static void ZEND_COLD emit_incompatible_method_error(
10851085
"Return type of %s should either be compatible with %s, "
10861086
"or the #[\\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice",
10871087
ZSTR_VAL(child_prototype), ZSTR_VAL(parent_prototype));
1088-
if (EG(exception)) {
1089-
zend_exception_uncaught_error(
1090-
"During inheritance of %s", ZSTR_VAL(parent_scope->name));
1091-
}
1088+
ZEND_ASSERT(!EG(exception));
10921089
}
10931090
} else {
10941091
zend_error_at(E_COMPILE_ERROR, func_filename(child), func_lineno(child),
@@ -3710,6 +3707,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
37103707
/* Do not leak recorded errors to the next linked class. */
37113708
if (!orig_record_errors) {
37123709
EG(record_errors) = false;
3710+
zend_emit_recorded_errors();
37133711
zend_free_recorded_errors();
37143712
}
37153713
zend_bailout();
@@ -3759,6 +3757,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
37593757
}
37603758

37613759
if (!orig_record_errors) {
3760+
zend_emit_recorded_errors();
37623761
zend_free_recorded_errors();
37633762
}
37643763
if (traits_and_interfaces) {

Zend/zend_vm_def.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -7926,7 +7926,7 @@ ZEND_VM_HANDLER(145, ZEND_DECLARE_CLASS_DELAYED, CONST, CONST)
79267926
if (zv) {
79277927
SAVE_OPLINE();
79287928
ce = zend_bind_class_in_slot(zv, lcname, Z_STR_P(RT_CONSTANT(opline, opline->op2)));
7929-
if (!ce) {
7929+
if (EG(exception)) {
79307930
HANDLE_EXCEPTION();
79317931
}
79327932
}
@@ -7950,7 +7950,7 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT)
79507950
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
79517951
SAVE_OPLINE();
79527952
ce = zend_do_link_class(ce, (OP2_TYPE == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL, rtd_key);
7953-
if (!ce) {
7953+
if (EG(exception)) {
79547954
HANDLE_EXCEPTION();
79557955
}
79567956
}

ext/opcache/ZendAccelerator.c

+23-14
Original file line numberDiff line numberDiff line change
@@ -1812,11 +1812,6 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
18121812
orig_functions_count = EG(function_table)->nNumUsed;
18131813
orig_class_count = EG(class_table)->nNumUsed;
18141814

1815-
/* Override them with ours */
1816-
if (ZCG(accel_directives).record_warnings) {
1817-
zend_begin_record_errors();
1818-
}
1819-
18201815
zend_try {
18211816
orig_compiler_options = CG(compiler_options);
18221817
CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
@@ -1845,12 +1840,11 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
18451840

18461841
/* Restore originals */
18471842
CG(active_op_array) = orig_active_op_array;
1848-
EG(record_errors) = 0;
18491843

18501844
if (!op_array) {
18511845
/* compilation failed */
1852-
zend_free_recorded_errors();
18531846
if (do_bailout) {
1847+
zend_free_recorded_errors();
18541848
zend_bailout();
18551849
}
18561850
return NULL;
@@ -1865,10 +1859,6 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
18651859
zend_accel_move_user_functions(CG(function_table), CG(function_table)->nNumUsed - orig_functions_count, &new_persistent_script->script);
18661860
zend_accel_move_user_classes(CG(class_table), CG(class_table)->nNumUsed - orig_class_count, &new_persistent_script->script);
18671861
zend_accel_build_delayed_early_binding_list(new_persistent_script);
1868-
new_persistent_script->num_warnings = EG(num_errors);
1869-
new_persistent_script->warnings = EG(errors);
1870-
EG(num_errors) = 0;
1871-
EG(errors) = NULL;
18721862

18731863
efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
18741864

@@ -1962,8 +1952,16 @@ static zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int
19621952
persistent_script = opcache_compile_file(file_handle, type, &op_array);
19631953

19641954
if (persistent_script) {
1955+
if (ZCG(accel_directives).record_warnings) {
1956+
persistent_script->num_warnings = EG(num_errors);
1957+
persistent_script->warnings = EG(errors);
1958+
}
1959+
19651960
from_memory = false;
19661961
persistent_script = cache_script_in_file_cache(persistent_script, &from_memory);
1962+
1963+
zend_emit_recorded_errors();
1964+
19671965
return zend_accel_load_script(persistent_script, from_memory);
19681966
}
19691967

@@ -2162,6 +2160,8 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
21622160
return accelerator_orig_compile_file(file_handle, type);
21632161
}
21642162

2163+
zend_begin_record_errors();
2164+
21652165
SHM_PROTECT();
21662166
HANDLE_UNBLOCK_INTERRUPTIONS();
21672167
persistent_script = opcache_compile_file(file_handle, type, &op_array);
@@ -2173,6 +2173,11 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
21732173
*/
21742174
from_shared_memory = false;
21752175
if (persistent_script) {
2176+
if (ZCG(accel_directives).record_warnings) {
2177+
persistent_script->num_warnings = EG(num_errors);
2178+
persistent_script->warnings = EG(errors);
2179+
}
2180+
21762181
/* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */
21772182
bool orig_gc_state = gc_enable(false);
21782183
persistent_script = cache_script_in_shared_memory(persistent_script, key, &from_shared_memory);
@@ -2185,6 +2190,9 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
21852190
if (!persistent_script) {
21862191
SHM_PROTECT();
21872192
HANDLE_UNBLOCK_INTERRUPTIONS();
2193+
EG(record_errors) = false;
2194+
zend_emit_recorded_errors();
2195+
zend_free_recorded_errors();
21882196
return op_array;
21892197
}
21902198
if (from_shared_memory) {
@@ -2198,6 +2206,10 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
21982206
persistent_script->dynamic_members.last_used = ZCG(request_time);
21992207
SHM_PROTECT();
22002208
HANDLE_UNBLOCK_INTERRUPTIONS();
2209+
2210+
EG(record_errors) = false;
2211+
zend_emit_recorded_errors();
2212+
zend_free_recorded_errors();
22012213
} else {
22022214

22032215
#ifndef ZEND_WIN32
@@ -2461,9 +2473,6 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce,
24612473
entry->next = proto->inheritance_cache;
24622474
proto->inheritance_cache = entry;
24632475

2464-
EG(num_errors) = 0;
2465-
EG(errors) = NULL;
2466-
24672476
ZCSG(map_ptr_last) = CG(map_ptr_last);
24682477

24692478
zend_shared_alloc_destroy_xlat_table();

ext/opcache/tests/gh17422/003.phpt

+3-1
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ warning();
2222
?>
2323
--EXPECTF--
2424
Fatal error: Allowed memory size of 2097152 bytes exhausted %s on line 6
25-
array(2) {
25+
array(3) {
2626
[0]=>
2727
string(7) "003.php"
2828
[1]=>
2929
string(12) "shutdown.inc"
30+
[2]=>
31+
string(11) "warning.inc"
3032
}

ext/opcache/tests/gh17422/004.phpt

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ warning();
2828
?>
2929
--EXPECTF--
3030
Fatal error: Cannot redeclare %Swarning() (previously declared in %s(8) : eval()'d code:1) in %swarning.inc on line 2
31-
array(2) {
31+
array(3) {
3232
[0]=>
3333
string(7) "004.php"
3434
[1]=>
3535
string(12) "shutdown.inc"
36+
[2]=>
37+
string(11) "warning.inc"
3638
}

ext/opcache/zend_persist.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1355,11 +1355,11 @@ static void zend_accel_persist_class_table(HashTable *class_table)
13551355

13561356
zend_error_info **zend_persist_warnings(uint32_t num_warnings, zend_error_info **warnings) {
13571357
if (warnings) {
1358-
warnings = zend_shared_memdup_free(warnings, num_warnings * sizeof(zend_error_info *));
1358+
warnings = zend_shared_memdup(warnings, num_warnings * sizeof(zend_error_info *));
13591359
for (uint32_t i = 0; i < num_warnings; i++) {
1360-
warnings[i] = zend_shared_memdup_free(warnings[i], sizeof(zend_error_info));
13611360
zend_accel_store_string(warnings[i]->filename);
13621361
zend_accel_store_string(warnings[i]->message);
1362+
warnings[i] = zend_shared_memdup(warnings[i], sizeof(zend_error_info));
13631363
}
13641364
}
13651365
return warnings;

0 commit comments

Comments
 (0)