Skip to content

Commit b3e33be

Browse files
committed
Forward shutdown exceptions to user error handlers
Fixes phpGH-10695 Closes GH-110905
1 parent 96ea06a commit b3e33be

File tree

17 files changed

+193
-3
lines changed

17 files changed

+193
-3
lines changed

UPGRADING

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ PHP 8.3 UPGRADE NOTES
107107
longer accepts paths containing the parent directory (`..`). Previously,
108108
only paths starting with `..` were disallowed. This could easily be
109109
circumvented by prepending `./` to the path.
110+
. User exception handlers now catch exceptions during shutdown.
110111

111112
- Dom:
112113
. Changed DOMCharacterData::appendData() tentative return type to true.

Zend/Optimizer/zend_func_infos.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ static const func_info_t func_infos[] = {
689689
F1("serialize", MAY_BE_STRING),
690690
F1("xml_error_string", MAY_BE_STRING|MAY_BE_NULL),
691691
F1("xml_parser_get_option", MAY_BE_STRING|MAY_BE_LONG|MAY_BE_BOOL),
692+
FN("zend_test_create_throwing_resource", MAY_BE_RESOURCE),
692693
FN("zip_open", MAY_BE_RESOURCE|MAY_BE_LONG|MAY_BE_FALSE),
693694
FN("zip_read", MAY_BE_RESOURCE|MAY_BE_FALSE),
694695
F1("ob_gzhandler", MAY_BE_STRING|MAY_BE_FALSE),

Zend/tests/gh10695_1.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
GH-10695: Exceptions in register_shutdown_function() are caught by set_exception_handler()
3+
--FILE--
4+
<?php
5+
set_exception_handler(function (\Throwable $exception) {
6+
echo 'Caught: ' . $exception->getMessage() . "\n";
7+
});
8+
9+
register_shutdown_function(function () {
10+
echo "register_shutdown_function()\n";
11+
throw new \Exception('shutdown');
12+
});
13+
?>
14+
--EXPECT--
15+
register_shutdown_function()
16+
Caught: shutdown

Zend/tests/gh10695_2.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
GH-10695: Exceptions in destructor during shutdown are caught
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __destruct() {
7+
throw new \Exception(__METHOD__);
8+
}
9+
}
10+
11+
set_exception_handler(function (\Throwable $exception) {
12+
echo 'Caught: ' . $exception->getMessage() . "\n";
13+
});
14+
15+
const FOO = new Foo;
16+
?>
17+
--EXPECT--
18+
Caught: Foo::__destruct

Zend/tests/gh10695_3.phpt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
GH-10695: Uncaught exceptions are not caught again
3+
--FILE--
4+
<?php
5+
register_shutdown_function(function () {
6+
echo "shutdown\n";
7+
set_exception_handler(function (\Throwable $exception) {
8+
echo 'Caught: ' . $exception->getMessage() . "\n";
9+
});
10+
});
11+
12+
throw new \Exception('main');
13+
?>
14+
--EXPECTF--
15+
Fatal error: Uncaught Exception: main in %s:%d
16+
Stack trace:
17+
#0 {main}
18+
thrown in %s on line %d
19+
shutdown

Zend/tests/gh10695_4.phpt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
GH-10695: Exception handlers are not called twice
3+
--FILE--
4+
<?php
5+
set_exception_handler(function (\Throwable $exception) {
6+
echo 'Caught: ' . $exception->getMessage() . "\n";
7+
throw new \Exception('exception handler');
8+
});
9+
10+
throw new \Exception('main');
11+
?>
12+
--EXPECTF--
13+
Caught: main
14+
15+
Fatal error: Uncaught Exception: exception handler in %s:%d
16+
Stack trace:
17+
#0 [internal function]: {closure}(Object(Exception))
18+
#1 {main}
19+
thrown in %s on line %d

Zend/tests/gh10695_5.phpt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
GH-10695: Exception handlers can register another exception handler
3+
--FILE--
4+
<?php
5+
set_exception_handler(function (\Throwable $exception) {
6+
echo 'Caught: ' . $exception->getMessage() . "\n";
7+
set_exception_handler(function (\Throwable $exception) {
8+
echo 'Caught: ' . $exception->getMessage() . "\n";
9+
});
10+
throw new \Exception('exception handler');
11+
});
12+
13+
throw new \Exception('main');
14+
?>
15+
--EXPECT--
16+
Caught: main
17+
Caught: exception handler

Zend/tests/gh10695_6.phpt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
GH-10695: Exceptions from output buffering handlers during shutdown are caught
3+
--FILE--
4+
<?php
5+
set_exception_handler(function (\Throwable $exception) {
6+
echo 'Caught: ' . $exception->getMessage() . "\n";
7+
});
8+
9+
ob_start(function () {
10+
throw new \Exception('ob_start');
11+
});
12+
?>
13+
--EXPECT--
14+
Caught: ob_start

Zend/tests/gh10695_7.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
GH-10695: Exceptions in error handlers during shutdown are caught
3+
--FILE--
4+
<?php
5+
set_exception_handler(function (\Throwable $exception) {
6+
echo 'Caught: ' . $exception->getMessage() . "\n";
7+
});
8+
set_error_handler(function ($errno, $errstr) {
9+
throw new \Exception($errstr);
10+
});
11+
register_shutdown_function(function () {
12+
trigger_error('main', E_USER_ERROR);
13+
});
14+
?>
15+
--EXPECT--
16+
Caught: main

Zend/zend.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,6 +1819,7 @@ ZEND_API ZEND_COLD void zend_user_exception_handler(void) /* {{{ */
18191819
EG(exception) = NULL;
18201820
ZVAL_OBJ(&params[0], old_exception);
18211821
ZVAL_COPY_VALUE(&orig_user_exception_handler, &EG(user_exception_handler));
1822+
ZVAL_UNDEF(&EG(user_exception_handler));
18221823

18231824
if (call_user_function(CG(function_table), NULL, &orig_user_exception_handler, &retval2, 1, params) == SUCCESS) {
18241825
zval_ptr_dtor(&retval2);
@@ -1830,6 +1831,8 @@ ZEND_API ZEND_COLD void zend_user_exception_handler(void) /* {{{ */
18301831
} else {
18311832
EG(exception) = old_exception;
18321833
}
1834+
1835+
zval_ptr_dtor(&orig_user_exception_handler);
18331836
} /* }}} */
18341837

18351838
ZEND_API zend_result zend_execute_scripts(int type, zval *retval, int file_count, ...) /* {{{ */

0 commit comments

Comments
 (0)