diff --git a/NEWS b/NEWS index 946e17baee568..9e94e3a00b4e0 100644 --- a/NEWS +++ b/NEWS @@ -58,6 +58,8 @@ PHP NEWS . The socket_set_timeout() alias function has been deprecated. (timwolla) . Passing null to to readdir(), rewinddir(), and closedir() to use the last opened directory has been deprecated. (Girgias) + . Added the exit_status() function to retrieve the current exit + status. (alexandre-daubois) 31 Jul 2025, PHP 8.5.0alpha4 diff --git a/UPGRADING b/UPGRADING index 3a2a63795eef0..f52ce6dc162d4 100644 --- a/UPGRADING +++ b/UPGRADING @@ -574,6 +574,7 @@ PHP 8.5 UPGRADE NOTES - Standard: . Added array_first() and array_last(). RFC: https://wiki.php.net/rfc/array_first_last + . Added exit_status(). ======================================== 7. New Classes and Interfaces diff --git a/Zend/tests/exit_status/exit_status_basic.phpt b/Zend/tests/exit_status/exit_status_basic.phpt new file mode 100644 index 0000000000000..1124854886b4a --- /dev/null +++ b/Zend/tests/exit_status/exit_status_basic.phpt @@ -0,0 +1,12 @@ +--TEST-- +exit_status() basic functionality test +--FILE-- + +--EXPECT-- +int(0) +bool(true) +bool(true) diff --git a/Zend/tests/exit_status/exit_status_error.phpt b/Zend/tests/exit_status/exit_status_error.phpt new file mode 100644 index 0000000000000..cf59a03d4291e --- /dev/null +++ b/Zend/tests/exit_status/exit_status_error.phpt @@ -0,0 +1,21 @@ +--TEST-- +exit_status() error with parameters +--FILE-- + +--EXPECT-- +PASS: Caught expected ArgumentCountError +PASS: Caught expected ArgumentCountError for multiple params diff --git a/Zend/tests/exit_status/exit_status_exception.phpt b/Zend/tests/exit_status/exit_status_exception.phpt new file mode 100644 index 0000000000000..edabd0bf3c445 --- /dev/null +++ b/Zend/tests/exit_status/exit_status_exception.phpt @@ -0,0 +1,17 @@ +--TEST-- +exit_status() with uncaught exceptions setting exit code +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception: Test exception in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d +Exit status is: 255 diff --git a/Zend/tests/exit_status/exit_status_shutdown.phpt b/Zend/tests/exit_status/exit_status_shutdown.phpt new file mode 100644 index 0000000000000..e5002ac85f0a4 --- /dev/null +++ b/Zend/tests/exit_status/exit_status_shutdown.phpt @@ -0,0 +1,20 @@ +--TEST-- +exit_status() in shutdown handler preserving original exit code +--FILE-- + +--EXPECT-- +Setting exit status to 42 +Current exit status: 42 +Final status will be: 42 diff --git a/Zend/tests/exit_status/exit_status_string.phpt b/Zend/tests/exit_status/exit_status_string.phpt new file mode 100644 index 0000000000000..68e0437695f2e --- /dev/null +++ b/Zend/tests/exit_status/exit_status_string.phpt @@ -0,0 +1,14 @@ +--TEST-- +exit_status() with string exit codes +--FILE-- + +--EXPECT-- +Before exit with string +Error messageExit status: 0 diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index b25925b89f7f9..ab878b9567649 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -501,6 +501,13 @@ ZEND_FUNCTION(error_reporting) } /* }}} */ +ZEND_FUNCTION(exit_status) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + RETURN_LONG(EG(exit_status)); +} + static bool validate_constant_array_argument(HashTable *ht, int argument_number) /* {{{ */ { bool ret = 1; diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php index 9b2267b531eb2..68362677f4bfd 100644 --- a/Zend/zend_builtin_functions.stub.php +++ b/Zend/zend_builtin_functions.stub.php @@ -41,6 +41,8 @@ function strncasecmp(string $string1, string $string2, int $length): int {} function error_reporting(?int $error_level = null): int {} +function exit_status(): int {} + function define(string $constant_name, mixed $value, bool $case_insensitive = false): bool {} function defined(string $constant_name): bool {} diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index cf349b551ac21..58cd4f711ab4c 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: 9b49f527064695c812cd204d9efc63c13681d942 */ + * Stub hash: 754be326aa54bdac769a16875276cf0161491718 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_clone, 0, 1, IS_OBJECT, 0) ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) @@ -48,6 +48,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_error_reporting, 0, 0, IS_LONG, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, error_level, IS_LONG, 1, "null") ZEND_END_ARG_INFO() +#define arginfo_exit_status arginfo_func_num_args + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_define, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, constant_name, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) @@ -260,6 +262,7 @@ ZEND_FUNCTION(strncmp); ZEND_FUNCTION(strcasecmp); ZEND_FUNCTION(strncasecmp); ZEND_FUNCTION(error_reporting); +ZEND_FUNCTION(exit_status); ZEND_FUNCTION(define); ZEND_FUNCTION(defined); ZEND_FUNCTION(get_class); @@ -325,6 +328,7 @@ static const zend_function_entry ext_functions[] = { ZEND_RAW_FENTRY("strcasecmp", zif_strcasecmp, arginfo_strcasecmp, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("strncasecmp", zif_strncasecmp, arginfo_strncasecmp, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_FE(error_reporting, arginfo_error_reporting) + ZEND_FE(exit_status, arginfo_exit_status) ZEND_FE(define, arginfo_define) ZEND_FE(defined, arginfo_defined) ZEND_FE(get_class, arginfo_get_class)