Skip to content

Commit 7b3e68f

Browse files
arnaud-lbTimWolla
andcommitted
Fix error handling inconsistency with opcache
When opcache is enabled, error handling is altered in the following ways: * Errors emitted during compilation bypass the user-defined error handler * Exceptions emitted during class linking are turned into fatal errors Changes here make the behavior consistent regardless of opcache being enabled or not: * Errors emitted during compilation and class linking are always delayed and handled after compilation or class linking. During handling, user-defined error handlers are not bypassed. Fatal errors emitted during compilation or class linking cause any delayed errors to be handled immediately (without calling user-defined error handlers, as it would be unsafe). * Exceptions thrown by user-defined error handlers when handling class linking error are not promoted to fatal errors anymore and do not prevent linking. Fixes GH-17422. Closes GH-18541. Closes GH-17627. Co-authored-by: Tim Düsterhus <[email protected]>
1 parent 9b777b3 commit 7b3e68f

36 files changed

+408
-61
lines changed

.github/scripts/windows/test_task.bat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ mkdir %PHP_BUILD_DIR%\test_file_cache
128128
rem generate php.ini
129129
echo extension_dir=%PHP_BUILD_DIR% > %PHP_BUILD_DIR%\php.ini
130130
echo opcache.file_cache=%PHP_BUILD_DIR%\test_file_cache >> %PHP_BUILD_DIR%\php.ini
131+
echo opcache.record_warnings=1 >> %PHP_BUILD_DIR%\php.ini
131132
rem work-around for some spawned PHP processes requiring OpenSSL and sockets
132133
echo extension=php_openssl.dll >> %PHP_BUILD_DIR%\php.ini
133134
echo extension=php_sockets.dll >> %PHP_BUILD_DIR%\php.ini

NEWS

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ PHP NEWS
1515
- OPcache:
1616
. Disallow changing opcache.memory_consumption when SHM is already set up.
1717
(timwolla)
18-
. Fixed GH-15074 (Compiling opcache statically into ZTS PHP fails). (Arnaud)
18+
. Fixed bug GH-15074 (Compiling opcache statically into ZTS PHP fails).
19+
(Arnaud)
1920
. Make OPcache non-optional (Arnaud, timwolla)
21+
. Fixed bug GH-17422 (OPcache bypasses the user-defined error handler for
22+
deprecations). (Arnaud, timwolla)
2023

2124
- OpenSSL:
2225
. Add $digest_algo parameter to openssl_public_encrypt() and

UPGRADING

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ PHP 8.5 UPGRADE NOTES
4444
. Traits are now bound before the parent class. This is a subtle behavioral
4545
change, but should more closely match user expectations, demonstrated by
4646
GH-15753 and GH-16198.
47+
. Errors emitted during compilation and class linking are now always delayed
48+
and handled after compilation or class linking. Fatal errors emitted during
49+
compilation or class linking cause any delayed errors to be handled
50+
immediately, without calling user-defined error handlers.
51+
. Exceptions thrown by user-defined error handlers when handling class linking
52+
errors are not promoted to fatal errors anymore and do not prevent linking.
4753

4854
- DOM:
4955
. Cloning a DOMNamedNodeMap, DOMNodeList, Dom\NamedNodeMap, Dom\NodeList,

UPGRADING.INTERNALS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ PHP 8.5 INTERNALS UPGRADE NOTES
7171
* zend_register_string_constant()
7272
* zend_register_stringl_constant()
7373
. EG(fake_scope) now is a _const_ zend_class_entry*.
74+
. zend_begin_record_errors() or EG(record_errors)=true cause errors to be
75+
delayed. Before, errors would be recorded but not delayed.
7476

7577
========================
7678
2. Build system changes

Zend/tests/inheritance/deprecation_to_exception_during_inheritance.phpt

