From cebe433e5617620ce61c9636225623b9c32ce15b Mon Sep 17 00:00:00 2001 From: Marc Bennewitz Date: Thu, 27 Nov 2025 09:41:12 +0100 Subject: [PATCH] Adds possibility to include called object in exception backtrace --- Zend/tests/exception_provide_object.phpt | 158 +++++++++++++++++++++++ Zend/zend.c | 1 + Zend/zend_exceptions.c | 11 +- Zend/zend_globals.h | 1 + php.ini-development | 14 +- php.ini-production | 14 +- 6 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 Zend/tests/exception_provide_object.phpt diff --git a/Zend/tests/exception_provide_object.phpt b/Zend/tests/exception_provide_object.phpt new file mode 100644 index 0000000000000..fefdb39fcdd1b --- /dev/null +++ b/Zend/tests/exception_provide_object.phpt @@ -0,0 +1,158 @@ +--TEST-- +Exceptions providing object +--FILE-- +random = random_int(0, 100); + } + + function doThrow(...$args) { + throw new Exception(); + } + + function funcDoThrow() { + return fn(...$args) => $this->doThrow(...$args); + } +} + +function doThrow(...$args) { + (new Test())->funcDoThrow()(...$args); +} + +echo "zend.exception_ignore_args=0\n"; +echo "zend.exception_provide_object=1\n"; +ini_set("zend.exception_ignore_args", 0); +ini_set("zend.exception_provide_object", 1); + +try { + doThrow("foo", "bar"); +} catch (Exception $e) { + var_dump($e->getTrace()); +} + +echo "zend.exception_ignore_args=1\n"; +echo "zend.exception_provide_object=0\n"; +ini_set("zend.exception_ignore_args", 1); +ini_set("zend.exception_provide_object", 0); + +try { + doThrow("foo", "bar"); +} catch (Exception $e) { + var_dump($e->getTrace()); +} + +?> +--EXPECTF-- +zend.exception_ignore_args=0 +zend.exception_provide_object=1 +array(3) { + [0]=> + array(7) { + ["file"]=> + string(%d) "%sexception_provide_object.php" + ["line"]=> + int(%d) + ["function"]=> + string(7) "doThrow" + ["class"]=> + string(4) "Test" + ["object"]=> + object(Test)#%d (%d) { + ["random"]=> + int(%d) + } + ["type"]=> + string(2) "->" + ["args"]=> + array(2) { + [0]=> + string(3) "foo" + [1]=> + string(3) "bar" + } + } + [1]=> + array(7) { + ["file"]=> + string(%d) "%sexception_provide_object.php" + ["line"]=> + int(%d) + ["function"]=> + string(%d) "{closure:Test::funcDoThrow():%d}" + ["class"]=> + string(4) "Test" + ["object"]=> + object(Test)#%d (%d) { + ["random"]=> + int(%d) + } + ["type"]=> + string(2) "->" + ["args"]=> + array(2) { + [0]=> + string(3) "foo" + [1]=> + string(3) "bar" + } + } + [2]=> + array(4) { + ["file"]=> + string(%d) "%sexception_provide_object.php" + ["line"]=> + int(%d) + ["function"]=> + string(7) "doThrow" + ["args"]=> + array(2) { + [0]=> + string(3) "foo" + [1]=> + string(3) "bar" + } + } +} +zend.exception_ignore_args=1 +zend.exception_provide_object=0 +array(3) { + [0]=> + array(5) { + ["file"]=> + string(%d) "%sexception_provide_object.php" + ["line"]=> + int(%d) + ["function"]=> + string(7) "doThrow" + ["class"]=> + string(4) "Test" + ["type"]=> + string(2) "->" + } + [1]=> + array(5) { + ["file"]=> + string(%d) "%sexception_provide_object.php" + ["line"]=> + int(%d) + ["function"]=> + string(%d) "{closure:Test::funcDoThrow():%d}" + ["class"]=> + string(4) "Test" + ["type"]=> + string(2) "->" + } + [2]=> + array(3) { + ["file"]=> + string(%d) "%sexception_provide_object.php" + ["line"]=> + int(%d) + ["function"]=> + string(7) "doThrow" + } +} diff --git a/Zend/zend.c b/Zend/zend.c index 045d25134f8c9..2449e61dfd284 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -270,6 +270,7 @@ ZEND_INI_BEGIN() STD_ZEND_INI_BOOLEAN("zend.signal_check", SIGNAL_CHECK_DEFAULT, ZEND_INI_SYSTEM, OnUpdateBool, check, zend_signal_globals_t, zend_signal_globals) #endif STD_ZEND_INI_BOOLEAN("zend.exception_ignore_args", "0", ZEND_INI_ALL, OnUpdateBool, exception_ignore_args, zend_executor_globals, executor_globals) + STD_ZEND_INI_BOOLEAN("zend.exception_provide_object", "0", ZEND_INI_ALL, OnUpdateBool, exception_provide_object, zend_executor_globals, executor_globals) STD_ZEND_INI_ENTRY("zend.exception_string_param_max_len", "15", ZEND_INI_ALL, OnSetExceptionStringParamMaxLen, exception_string_param_max_len, zend_executor_globals, executor_globals) STD_ZEND_INI_ENTRY("fiber.stack_size", NULL, ZEND_INI_ALL, OnUpdateFiberStackSize, fiber_stack_size, zend_executor_globals, executor_globals) #ifdef ZEND_CHECK_STACK_LIMIT diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 0b0945aac0f44..a44804bdf854f 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -290,14 +290,19 @@ static zend_object *zend_default_exception_new(zend_class_entry *class_type) /* zval tmp; zval trace; zend_string *filename; + int options = 0; zend_object *object = zend_objects_new(class_type); object_properties_init(object, class_type); if (EG(current_execute_data)) { - zend_fetch_debug_backtrace(&trace, - 0, - EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0); + if (EG(exception_ignore_args)) { + options |= DEBUG_BACKTRACE_IGNORE_ARGS; + } + if (EG(exception_provide_object)) { + options |= DEBUG_BACKTRACE_PROVIDE_OBJECT; + } + zend_fetch_debug_backtrace(&trace, 0, options, 0); } else { ZVAL_EMPTY_ARRAY(&trace); } diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 48b978b535014..38e849a49240f 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -235,6 +235,7 @@ struct _zend_executor_globals { int user_error_handler_error_reporting; bool exception_ignore_args; + bool exception_provide_object; zval user_error_handler; zval user_exception_handler; zend_stack user_error_handlers_error_reporting; diff --git a/php.ini-development b/php.ini-development index 7018682fb0d9a..58c52b507d353 100644 --- a/php.ini-development +++ b/php.ini-development @@ -367,12 +367,24 @@ zend.enable_gc = On ; Allows to include or exclude arguments from stack traces generated for exceptions. ; In production, it is recommended to turn this setting on to prohibit the output -; of sensitive information in stack traces +; of sensitive information in stack traces. +; Note: This increases the refcount of the objects in arguments, and therefore delays +; object destruction until all references have been destroyed. ; Default Value: Off ; Development Value: Off ; Production Value: On zend.exception_ignore_args = Off +; Allows to include or exclude called object from stack traces generated for exceptions. +; In production, it is recommended to turn this setting off to prohibit the output +; of sensitive information in stack traces. +; Note: This increases the refcount of the objects, and therefore delays object +; destruction until all references have been destroyed. +; Default Value: Off +; Development Value: On +; Production Value: Off +zend.exception_provide_object = On + ; Allows setting the maximum string length in an argument of a stringified stack trace ; to a value between 0 and 1000000. ; This has no effect when zend.exception_ignore_args is enabled. diff --git a/php.ini-production b/php.ini-production index 602d005afd54f..2d7d6aa376b15 100644 --- a/php.ini-production +++ b/php.ini-production @@ -367,12 +367,24 @@ zend.enable_gc = On ; Allows to include or exclude arguments from stack traces generated for exceptions. ; In production, it is recommended to turn this setting on to prohibit the output -; of sensitive information in stack traces +; of sensitive information in stack traces. +; Note: This increases the refcount of the objects in arguments, and therefore delays +; object destruction until all references have been destroyed. ; Default Value: Off ; Development Value: Off ; Production Value: On zend.exception_ignore_args = On +; Allows to include or exclude called object from stack traces generated for exceptions. +; In production, it is recommended to turn this setting off to prohibit the output +; of sensitive information in stack traces. +; Note: This increases the refcount of the objects, and therefore delays object +; destruction until all references have been destroyed. +; Default Value: Off +; Development Value: On +; Production Value: Off +zend.exception_provide_object = Off + ; Allows setting the maximum string length in an argument of a stringified stack trace ; to a value between 0 and 1000000. ; This has no effect when zend.exception_ignore_args is enabled.