mirror of
https://github.com/php/php-src.git
synced 2024-11-23 18:04:36 +08:00
Restore error handler after running it
Symfony relies on finding the exception handler in the handler stack. There's currently no clean API to find it, so they pop all the handlers, and push them again once the stack is empty. This PR attempts to minimize the BC break by pushing the current handler onto the stack and clearing the current handler, and restoring it once it has finished. This is essentially equivalent to set_exception_handler(null) and restore_exception_handler(). restore_exception_handler() however is only called if the exception handler is still unset. If the handler has pushed a new handler in the meantime, we assume it knows what it's doing. Fixes GH-13446 Closes GH-13686
This commit is contained in:
parent
f2ec6e4806
commit
3301d9602a
1
NEWS
1
NEWS
@ -7,6 +7,7 @@ PHP NEWS
|
||||
scanning WeakMaps). (Arnaud)
|
||||
. Fixed bug GH-13612 (Corrupted memory in destructor with weak references).
|
||||
(nielsdos)
|
||||
. Fixed bug GH-13446 (Restore exception handler after it finishes). (ilutov)
|
||||
|
||||
- DOM:
|
||||
. Add some missing ZPP checks. (nielsdos)
|
||||
|
19
Zend/tests/gh13446_1.phpt
Normal file
19
Zend/tests/gh13446_1.phpt
Normal file
@ -0,0 +1,19 @@
|
||||
--TEST--
|
||||
GH-13446: Exception handler is restored after is has finished
|
||||
--FILE--
|
||||
<?php
|
||||
function exception_handler($ex) {
|
||||
echo 'Exception caught: ', $ex->getMessage(), "\n";
|
||||
}
|
||||
set_exception_handler('exception_handler');
|
||||
|
||||
register_shutdown_function(function () {
|
||||
echo set_exception_handler(null), "\n";
|
||||
restore_exception_handler();
|
||||
});
|
||||
|
||||
throw new Exception('Test');
|
||||
?>
|
||||
--EXPECT--
|
||||
Exception caught: Test
|
||||
exception_handler
|
16
Zend/tests/gh13446_2.phpt
Normal file
16
Zend/tests/gh13446_2.phpt
Normal file
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
GH-13446: Exception handler attempting to free itself
|
||||
--FILE--
|
||||
<?php
|
||||
$x = new \stdClass();
|
||||
$handler = function ($ex) use (&$handler, $x) {
|
||||
$handler = null;
|
||||
var_dump($x);
|
||||
};
|
||||
unset($x);
|
||||
set_exception_handler($handler);
|
||||
throw new Exception('Unhandled');
|
||||
?>
|
||||
--EXPECT--
|
||||
object(stdClass)#1 (0) {
|
||||
}
|
25
Zend/tests/gh13446_3.phpt
Normal file
25
Zend/tests/gh13446_3.phpt
Normal file
@ -0,0 +1,25 @@
|
||||
--TEST--
|
||||
GH-13446: Exception handler isn't restored if it is previously modified
|
||||
--FILE--
|
||||
<?php
|
||||
function exception_handler_1($ex) {
|
||||
echo "Handler 1\n";
|
||||
set_exception_handler('exception_handler_2');
|
||||
}
|
||||
|
||||
function exception_handler_2($ex) {
|
||||
echo "Handler 2\n";
|
||||
}
|
||||
|
||||
set_exception_handler('exception_handler_1');
|
||||
|
||||
register_shutdown_function(function () {
|
||||
echo set_exception_handler(null), "\n";
|
||||
restore_exception_handler();
|
||||
});
|
||||
|
||||
throw new Exception();
|
||||
?>
|
||||
--EXPECT--
|
||||
Handler 1
|
||||
exception_handler_2
|
21
Zend/tests/gh13446_4.phpt
Normal file
21
Zend/tests/gh13446_4.phpt
Normal file
@ -0,0 +1,21 @@
|
||||
--TEST--
|
||||
GH-13446: Exception handler isn't restored if stack is empty
|
||||
--FILE--
|
||||
<?php
|
||||
function exception_handler() {
|
||||
echo "Handler\n";
|
||||
restore_exception_handler();
|
||||
restore_exception_handler();
|
||||
}
|
||||
set_exception_handler('exception_handler');
|
||||
|
||||
register_shutdown_function(function () {
|
||||
var_dump(set_exception_handler(null));
|
||||
restore_exception_handler();
|
||||
});
|
||||
|
||||
throw new Exception();
|
||||
?>
|
||||
--EXPECT--
|
||||
Handler
|
||||
NULL
|
10
Zend/zend.c
10
Zend/zend.c
@ -1839,7 +1839,9 @@ ZEND_API ZEND_COLD void zend_user_exception_handler(void) /* {{{ */
|
||||
old_exception = EG(exception);
|
||||
EG(exception) = NULL;
|
||||
ZVAL_OBJ(¶ms[0], old_exception);
|
||||
|
||||
ZVAL_COPY_VALUE(&orig_user_exception_handler, &EG(user_exception_handler));
|
||||
zend_stack_push(&EG(user_exception_handlers), &orig_user_exception_handler);
|
||||
ZVAL_UNDEF(&EG(user_exception_handler));
|
||||
|
||||
if (call_user_function(CG(function_table), NULL, &orig_user_exception_handler, &retval2, 1, params) == SUCCESS) {
|
||||
@ -1853,7 +1855,13 @@ ZEND_API ZEND_COLD void zend_user_exception_handler(void) /* {{{ */
|
||||
EG(exception) = old_exception;
|
||||
}
|
||||
|
||||
zval_ptr_dtor(&orig_user_exception_handler);
|
||||
if (Z_TYPE(EG(user_exception_handler)) == IS_UNDEF) {
|
||||
zval *tmp = zend_stack_top(&EG(user_exception_handlers));
|
||||
if (tmp) {
|
||||
ZVAL_COPY_VALUE(&EG(user_exception_handler), tmp);
|
||||
zend_stack_del_top(&EG(user_exception_handlers));
|
||||
}
|
||||
}
|
||||
} /* }}} */
|
||||
|
||||
ZEND_API zend_result zend_execute_scripts(int type, zval *retval, int file_count, ...) /* {{{ */
|
||||
|
Loading…
Reference in New Issue
Block a user