From 5c329f7163cc07130b3d0d487cb52e771d7482e0 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 4 Feb 2025 12:43:33 +0100 Subject: [PATCH 1/4] Add get_error_handler(), get_exception_handler() functions --- Zend/tests/get_error_handler.phpt | 68 +++++++++++++++++++++++++++ Zend/tests/get_exception_handler.phpt | 68 +++++++++++++++++++++++++++ Zend/zend_builtin_functions.c | 18 +++++++ Zend/zend_builtin_functions.stub.php | 4 ++ Zend/zend_builtin_functions_arginfo.h | 11 ++++- 5 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/get_error_handler.phpt create mode 100644 Zend/tests/get_exception_handler.phpt diff --git a/Zend/tests/get_error_handler.phpt b/Zend/tests/get_error_handler.phpt new file mode 100644 index 0000000000000..624774683b78a --- /dev/null +++ b/Zend/tests/get_error_handler.phpt @@ -0,0 +1,68 @@ +--TEST-- +get_error_handler() +--FILE-- +handle(...)); +var_dump(get_error_handler()); + +set_error_handler(function () {}); +var_dump(get_error_handler()); + +?> +==DONE== +--EXPECTF-- +string(3) "foo" +NULL +array(2) { + [0]=> + string(1) "C" + [1]=> + string(12) "handleStatic" +} +string(15) "C::handleStatic" +array(2) { + [0]=> + object(C)#%d (0) { + } + [1]=> + string(6) "handle" +} +object(Closure)#%d (2) { + ["function"]=> + string(9) "C::handle" + ["this"]=> + object(C)#%d (0) { + } +} +object(Closure)#%d (3) { + ["name"]=> + string(%d) "{closure:%s:%d}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(%d) +} +==DONE== diff --git a/Zend/tests/get_exception_handler.phpt b/Zend/tests/get_exception_handler.phpt new file mode 100644 index 0000000000000..570af4e1d1cf5 --- /dev/null +++ b/Zend/tests/get_exception_handler.phpt @@ -0,0 +1,68 @@ +--TEST-- +get_exception_handler() +--FILE-- +handle(...)); +var_dump(get_exception_handler()); + +set_exception_handler(function () {}); +var_dump(get_exception_handler()); + +?> +==DONE== +--EXPECTF-- +string(3) "foo" +NULL +array(2) { + [0]=> + string(1) "C" + [1]=> + string(12) "handleStatic" +} +string(15) "C::handleStatic" +array(2) { + [0]=> + object(C)#%d (0) { + } + [1]=> + string(6) "handle" +} +object(Closure)#%d (2) { + ["function"]=> + string(9) "C::handle" + ["this"]=> + object(C)#%d (0) { + } +} +object(Closure)#%d (3) { + ["name"]=> + string(%d) "{closure:%s:%d}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(%d) +} +==DONE== diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index bb8bb28bf6e4f..8244c57a5f976 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1323,6 +1323,15 @@ ZEND_FUNCTION(restore_error_handler) } /* }}} */ +ZEND_FUNCTION(get_error_handler) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { + RETURN_ZVAL(&EG(user_error_handler), 1, 0); + } +} + /* {{{ Sets a user-defined exception handler function. Returns the previously defined exception handler, or false on error */ ZEND_FUNCTION(set_exception_handler) { @@ -1369,6 +1378,15 @@ ZEND_FUNCTION(restore_exception_handler) } /* }}} */ +ZEND_FUNCTION(get_exception_handler) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { + RETURN_ZVAL(&EG(user_exception_handler), 1, 0); + } +} + static inline void get_declared_class_impl(INTERNAL_FUNCTION_PARAMETERS, int flags) /* {{{ */ { zend_string *key; diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php index f7009c4ffba6e..43935ffb89a85 100644 --- a/Zend/zend_builtin_functions.stub.php +++ b/Zend/zend_builtin_functions.stub.php @@ -117,11 +117,15 @@ function set_error_handler(?callable $callback, int $error_levels = E_ALL) {} function restore_error_handler(): true {} +function get_error_handler(): mixed {} + /** @return callable|null */ function set_exception_handler(?callable $callback) {} function restore_exception_handler(): true {} +function get_exception_handler(): mixed {} + /** * @return array * @refcount 1 diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index cf34b1c0012d5..da8b51cea241c 100644 --- a/Zend/zend_builtin_functions_arginfo.h +++ b/Zend/zend_builtin_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3dbc84896823c9aaa9ac8aeef8841266920c3e50 */ + * Stub hash: f9a6466d527e6abcd48cc36fb6a5d8434f3eb7bb */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_exit, 0, 0, IS_NEVER, 0) ZEND_ARG_TYPE_MASK(0, status, MAY_BE_STRING|MAY_BE_LONG, "0") @@ -148,12 +148,17 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_restore_error_handler, 0, 0, IS_TRUE, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_get_error_handler, 0, 0, IS_MIXED, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_set_exception_handler, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 1) ZEND_END_ARG_INFO() #define arginfo_restore_exception_handler arginfo_restore_error_handler +#define arginfo_get_exception_handler arginfo_get_error_handler + #define arginfo_get_declared_classes arginfo_func_get_args #define arginfo_get_declared_traits arginfo_func_get_args @@ -272,8 +277,10 @@ ZEND_FUNCTION(get_included_files); ZEND_FUNCTION(trigger_error); ZEND_FUNCTION(set_error_handler); ZEND_FUNCTION(restore_error_handler); +ZEND_FUNCTION(get_error_handler); ZEND_FUNCTION(set_exception_handler); ZEND_FUNCTION(restore_exception_handler); +ZEND_FUNCTION(get_exception_handler); ZEND_FUNCTION(get_declared_classes); ZEND_FUNCTION(get_declared_traits); ZEND_FUNCTION(get_declared_interfaces); @@ -336,8 +343,10 @@ static const zend_function_entry ext_functions[] = { ZEND_RAW_FENTRY("user_error", zif_trigger_error, arginfo_user_error, 0, NULL, NULL) ZEND_FE(set_error_handler, arginfo_set_error_handler) ZEND_FE(restore_error_handler, arginfo_restore_error_handler) + ZEND_FE(get_error_handler, arginfo_get_error_handler) ZEND_FE(set_exception_handler, arginfo_set_exception_handler) ZEND_FE(restore_exception_handler, arginfo_restore_exception_handler) + ZEND_FE(get_exception_handler, arginfo_get_exception_handler) ZEND_FE(get_declared_classes, arginfo_get_declared_classes) ZEND_FE(get_declared_traits, arginfo_get_declared_traits) ZEND_FE(get_declared_interfaces, arginfo_get_declared_interfaces) From 8d861b22234a73ae73e16ac2ba0b04eace318cea Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 4 Feb 2025 20:14:20 +0100 Subject: [PATCH 2/4] Use ?callable return type --- Zend/zend_builtin_functions.stub.php | 4 ++-- Zend/zend_builtin_functions_arginfo.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php index 43935ffb89a85..7f316835aea6b 100644 --- a/Zend/zend_builtin_functions.stub.php +++ b/Zend/zend_builtin_functions.stub.php @@ -117,14 +117,14 @@ function set_error_handler(?callable $callback, int $error_levels = E_ALL) {} function restore_error_handler(): true {} -function get_error_handler(): mixed {} +function get_error_handler(): ?callable {} /** @return callable|null */ function set_exception_handler(?callable $callback) {} function restore_exception_handler(): true {} -function get_exception_handler(): mixed {} +function get_exception_handler(): ?callable {} /** * @return array diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index da8b51cea241c..9498b8292f892 100644 --- a/Zend/zend_builtin_functions_arginfo.h +++ b/Zend/zend_builtin_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f9a6466d527e6abcd48cc36fb6a5d8434f3eb7bb */ + * Stub hash: a24761186f1ddf758e648b0a764826537cbd33b9 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_exit, 0, 0, IS_NEVER, 0) ZEND_ARG_TYPE_MASK(0, status, MAY_BE_STRING|MAY_BE_LONG, "0") @@ -148,7 +148,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_restore_error_handler, 0, 0, IS_TRUE, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_get_error_handler, 0, 0, IS_MIXED, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_get_error_handler, 0, 0, IS_CALLABLE, 1) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_set_exception_handler, 0, 0, 1) From 78c5bac57477309f2b6987b4554dcfb03cba3702 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 21 Mar 2025 17:38:17 +0100 Subject: [PATCH 3/4] Use RETURN_COPY() --- Zend/zend_builtin_functions.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 8244c57a5f976..212e0c382fb60 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1328,7 +1328,7 @@ ZEND_FUNCTION(get_error_handler) ZEND_PARSE_PARAMETERS_NONE(); if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { - RETURN_ZVAL(&EG(user_error_handler), 1, 0); + RETURN_COPY(&EG(user_error_handler)); } } @@ -1383,7 +1383,7 @@ ZEND_FUNCTION(get_exception_handler) ZEND_PARSE_PARAMETERS_NONE(); if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { - RETURN_ZVAL(&EG(user_exception_handler), 1, 0); + RETURN_COPY(&EG(user_exception_handler)); } } From cb6666baac8a6fbe3a5b3b2b2998b098a9e19c64 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 25 Mar 2025 10:50:50 +0100 Subject: [PATCH 4/4] Improve tests --- Zend/tests/get_error_handler.phpt | 104 +++++++++++++++---------- Zend/tests/get_exception_handler.phpt | 107 +++++++++++++++----------- 2 files changed, 125 insertions(+), 86 deletions(-) diff --git a/Zend/tests/get_error_handler.phpt b/Zend/tests/get_error_handler.phpt index 624774683b78a..abe0b608a4cf9 100644 --- a/Zend/tests/get_error_handler.phpt +++ b/Zend/tests/get_error_handler.phpt @@ -8,61 +8,81 @@ class C { static function handleStatic() {} } +class Invokable { + public function __invoke() { + } +} + function foo() {} -set_error_handler("foo"); -var_dump(get_error_handler()); +echo "No error handler\n"; +var_dump(get_error_handler() === null); + +echo "\nFunction string\n"; +set_error_handler('foo'); +var_dump(get_error_handler() === 'foo'); +echo "\nNULL\n"; set_error_handler(null); -var_dump(get_error_handler()); +var_dump(get_error_handler() === null); +echo "\nStatic method array\n"; set_error_handler([C::class, 'handleStatic']); -var_dump(get_error_handler()); +var_dump(get_error_handler() === [C::class, 'handleStatic']); +echo "\nStatic method string\n"; set_error_handler('C::handleStatic'); -var_dump(get_error_handler()); +var_dump(get_error_handler() === 'C::handleStatic'); + +echo "\nInstance method array\n"; +set_error_handler([$c = new C(), 'handle']); +var_dump(get_error_handler() === [$c, 'handle']); + +echo "\nFirst class callable method\n"; +set_error_handler($f = (new C())->handle(...)); +var_dump(get_error_handler() === $f); -set_error_handler([new C(), 'handle']); -var_dump(get_error_handler()); +echo "\nClosure\n"; +set_error_handler($f = function () {}); +var_dump(get_error_handler() === $f); -set_error_handler((new C())->handle(...)); -var_dump(get_error_handler()); +echo "\nInvokable\n"; +set_error_handler($object = new Invokable()); +var_dump(get_error_handler() === $object); -set_error_handler(function () {}); -var_dump(get_error_handler()); +echo "\nStable return value\n"; +var_dump(get_error_handler() === get_error_handler()); ?> ==DONE== ---EXPECTF-- -string(3) "foo" +--EXPECT-- +No error handler +bool(true) + +Function string +bool(true) + NULL -array(2) { - [0]=> - string(1) "C" - [1]=> - string(12) "handleStatic" -} -string(15) "C::handleStatic" -array(2) { - [0]=> - object(C)#%d (0) { - } - [1]=> - string(6) "handle" -} -object(Closure)#%d (2) { - ["function"]=> - string(9) "C::handle" - ["this"]=> - object(C)#%d (0) { - } -} -object(Closure)#%d (3) { - ["name"]=> - string(%d) "{closure:%s:%d}" - ["file"]=> - string(%d) "%s" - ["line"]=> - int(%d) -} +bool(true) + +Static method array +bool(true) + +Static method string +bool(true) + +Instance method array +bool(true) + +First class callable method +bool(true) + +Closure +bool(true) + +Invokable +bool(true) + +Stable return value +bool(true) ==DONE== diff --git a/Zend/tests/get_exception_handler.phpt b/Zend/tests/get_exception_handler.phpt index 570af4e1d1cf5..05f43db2c0f4c 100644 --- a/Zend/tests/get_exception_handler.phpt +++ b/Zend/tests/get_exception_handler.phpt @@ -8,61 +8,80 @@ class C { static function handleStatic() {} } +class Invokable { + public function __invoke() { + } +} + function foo() {} -set_exception_handler("foo"); -var_dump(get_exception_handler()); +echo "No exception handler\n"; +var_dump(get_exception_handler() === null); + +echo "\nFunction string\n"; +set_exception_handler('foo'); +var_dump(get_exception_handler() === 'foo'); +echo "\nNULL\n"; set_exception_handler(null); -var_dump(get_exception_handler()); +var_dump(get_exception_handler() === null); +echo "\nStatic method array\n"; set_exception_handler([C::class, 'handleStatic']); -var_dump(get_exception_handler()); +var_dump(get_exception_handler() === [C::class, 'handleStatic']); +echo "\nStatic method string\n"; set_exception_handler('C::handleStatic'); -var_dump(get_exception_handler()); +var_dump(get_exception_handler() === 'C::handleStatic'); -set_exception_handler([new C(), 'handle']); -var_dump(get_exception_handler()); +echo "\nInstance method array\n"; +set_exception_handler([$c = new C(), 'handle']); +var_dump(get_exception_handler() === [$c, 'handle']); -set_exception_handler((new C())->handle(...)); -var_dump(get_exception_handler()); +echo "\nFirst class callable method\n"; +set_exception_handler($f = (new C())->handle(...)); +var_dump(get_exception_handler() === $f); -set_exception_handler(function () {}); -var_dump(get_exception_handler()); +echo "\nClosure\n"; +set_exception_handler($f = function () {}); +var_dump(get_exception_handler() === $f); + +echo "\nInvokable\n"; +set_exception_handler($object = new Invokable()); +var_dump(get_exception_handler() === $object); + +echo "\nStable return value\n"; +var_dump(get_exception_handler() === get_exception_handler()); + +?>==DONE== +--EXPECT-- +No exception handler +bool(true) + +Function string +bool(true) -?> -==DONE== ---EXPECTF-- -string(3) "foo" NULL -array(2) { - [0]=> - string(1) "C" - [1]=> - string(12) "handleStatic" -} -string(15) "C::handleStatic" -array(2) { - [0]=> - object(C)#%d (0) { - } - [1]=> - string(6) "handle" -} -object(Closure)#%d (2) { - ["function"]=> - string(9) "C::handle" - ["this"]=> - object(C)#%d (0) { - } -} -object(Closure)#%d (3) { - ["name"]=> - string(%d) "{closure:%s:%d}" - ["file"]=> - string(%d) "%s" - ["line"]=> - int(%d) -} +bool(true) + +Static method array +bool(true) + +Static method string +bool(true) + +Instance method array +bool(true) + +First class callable method +bool(true) + +Closure +bool(true) + +Invokable +bool(true) + +Stable return value +bool(true) ==DONE==