Skip to content

Commit 279831c

Browse files
committed
Always record errors during compilation
1 parent 1dd8bf4 commit 279831c

12 files changed

+91
-45
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

+39-4
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,31 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
14521452
return;
14531453
}
14541454

1455+
/* Emit any delayed error before handling fatal error */
1456+
if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL)) {
1457+
if (EG(num_errors)) {
1458+
uint32_t num_errors = EG(num_errors);
1459+
zend_error_info **errors = EG(errors);
1460+
EG(num_errors) = 0;
1461+
EG(errors) = NULL;
1462+
1463+
bool orig_record_errors = EG(record_errors);
1464+
EG(record_errors) = false;
1465+
1466+
/* Disable user error handler before emitting delayed errors, as
1467+
* it's unsafe to execute user code after a fatal error. */
1468+
int orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
1469+
EG(user_error_handler_error_reporting) = 0;
1470+
1471+
zend_emit_recorded_errors_ex(num_errors, errors);
1472+
1473+
EG(user_error_handler_error_reporting) = orig_user_error_handler_error_reporting;
1474+
EG(record_errors) = orig_record_errors;
1475+
EG(num_errors) = num_errors;
1476+
EG(errors) = errors;
1477+
}
1478+
}
1479+
14551480
if (EG(record_errors)) {
14561481
zend_error_info *info = emalloc(sizeof(zend_error_info));
14571482
info->type = type;
@@ -1464,6 +1489,11 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
14641489
EG(num_errors)++;
14651490
EG(errors) = erealloc(EG(errors), sizeof(zend_error_info*) * EG(num_errors));
14661491
EG(errors)[EG(num_errors)-1] = info;
1492+
1493+
/* Do not process non-fatal recorded error */
1494+
if (!(type & E_FATAL_ERRORS) || (type & E_DONT_BAIL)) {
1495+
return;
1496+
}
14671497
}
14681498

14691499
// Always clear the last backtrace.
@@ -1752,15 +1782,20 @@ ZEND_API void zend_begin_record_errors(void)
17521782
EG(errors) = NULL;
17531783
}
17541784

1755-
ZEND_API void zend_emit_recorded_errors(void)
1785+
ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors)
17561786
{
1757-
EG(record_errors) = false;
1758-
for (uint32_t i = 0; i < EG(num_errors); i++) {
1759-
zend_error_info *error = EG(errors)[i];
1787+
for (uint32_t i = 0; i < num_errors; i++) {
1788+
zend_error_info *error = errors[i];
17601789
zend_error_zstr_at(error->type, error->filename, error->lineno, error->message);
17611790
}
17621791
}
17631792

1793+
ZEND_API void zend_emit_recorded_errors(void)
1794+
{
1795+
EG(record_errors) = false;
1796+
zend_emit_recorded_errors_ex(EG(num_errors), EG(errors));
1797+
}
1798+
17641799
ZEND_API void zend_free_recorded_errors(void)
17651800
{
17661801
if (!EG(num_errors)) {

Zend/zend.h

+1
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ ZEND_API void zend_replace_error_handling(zend_error_handling_t error_handling,
444444
ZEND_API void zend_restore_error_handling(zend_error_handling *saved);
445445
ZEND_API void zend_begin_record_errors(void);
446446
ZEND_API void zend_emit_recorded_errors(void);
447+
ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors);
447448
ZEND_API void zend_free_recorded_errors(void);
448449
END_EXTERN_C()
449450

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_globals.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ struct _zend_executor_globals {
295295
size_t fiber_stack_size;
296296

297297
/* If record_errors is enabled, all emitted diagnostics will be recorded,
298-
* in addition to being processed as usual. */
298+
* and their processing is delayed until zend_emit_recorded_errors()
299+
* is called or a fatal diagnostic is emitted. */
299300
bool record_errors;
300301
uint32_t num_errors;
301302
zend_error_info **errors;

Zend/zend_inheritance.c

+2-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),
@@ -3759,6 +3756,7 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
37593756
}
37603757

37613758
if (!orig_record_errors) {
3759+
zend_emit_recorded_errors();
37623760
zend_free_recorded_errors();
37633761
}
37643762
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

+28-24
Original file line numberDiff line numberDiff line change
@@ -1732,13 +1732,6 @@ static void zend_accel_set_auto_globals(int mask)
17321732
ZCG(auto_globals_mask) |= mask;
17331733
}
17341734

1735-
static void replay_warnings(uint32_t num_warnings, zend_error_info **warnings) {
1736-
for (uint32_t i = 0; i < num_warnings; i++) {
1737-
zend_error_info *warning = warnings[i];
1738-
zend_error_zstr_at(warning->type, warning->filename, warning->lineno, warning->message);
1739-
}
1740-
}
1741-
17421735
static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, zend_op_array **op_array_p)
17431736
{
17441737
zend_persistent_script *new_persistent_script;
@@ -1812,11 +1805,6 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
18121805
orig_functions_count = EG(function_table)->nNumUsed;
18131806
orig_class_count = EG(class_table)->nNumUsed;
18141807

1815-
/* Override them with ours */
1816-
if (ZCG(accel_directives).record_warnings) {
1817-
zend_begin_record_errors();
1818-
}
1819-
18201808
zend_try {
18211809
orig_compiler_options = CG(compiler_options);
18221810
CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
@@ -1845,12 +1833,12 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
18451833

18461834
/* Restore originals */
18471835
CG(active_op_array) = orig_active_op_array;
1848-
EG(record_errors) = 0;
18491836

18501837
if (!op_array) {
18511838
/* compilation failed */
1852-
zend_free_recorded_errors();
18531839
if (do_bailout) {
1840+
EG(record_errors) = false;
1841+
zend_free_recorded_errors();
18541842
zend_bailout();
18551843
}
18561844
return NULL;
@@ -1865,10 +1853,6 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
18651853
zend_accel_move_user_functions(CG(function_table), CG(function_table)->nNumUsed - orig_functions_count, &new_persistent_script->script);
18661854
zend_accel_move_user_classes(CG(class_table), CG(class_table)->nNumUsed - orig_class_count, &new_persistent_script->script);
18671855
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;
18721856

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

@@ -1950,7 +1934,7 @@ static zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int
19501934
}
19511935
}
19521936
}
1953-
replay_warnings(persistent_script->num_warnings, persistent_script->warnings);
1937+
zend_emit_recorded_errors_ex(persistent_script->num_warnings, persistent_script->warnings);
19541938