Lines changed: 4 additions & 3 deletions
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
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
Deprecation promoted to exception during inheritance
3+
--SKIPIF--
4+
<?php
5+
if (getenv('SKIP_PRELOAD')) die('skip Error handler not active during preloading');
6+
?>
7+
--FILE--
8+
<?php
9+
10+
set_error_handler(function($code, $message) {
11+
throw new Exception($message);
12+
});
13+
14+
try {
15+
class C extends DateTime {
16+
public function getTimezone() {}
17+
public function getTimestamp() {}
18+
};
19+
} catch (Exception $e) {
20+
printf("%s: %s\n", $e::class, $e->getMessage());
21+
}
22+
23+
var_dump(new C());
24+
25+
?>
26+
--EXPECTF--
27+
Exception: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
28+
object(C)#%d (3) {
29+
["date"]=>
30+
string(%d) "%s"
31+
["timezone_type"]=>
32+
int(3)
33+
["timezone"]=>
34+
string(3) "UTC"
35+
}

Zend/tests/inheritance/gh15907.phpt

Lines changed: 5 additions & 2 deletions
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...', '%s', 7)
20+
#1 {main}
21+
thrown in %s on line %d

Zend/zend.c

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,29 @@ 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) && EG(num_errors)) {
1457+
uint32_t num_errors = EG(num_errors);
1458+
zend_error_info **errors = EG(errors);
1459+
EG(num_errors) = 0;
1460+
EG(errors) = NULL;
1461+
1462+
bool orig_record_errors = EG(record_errors);
1463+
EG(record_errors) = false;
1464+
1465+
/* Disable user error handler before emitting delayed errors, as
1466+
* it's unsafe to execute user code after a fatal error. */
1467+
int orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
1468+
EG(user_error_handler_error_reporting) = 0;
1469+
1470+
zend_emit_recorded_errors_ex(num_errors, errors);
1471+
1472+
EG(user_error_handler_error_reporting) = orig_user_error_handler_error_reporting;
1473+
EG(record_errors) = orig_record_errors;
1474+
EG(num_errors) = num_errors;
1475+
EG(errors) = errors;
1476+
}
1477+
14551478
if (EG(record_errors)) {
14561479
zend_error_info *info = emalloc(sizeof(zend_error_info));
14571480
info->type = type;
@@ -1464,6 +1487,11 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
14641487
EG(num_errors)++;
14651488
EG(errors) = erealloc(EG(errors), sizeof(zend_error_info*) * EG(num_errors));
14661489
EG(errors)[EG(num_errors)-1] = info;
1490+
1491+
/* Do not process non-fatal recorded error */
1492+
if (!(type & E_FATAL_ERRORS) || (type & E_DONT_BAIL)) {
1493+
return;
1494+
}
14671495
}
14681496

14691497
// Always clear the last backtrace.
@@ -1752,15 +1780,20 @@ ZEND_API void zend_begin_record_errors(void)
17521780
EG(errors) = NULL;
17531781
}
17541782

1755-
ZEND_API void zend_emit_recorded_errors(void)
1783+
ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors)
17561784
{
1757-
EG(record_errors) = false;
1758-
for (uint32_t i = 0; i < EG(num_errors); i++) {
1759-
zend_error_info *error = EG(errors)[i];
1785+
for (uint32_t i = 0; i < num_errors; i++) {
1786+
zend_error_info *error = errors[i];
17601787
zend_error_zstr_at(error->type, error->filename, error->lineno, error->message);
17611788
}
17621789
}
17631790

1791+
ZEND_API void zend_emit_recorded_errors(void)
1792+
{
1793+
EG(record_errors) = false;
1794+
zend_emit_recorded_errors_ex(EG(num_errors), EG(errors));
1795+
}
1796+
17641797
ZEND_API void zend_free_recorded_errors(void)
17651798
{
17661799
if (!EG(num_errors)) {

Zend/zend.h

Lines changed: 1 addition & 0 deletions
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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1327,7 +1327,6 @@ ZEND_API zend_class_entry *zend_bind_class_in_slot(
13271327

13281328
ce = zend_do_link_class(ce, lc_parent_name, Z_STR_P(lcname));
13291329
if (ce) {
1330-
ZEND_ASSERT(!EG(exception));
13311330
zend_observer_class_linked_notify(ce, Z_STR_P(lcname));
13321331
return ce;
13331332
}

0 commit comments

Comments
 (0)