19551939
if (persistent_script->ping_auto_globals_mask & ~ZCG(auto_globals_mask)) {
19561940
zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask & ~ZCG(auto_globals_mask));
@@ -1959,11 +1943,22 @@ static zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int
19591943
return zend_accel_load_script(persistent_script, 1);
19601944
}
19611945

1946+
zend_begin_record_errors();
1947+
19621948
persistent_script = opcache_compile_file(file_handle, type, &op_array);
19631949

19641950
if (persistent_script) {
1951+
if (ZCG(accel_directives).record_warnings) {
1952+
persistent_script->num_warnings = EG(num_errors);
1953+
persistent_script->warnings = EG(errors);
1954+
}
1955+
19651956
from_memory = false;
19661957
persistent_script = cache_script_in_file_cache(persistent_script, &from_memory);
1958+
1959+
zend_emit_recorded_errors();
1960+
zend_free_recorded_errors();
1961+
19671962
return zend_accel_load_script(persistent_script, from_memory);
19681963
}
19691964

@@ -2162,6 +2157,8 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
21622157
return accelerator_orig_compile_file(file_handle, type);
21632158
}
21642159

2160+
zend_begin_record_errors();
2161+
21652162
SHM_PROTECT();
21662163
HANDLE_UNBLOCK_INTERRUPTIONS();
21672164
persistent_script = opcache_compile_file(file_handle, type, &op_array);
@@ -2173,6 +2170,11 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
21732170
*/
21742171
from_shared_memory = false;
21752172
if (persistent_script) {
2173+
if (ZCG(accel_directives).record_warnings) {
2174+
persistent_script->num_warnings = EG(num_errors);
2175+
persistent_script->warnings = EG(errors);
2176+
}
2177+
21762178
/* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */
21772179
bool orig_gc_state = gc_enable(false);
21782180
persistent_script = cache_script_in_shared_memory(persistent_script, key, &from_shared_memory);
@@ -2185,6 +2187,8 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
21852187
if (!persistent_script) {
21862188
SHM_PROTECT();
21872189
HANDLE_UNBLOCK_INTERRUPTIONS();
2190+
zend_emit_recorded_errors();
2191+
zend_free_recorded_errors();
21882192
return op_array;
21892193
}
21902194
if (from_shared_memory) {
@@ -2198,6 +2202,9 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
21982202
persistent_script->dynamic_members.last_used = ZCG(request_time);
21992203
SHM_PROTECT();
22002204
HANDLE_UNBLOCK_INTERRUPTIONS();
2205+
2206+
zend_emit_recorded_errors();
2207+
zend_free_recorded_errors();
22012208
} else {
22022209

22032210
#ifndef ZEND_WIN32
@@ -2240,7 +2247,7 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
22402247
SHM_PROTECT();
22412248
HANDLE_UNBLOCK_INTERRUPTIONS();
22422249

2243-
replay_warnings(persistent_script->num_warnings, persistent_script->warnings);
2250+
zend_emit_recorded_errors_ex(persistent_script->num_warnings, persistent_script->warnings);
22442251
from_shared_memory = true;
22452252
}
22462253

@@ -2307,7 +2314,7 @@ static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce,
23072314
entry = zend_accel_inheritance_cache_find(entry, ce, parent, traits_and_interfaces, &needs_autoload);
23082315
if (entry) {
23092316
if (!needs_autoload) {
2310-
replay_warnings(entry->num_warnings, entry->warnings);
2317+
zend_emit_recorded_errors_ex(entry->num_warnings, entry->warnings);
23112318
if (ZCSG(map_ptr_last) > CG(map_ptr_last)) {
23122319
zend_map_ptr_extend(ZCSG(map_ptr_last));
23132320
}
@@ -2461,9 +2468,6 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce,
24612468
entry->next = proto->inheritance_cache;
24622469
proto->inheritance_cache = entry;
24632470

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

24692473
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)