From b092f3fff1fe6c53cdc684ea59fed77a3c8b4564 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Fri, 25 Sep 2026 10:34:27 +0200 Subject: [PATCH 01/10] Require Autoconf 2.71 or newer (#21159) This updates the minimum required Autoconf version to 2.71. - Autoconf versions 2.70 started supporting C11 standards. - Autoconf 2.71 was released soon after 2.70 providing some bugfixes. Changes: - Removed obsolete AC_PROG_CC_C99 macro. In Autoconf 2.70 and later this is done by the AC_PROG_CC macro. - m4_normalize is not needed for AC_CHECK_HEADERS and AC_CHECK_FUNCTIONS macros anymore, as the argument is normalized internally by Autoconf in these newer versions. - Adjusted C11 check in configure.ac and added check also in phpize mode. Extensions using PHP headers should be also built with some C11-compliant compiler. - Removed ac_cv_header_sys_types_h_makedev hack for AC_HEADER_MAJOR macro when using Autoconf versions prior to 2.70. - Replaced AC_CONFIG_MACRO_DIR with preferred AC_CONFIG_MACRO_DIRS. This also enables using --runstatedir configure option in the future. --- UPGRADING.INTERNALS | 3 +++ Zend/Zend.m4 | 8 ++++---- configure.ac | 32 +++++++++++--------------------- ext/ldap/config.m4 | 4 ++-- ext/pcntl/config.m4 | 4 ++-- ext/posix/config.m4 | 4 ++-- scripts/phpize.m4 | 7 ++++++- 7 files changed, 30 insertions(+), 32 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index b2aa21986efb..57e1ab63be8a 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -21,6 +21,9 @@ PHP 8.7 INTERNALS UPGRADE NOTES 2. Build system changes ======================== +- Unix build system changes: + . Autoconf minimum required version upgraded to 2.71. + ======================== 3. Module changes ======================== diff --git a/Zend/Zend.m4 b/Zend/Zend.m4 index 1f4e5e26f00e..377cf40b12bd 100644 --- a/Zend/Zend.m4 +++ b/Zend/Zend.m4 @@ -133,13 +133,13 @@ dnl AC_DEFUN([ZEND_INIT], [dnl AC_REQUIRE([AC_PROG_CC]) -AC_CHECK_HEADERS(m4_normalize([ +AC_CHECK_HEADERS([ cpuid.h libproc.h -])) +]) dnl Check for library functions. -AC_CHECK_FUNCS(m4_normalize([ +AC_CHECK_FUNCS([ getpid gettid kill @@ -151,7 +151,7 @@ AC_CHECK_FUNCS(m4_normalize([ pthread_getthrds_np pthread_stackseg_np strnlen -])) +]) AC_CHECK_DECL([clock_gettime_nsec_np], [AC_DEFINE([HAVE_CLOCK_GETTIME_NSEC_NP], [1], diff --git a/configure.ac b/configure.ac index 5a62f91848f2..50df06a6b479 100644 --- a/configure.ac +++ b/configure.ac @@ -22,10 +22,10 @@ m4_include([Zend/Zend.m4]) dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- -AC_PREREQ([2.68]) +AC_PREREQ([2.71]) AC_INIT([PHP],[8.7.0-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) -AC_CONFIG_MACRO_DIR([build]) +AC_CONFIG_MACRO_DIRS([build]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER @@ -120,11 +120,14 @@ dnl ---------------------------------------------------------------------------- PKG_PROG_PKG_CONFIG AC_PROG_CC([cc gcc]) + +dnl Check if C compiler accepts C11. +AS_CASE([$ac_prog_cc_stdc], [c99|c89|no], + [AC_MSG_ERROR([C compiler would not accept C11 code.])]) + PHP_DETECT_ICC PHP_DETECT_SUNCC -dnl AC_PROG_CC_C99 is obsolete with autoconf >= 2.70 yet necessary for <= 2.69. -m4_version_prereq([2.70],,[AC_PROG_CC_C99]) AC_PROG_CPP AC_USE_SYSTEM_EXTENSIONS AC_PROG_LN_S @@ -135,17 +138,6 @@ AS_VAR_IF([cross_compiling], [yes], AC_MSG_RESULT([$BUILD_CC])], [BUILD_CC=$CC]) -dnl The macro AC_PROG_CC_C99 sets the shell variable ac_cv_prog_cc_c99 to 'no' -dnl if the compiler does not support C99.i.e. does not support any of _Bool, -dnl flexible arrays, inline, long long int, mixed code and declarations, -dnl named initialization of structs, restrict, varargs macros, variable -dnl declarations in for loops and variable length arrays. -dnl -dnl https://www.gnu.org/software/autoconf/manual/autoconf-2.60/html_node/C-Compiler.html -if test "$ac_cv_prog_cc_c99" = no; then - AC_MSG_ERROR([C compiler would not accept C99 code]) -fi - dnl Support systems with system libraries in e.g. /usr/lib64. PHP_ARG_WITH([libdir], [for system library directory], @@ -380,7 +372,7 @@ dnl Then headers. dnl ---------------------------------------------------------------------------- dnl QNX requires unix.h to allow functions in libunix to work properly. -AC_CHECK_HEADERS(m4_normalize([ +AC_CHECK_HEADERS([ dirent.h sys/param.h sys/types.h @@ -428,7 +420,7 @@ AC_CHECK_HEADERS(m4_normalize([ nmmintrin.h wmmintrin.h immintrin.h -]),,, [dnl +],,, [dnl #ifdef HAVE_SYS_PARAM_H #include #endif @@ -452,8 +444,6 @@ PHP_BROKEN_GETCWD AS_VAR_IF([GCC], [yes], [PHP_BROKEN_GCC_STRLEN_OPT]) dnl Detect the headers required to use makedev, major, and minor. -dnl Autoconf <= 2.69 didn't check glibc 2.25 deprecated macros in sys/types.h. -m4_version_prereq([2.70],,[ac_cv_header_sys_types_h_makedev=no]) AC_HEADER_MAJOR dnl Checks for typedefs, structures, and compiler characteristics. @@ -546,7 +536,7 @@ PHP_CHECK_VARIABLE_ATTRIBUTE([aligned]) dnl Checks for library functions. dnl ---------------------------------------------------------------------------- -AC_CHECK_FUNCS(m4_normalize([ +AC_CHECK_FUNCS([ alphasort asctime_r asprintf @@ -609,7 +599,7 @@ AC_CHECK_FUNCS(m4_normalize([ usleep utime vasprintf -])) +]) PHP_ARG_ENABLE([system-glob], [whether to use the system glob function], diff --git a/ext/ldap/config.m4 b/ext/ldap/config.m4 index 65a3d3b90eed..fd56d2d0c361 100644 --- a/ext/ldap/config.m4 +++ b/ext/ldap/config.m4 @@ -132,7 +132,7 @@ if test "$PHP_LDAP" != "no"; then dnl Solaris 2.8 claims to be 2004 API, but doesn't have ldap_parse_reference() dnl nor ldap_start_tls_s() - AC_CHECK_FUNCS(m4_normalize([ + AC_CHECK_FUNCS([ ldap_control_find ldap_extended_operation_s ldap_parse_extended_result @@ -142,7 +142,7 @@ if test "$PHP_LDAP" != "no"; then ldap_refresh_s ldap_start_tls_s ldap_whoami_s - ])) + ]) dnl Sanity check AC_CHECK_FUNC([ldap_sasl_bind_s],, diff --git a/ext/pcntl/config.m4 b/ext/pcntl/config.m4 index 553419114fd2..d0fffb45037e 100644 --- a/ext/pcntl/config.m4 +++ b/ext/pcntl/config.m4 @@ -9,7 +9,7 @@ if test "$PHP_PCNTL" != "no"; then [AC_MSG_FAILURE([ext/pcntl: required function $function() not found.])]) done - AC_CHECK_FUNCS(m4_normalize([ + AC_CHECK_FUNCS([ forkx getcpuid getpriority @@ -26,7 +26,7 @@ if test "$PHP_PCNTL" != "no"; then waitid wait6 syscall - ])) + ]) AC_CHECK_FUNCS([WIFCONTINUED],, [AC_CHECK_DECL([WIFCONTINUED], [AC_DEFINE([HAVE_WIFCONTINUED], [1])],, diff --git a/ext/posix/config.m4 b/ext/posix/config.m4 index 865546356953..b0f6b1879593 100644 --- a/ext/posix/config.m4 +++ b/ext/posix/config.m4 @@ -12,7 +12,7 @@ if test "$PHP_POSIX" = "yes"; then [$ext_shared],, [-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) - AC_CHECK_FUNCS(m4_normalize([ + AC_CHECK_FUNCS([ ctermid eaccess getgrgid_r @@ -28,7 +28,7 @@ if test "$PHP_POSIX" = "yes"; then seteuid setrlimit setsid - ])) + ]) dnl Check for makedev. If it's defined as a macro, AC_CHECK_FUNCS won't work. dnl Required headers are included by the AC_HEADER_MAJOR logic. diff --git a/scripts/phpize.m4 b/scripts/phpize.m4 index 6bfad3846596..21b5a856344d 100644 --- a/scripts/phpize.m4 +++ b/scripts/phpize.m4 @@ -15,7 +15,7 @@ m4_include([build/php_cxx_compile_stdcxx.m4]) m4_include([build/php.m4]) m4_include([build/pkg.m4]) -AC_PREREQ([2.68]) +AC_PREREQ([2.71]) AC_INIT AC_CONFIG_SRCDIR([config.m4]) AC_CONFIG_AUX_DIR([build]) @@ -36,6 +36,11 @@ PHP_INIT_BUILD_SYSTEM PKG_PROG_PKG_CONFIG AC_PROG_CC([cc gcc]) + +dnl Check if C compiler accepts C11. +AS_CASE([$ac_prog_cc_stdc], [c99|c89|no], + [AC_MSG_ERROR([C compiler would not accept C11 code.])]) + PHP_DETECT_ICC PHP_DETECT_SUNCC From 5244ba77487b749162728924e569c5567475b93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 25 Sep 2026 10:40:10 +0200 Subject: [PATCH 02/10] zend_portability: Simplify check for `max_align_t` (#21229) C11 / C++11 are the baseline requirements, thus we only need to check for Windows. --- Zend/zend_portability.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index ccad24682fdb..2e16a3683644 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -870,8 +870,7 @@ extern "C++" { # define ZEND_STATIC_ASSERT(c, m) #endif -#if ((defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) /* C11 */ \ - || (defined(__cplusplus) && __cplusplus >= 201103L) /* C++11 */) && !defined(ZEND_WIN32) +#if !defined(ZEND_WIN32) typedef max_align_t zend_max_align_t; #else typedef union { From b5526aa2fe0db6fbff55270e38aff245bff4a9ca Mon Sep 17 00:00:00 2001 From: ndossche <7771979+ndossche@users.noreply.github.com> Date: Thu, 24 Sep 2026 20:27:27 +0200 Subject: [PATCH 03/10] Fix OSS-Fuzz #565486253: coerced arg with '...' on non-variadic function The forwarding call is built 2x, and if the first time coerces an argument, you have a failed assertion the second time. So the type check (which does the coercion) has to happen on a value copy. Closes GH-23895. --- NEWS | 2 ++ .../const_arg_opt_004.phpt | 35 +++++++++++++++++++ Zend/zend_partial.c | 11 +++--- 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 Zend/tests/partial_application/const_arg_opt_004.phpt diff --git a/NEWS b/NEWS index a23e22ab27f7..5e233c1b8182 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,8 @@ PHP NEWS proxy objects instead of forwarding to the real instance). (lisachenko) . Fixed bug GH-23882 (array_map() optimization is incorrect for strict_types=1). (timwolla) + . Fixed OSS-Fuzz #565486253 (coerced arg with '...' on non-variadic + function). (ndossche) - Opcache: . Fix zend_analyze_calls() call_stack buffer overrun. (Mrmaxmeier) diff --git a/Zend/tests/partial_application/const_arg_opt_004.phpt b/Zend/tests/partial_application/const_arg_opt_004.phpt new file mode 100644 index 000000000000..f80c14dc6abd --- /dev/null +++ b/Zend/tests/partial_application/const_arg_opt_004.phpt @@ -0,0 +1,35 @@ +--TEST-- +Constant argument optimization - coerced arg with '...' on non-variadic function +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.file_update_protection=0 +--FILE-- + +--EXPECT-- +string(2) "15" +array(2) { + [0]=> + string(2) "15" + [1]=> + int(1) +} +string(2) "15" +array(3) { + [0]=> + string(2) "15" + [1]=> + int(2) + [2]=> + int(3) +} diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index 497e9b80d576..83bd67bccb8c 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -566,7 +566,10 @@ static zend_ast *zp_compile_forwarding_call( ZEND_ASSERT(!Z_REFCOUNTED(argv[offset])); /* This argument never changes, so we can burn it into the op_array - * and check its type ahead of time. */ + * and check its type ahead of time. + * Work with a value copy because a scalar type check may coerce this value. */ + zval value; + ZVAL_COPY_VALUE(&value, &argv[offset]); zend_arg_info *arg_info; if (offset < function->common.num_args) { @@ -577,18 +580,18 @@ static zend_ast *zp_compile_forwarding_call( arg_info = NULL; } if (arg_info && ZEND_TYPE_IS_SET(arg_info->type) - && UNEXPECTED(!zend_check_type_ex(&arg_info->type, &argv[offset], + && UNEXPECTED(!zend_check_type_ex(&arg_info->type, &value, /* current_frame */ true, /* is_internal */ false))) { zend_string *need_msg = zend_type_to_string_resolved(arg_info->type, function->common.scope); zend_argument_type_error_ex(function, offset + 1, "must be of type %s, %s given", - ZSTR_VAL(need_msg), zend_zval_value_name(&argv[offset])); + ZSTR_VAL(need_msg), zend_zval_value_name(&value)); zend_string_release(need_msg); goto error; } - args_ast = zend_ast_list_add(args_ast, zend_ast_create_zval(&argv[offset])); + args_ast = zend_ast_list_add(args_ast, zend_ast_create_zval(&value)); } else { args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_VAR, zend_ast_create_zval_from_str(zend_string_copy(var_names->params[offset])))); From c5e45a17674bb2b34e308a2ecfff6e8462a601c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 25 Sep 2026 12:21:01 +0200 Subject: [PATCH 04/10] tree-wide: Replace `ZEND_STATIC_ASSERT()` by `static_assert()` (#21228) * tree-wide: Replace `ZEND_STATIC_ASSERT()` by `static_assert()` * zend_portability: Remove `ZEND_STATIC_ASSERT()` --- UPGRADING.INTERNALS | 1 + Zend/zend_compile.c | 2 +- Zend/zend_compile.h | 2 +- Zend/zend_execute.h | 2 +- Zend/zend_portability.h | 8 -------- ext/date/time_duration.c | 4 ++-- ext/random/engine_mt19937.c | 2 +- ext/random/php_random_zend_utils.h | 2 +- 8 files changed, 8 insertions(+), 15 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 57e1ab63be8a..4b5eed39a7b5 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -16,6 +16,7 @@ PHP 8.7 INTERNALS UPGRADE NOTES - Removed zend_execute_scripts(). Manually call zend_execute_script() in a loop instead. +- Removed ZEND_STATIC_ASSERT(). Use C11 static_assert() directly instead. ======================== 2. Build system changes diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index bad60a5f1071..8d65f1e91394 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5944,7 +5944,7 @@ static void zend_compile_static_var_common(zend_string *var_name, zval *value, u opline->op1_type = IS_CV; opline->op1.var = lookup_cv(var_name); - ZEND_STATIC_ASSERT(sizeof(Bucket) % 8 == 0, "Bucket size not compatible with storing flags in lower three bits"); + static_assert(sizeof(Bucket) % 8 == 0, "Bucket size not compatible with storing flags in lower three bits"); opline->extended_value = (uint32_t)((char*)value - (char*)CG(active_op_array)->static_variables->arData) | mode; } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 6502770a2662..ec6d8b09bbe7 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -713,7 +713,7 @@ struct _zend_execute_data { (call)->This.u2.num_args /* Ensure the correct alignment before slots calculation */ -ZEND_STATIC_ASSERT(ZEND_MM_ALIGNED_SIZE(sizeof(zval)) == sizeof(zval), +static_assert(ZEND_MM_ALIGNED_SIZE(sizeof(zval)) == sizeof(zval), "zval must be aligned by ZEND_MM_ALIGNMENT"); /* A number of call frame slots (zvals) reserved for zend_execute_data. */ #define ZEND_CALL_FRAME_SLOT \ diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 2250a873af29..f7679d920d21 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -295,7 +295,7 @@ struct _zend_vm_stack { }; /* Ensure the correct alignment before slots calculation */ -ZEND_STATIC_ASSERT(ZEND_MM_ALIGNED_SIZE(sizeof(zval)) == sizeof(zval), +static_assert(ZEND_MM_ALIGNED_SIZE(sizeof(zval)) == sizeof(zval), "zval must be aligned by ZEND_MM_ALIGNMENT"); /* A number of call frame slots (zvals) reserved for _zend_vm_stack. */ #define ZEND_VM_STACK_HEADER_SLOTS \ diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index 2e16a3683644..e6c7ed992b40 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -862,14 +862,6 @@ extern "C++" { /** @deprecated */ #define ZEND_CGG_DIAGNOSTIC_IGNORED_END ZEND_DIAGNOSTIC_IGNORED_END -#if defined(__cplusplus) -# define ZEND_STATIC_ASSERT(c, m) static_assert((c), m) -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ -# define ZEND_STATIC_ASSERT(c, m) _Static_assert((c), m) -#else -# define ZEND_STATIC_ASSERT(c, m) -#endif - #if !defined(ZEND_WIN32) typedef max_align_t zend_max_align_t; #else diff --git a/ext/date/time_duration.c b/ext/date/time_duration.c index a9f69db7a3d8..f84bee1fc72e 100644 --- a/ext/date/time_duration.c +++ b/ext/date/time_duration.c @@ -25,8 +25,8 @@ #define NANOS_IN_MILLI 1000000 #define MILLIS_IN_SEC 1000 -ZEND_STATIC_ASSERT(NANOS_IN_MICRO * MICROS_IN_SEC == NANOS_IN_SEC, ""); -ZEND_STATIC_ASSERT(NANOS_IN_MILLI * MILLIS_IN_SEC == NANOS_IN_SEC, ""); +static_assert(NANOS_IN_MICRO * MICROS_IN_SEC == NANOS_IN_SEC, ""); +static_assert(NANOS_IN_MILLI * MILLIS_IN_SEC == NANOS_IN_SEC, ""); #define Z_PARAM_ULONG(l) { \ zend_long __##l; \ diff --git a/ext/random/engine_mt19937.c b/ext/random/engine_mt19937.c index a84fb9585c80..bccf3770fb43 100644 --- a/ext/random/engine_mt19937.c +++ b/ext/random/engine_mt19937.c @@ -85,7 +85,7 @@ */ #define N 624 /* length of state vector */ -ZEND_STATIC_ASSERT( +static_assert( N == sizeof(((php_random_status_state_mt19937*)0)->state) / sizeof(((php_random_status_state_mt19937*)0)->state[0]), "Assumed length of Mt19937 state vector does not match actual size." ); diff --git a/ext/random/php_random_zend_utils.h b/ext/random/php_random_zend_utils.h index 56718e5c214b..411da02a075c 100644 --- a/ext/random/php_random_zend_utils.h +++ b/ext/random/php_random_zend_utils.h @@ -25,7 +25,7 @@ typedef struct php_random_bytes_insecure_state_for_zend { php_random_status_state_xoshiro256starstar xoshiro256starstar_state; } php_random_bytes_insecure_state_for_zend; -ZEND_STATIC_ASSERT(sizeof(zend_random_bytes_insecure_state) >= sizeof(php_random_bytes_insecure_state_for_zend), ""); +static_assert(sizeof(zend_random_bytes_insecure_state) >= sizeof(php_random_bytes_insecure_state_for_zend), ""); ZEND_ATTRIBUTE_NONNULL PHPAPI void php_random_bytes_insecure_for_zend( zend_random_bytes_insecure_state *state, void *bytes, size_t size); From 6eb8d06b578ea2893c91c341dd1476ec0347ad79 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 25 Sep 2026 05:27:52 +0100 Subject: [PATCH 05/10] Fix GH-23897: php:function() assertion after failed registerPHPFunctions() A registerPHPFunctions() call that threw on a non callable name had already allocated the callback namespace, leaving it in PHP_DOM_REG_FUNC_MODE_NONE. The next php:function() call dispatched into it and tripped the MODE_SET assertion. Close GH-23900 --- NEWS | 2 ++ ext/dom/tests/gh23897.phpt | 25 +++++++++++++++++++++++++ ext/dom/xpath_callbacks.c | 2 +- ext/xsl/tests/gh23897.phpt | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 ext/dom/tests/gh23897.phpt create mode 100644 ext/xsl/tests/gh23897.phpt diff --git a/NEWS b/NEWS index 1de00a09ded0..113e79508dbd 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,8 @@ PHP NEWS (Ilia Alshanetsky) . Fixed bug GH-23729 (DOMXPath::__construct() use-after-free during an evaluation). (David Carlier) + . Fixed bug GH-23897 (php:function() assertion failure after a failed + registerPHPFunctions()). (David Carlier) - FTP: . Fixed bug GH-23619 (cryptic error on servers that don't support TLS diff --git a/ext/dom/tests/gh23897.phpt b/ext/dom/tests/gh23897.phpt new file mode 100644 index 000000000000..79c465ec1947 --- /dev/null +++ b/ext/dom/tests/gh23897.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-23897 (Assertion failure in php_dom_xpath_callback_dispatch() after failed registerPhpFunctions()) +--EXTENSIONS-- +dom +--FILE-- +loadXML(''); + +$xpath = new DOMXPath($dom); +$xpath->registerNamespace('php', 'http://php.net/xpath'); +try { + $xpath->registerPhpFunctions('testPhpFunction'); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +try { + var_dump($xpath->evaluate('php:function("testPhpFunction")')); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +TypeError: DOMXPath::registerPhpFunctions(): Argument #1 ($restrict) must be a callable, function "testPhpFunction" not found or invalid function name +Error: No callbacks were registered diff --git a/ext/dom/xpath_callbacks.c b/ext/dom/xpath_callbacks.c index 5dd3c5caded5..553671dbfa0a 100644 --- a/ext/dom/xpath_callbacks.c +++ b/ext/dom/xpath_callbacks.c @@ -407,7 +407,7 @@ static zend_result php_dom_xpath_callback_dispatch(php_dom_xpath_callbacks *xpat { zval callback_retval; - if (UNEXPECTED(ns == NULL)) { + if (UNEXPECTED(ns == NULL || ns->mode == PHP_DOM_REG_FUNC_MODE_NONE)) { zend_throw_error(NULL, "No callbacks were registered"); return FAILURE; } diff --git a/ext/xsl/tests/gh23897.phpt b/ext/xsl/tests/gh23897.phpt new file mode 100644 index 000000000000..cde0113153fc --- /dev/null +++ b/ext/xsl/tests/gh23897.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-23897 (Assertion failure in php_dom_xpath_callback_dispatch() after failed registerPHPFunctions()) +--EXTENSIONS-- +xsl +--CREDITS-- +YuanchengJiang +--FILE-- +loadXML(''); + +$xsl = new DOMDocument(); +$xsl->loadXML(<< + + + + +XML); + +$xslt = new XSLTProcessor(); +try { + $xslt->registerPHPFunctions('testPhpFunction'); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +$xslt->importStylesheet($xsl); +try { + var_dump($xslt->transformToXml($dom)); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +?> +--EXPECT-- +TypeError: XSLTProcessor::registerPHPFunctions(): Argument #1 ($functions) must be a callable, function "testPhpFunction" not found or invalid function name +Error: No callbacks were registered From c06e7370ae58ca8e132aae162c86bdaf40bc394d Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Sun, 13 Sep 2026 14:18:59 -0400 Subject: [PATCH 06/10] ext/opcache: Fix GH-23679 tracing JIT shadowed private writes zend_get_known_property_info treated a ZEND_ACC_CHANGED public property as the known offset even when the executing scope owned a private property of the same name, so tracing JIT wrote through a child's public slot. Returning NULL uses the runtime-cache path, which already goes through zend_get_property_offset. FETCH_OBJ_W, ASSIGN_OBJ, INC, and ASSIGN_OBJ_OP share the helper; $this access, child methods, and unshadowed or protected-to-public properties do not hit this arm. Fixes GH-23679 Closes GH-23683 --- NEWS | 8 +- ext/opcache/jit/zend_jit.c | 5 ++ ext/opcache/tests/jit/gh23679.phpt | 116 +++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 ext/opcache/tests/jit/gh23679.phpt diff --git a/NEWS b/NEWS index 4f963f08f1f6..23ce2db98b90 100644 --- a/NEWS +++ b/NEWS @@ -64,6 +64,10 @@ PHP NEWS . Fixed OSS-Fuzz #5674034779193344 (Read of uninitialized memory in is_cacheable_stream_path()). (ndossche) . Fix zend_analyze_calls() call_stack buffer overrun. (Mrmaxmeier) + . Fixed bug GH-23628 (Tracing JIT reads undefined property slots of lazy + proxy objects instead of forwarding to the real instance). (lisachenko) + . Fixed bug GH-23679 (Tracing JIT writes a parent private property into a + child's shadowing public property). (Ilia Alshanetsky) - PDO: . Fixed PDOStatement::getColumnMeta() reading out of bounds for an invalid @@ -98,10 +102,6 @@ PHP NEWS . Fixed inflate_init() dropping the preset dictionary for raw streams with a non-default window. (Ilia Alshanetsky) -- Opcache: - . Fixed bug GH-23628 (Tracing JIT reads undefined property slots of lazy - proxy objects instead of forwarding to the real instance). (lisachenko) - 24 Sep 2026, PHP 8.5.11 diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 76510743d333..1e95d80456be 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -650,6 +650,11 @@ static zend_property_info* zend_get_known_property_info(const zend_op_array *op_ } if (info->flags & ZEND_ACC_PUBLIC) { + if ((info->flags & ZEND_ACC_CHANGED) + && op_array->scope + && op_array->scope != ce) { + return NULL; + } return info; } else if (on_this) { if (ce == info->ce) { diff --git a/ext/opcache/tests/jit/gh23679.phpt b/ext/opcache/tests/jit/gh23679.phpt new file mode 100644 index 000000000000..8b3c65c6567f --- /dev/null +++ b/ext/opcache/tests/jit/gh23679.phpt @@ -0,0 +1,116 @@ +--TEST-- +GH-23679: tracing JIT must not write a parent private property into a child's shadowing public property +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.protect_memory=1 +opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=1 +opcache.jit_hot_return=1 +opcache.jit_hot_side_exit=1 +--EXTENSIONS-- +opcache +--FILE-- +arr[$k] = $v; + return $x; + } + + public function setAll($v) + { + $x = clone $this; + $x->arr = $v; + return $x; + } + + public function inc() + { + $x = clone $this; + $x->n++; + return $x; + } + + public function addN($k) + { + $x = clone $this; + $x->n += $k; + return $x; + } + + public function getDim($k) + { + return $this->arr[$k] ?? 'MISSING'; + } + + public function getN() + { + return $this->n; + } +} + +class B extends A +{ + public $arr = []; + public $n = 100; +} + +for ($i = 0; $i < 50; $i++) { + (new A)->setDim('x', 1)->getDim('x'); + (new A)->setAll(['x' => 1])->getDim('x'); + (new A)->inc()->getN(); + (new A)->addN(5)->getN(); + + $b = new B; + $b->arr = ['keep' => 1]; + $b->n = 100; + + $r = $b->setDim('x', 2); + if ($r->getDim('x') !== 2 || $r->arr !== ['keep' => 1]) { + echo "dim-assign i=$i private="; + var_dump($r->getDim('x')); + echo "dim-assign i=$i public="; + var_dump($r->arr); + exit(1); + } + + $r = $b->setAll(['y' => 4]); + if ($r->getDim('y') !== 4 || $r->arr !== ['keep' => 1]) { + echo "assign i=$i private="; + var_dump($r->getDim('y')); + echo "assign i=$i public="; + var_dump($r->arr); + exit(1); + } + + $r = $b->inc(); + if ($r->getN() !== 1 || $r->n !== 100) { + echo "inc i=$i private="; + var_dump($r->getN()); + echo "inc i=$i public="; + var_dump($r->n); + exit(1); + } + + $r = $b->addN(5); + if ($r->getN() !== 5 || $r->n !== 100) { + echo "assign-op i=$i private="; + var_dump($r->getN()); + echo "assign-op i=$i public="; + var_dump($r->n); + exit(1); + } +} +echo "ok\n"; +?> +--EXPECT-- +ok From 436cccbd982ba56da8798bc389dcaa400ba35eb2 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 25 Sep 2026 14:50:40 +0200 Subject: [PATCH 07/10] [skip ci] Sync .github files These files should be congruent across branches. --- .github/matrix.php | 3 ++- .github/workflows/test.yml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/matrix.php b/.github/matrix.php index ee9cb4eb05ab..87486791cff5 100644 --- a/.github/matrix.php +++ b/.github/matrix.php @@ -1,7 +1,8 @@ 'master', 'ref' => 'master', 'version' => [8, 6]], + ['name' => 'master', 'ref' => 'master', 'version' => [8, 7]], + ['name' => 'PHP-8.6', 'ref' => 'PHP-8.6', 'version' => [8, 6]], ['name' => 'PHP-8.5', 'ref' => 'PHP-8.5', 'version' => [8, 5]], ['name' => 'PHP-8.4', 'ref' => 'PHP-8.4', 'version' => [8, 4]], ['name' => 'PHP-8.3', 'ref' => 'PHP-8.3', 'version' => [8, 3]], diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eafedec5eafa..057ed83eb226 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,6 +18,7 @@ on: - PHP-8.3 - PHP-8.4 - PHP-8.5 + - PHP-8.6 - master pull_request: paths-ignore: *ignore_paths From 1b8d390d73b45cd687021197fec99ad4c8f9eb21 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 25 Sep 2026 15:00:52 +0200 Subject: [PATCH 08/10] [skip ci] Skip recursion tests on ASAN ASAN stack usage is unreliable so we might overflow before the limit is reached. --- ext/soap/tests/GHSA-rgrp-mwpx-f6rm-href-chain.phpt | 6 ++++++ ext/soap/tests/GHSA-rgrp-mwpx-f6rm-href-cycle.phpt | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/ext/soap/tests/GHSA-rgrp-mwpx-f6rm-href-chain.phpt b/ext/soap/tests/GHSA-rgrp-mwpx-f6rm-href-chain.phpt index e07f78d816f3..a8e96473166b 100644 --- a/ext/soap/tests/GHSA-rgrp-mwpx-f6rm-href-chain.phpt +++ b/ext/soap/tests/GHSA-rgrp-mwpx-f6rm-href-chain.phpt @@ -2,6 +2,12 @@ GHSA-rgrp-mwpx-f6rm: Stack overflow on a chain of href references --EXTENSIONS-- soap +--SKIPIF-- + --FILE-- --FILE-- Date: Fri, 25 Sep 2026 15:07:05 +0200 Subject: [PATCH 09/10] #if out zend_class_constants_are_updated() in release builds This function is only used in debug builds. --- Zend/zend_lazy_objects.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Zend/zend_lazy_objects.c b/Zend/zend_lazy_objects.c index f7ef547bc9cb..eb8b5f300eeb 100644 --- a/Zend/zend_lazy_objects.c +++ b/Zend/zend_lazy_objects.c @@ -179,6 +179,7 @@ bool zend_lazy_object_decr_lazy_props(zend_object *obj) return info->lazy_properties_count == 0; } +#if ZEND_DEBUG /* See zend_update_class_constants(). */ static zend_always_inline bool zend_class_constants_are_updated(const zend_class_entry *ce) { if (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) { @@ -190,6 +191,7 @@ static zend_always_inline bool zend_class_constants_are_updated(const zend_class } return false; } +#endif /** * Making objects lazy From 8cb57d2e2217aa3db64ca42b78489ede2a20dcf8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Sep 2026 15:26:08 +0200 Subject: [PATCH 10/10] ext/pcntl: do not drop queued signals when an exception is pending (#23624) * ext/pcntl: run signal handlers when dispatch happens with an exception pending ZEND_DO_FCALL runs its interrupt check right after an internal function returns, before the pending exception is handled, and zend_call_function() does the same for the calls it makes, so pcntl_interrupt_function() can reach the dispatcher with EG(exception) set. call_user_function() returns without calling anything in that state, the "if (EG(exception)) break" added by 296fad10fb4 fires on the first entry, and the drain loop then recycles the whole queue without a single handler having run. The signal is destroyed rather than delayed: a later pcntl_signal_dispatch() finds nothing left. Set the exception aside while the handlers run and chain it back afterwards. The frame is left as found, EG(opline_before_exception) included: depending on the caller, the exception may not be registered on it yet, or may already be on its way to a catch block, and the caller finishes the job once we return. zend_test_raise_and_throw() and the user frame test are from Arnaud Le Blanc. Any long blocking internal call that throws on timeout reaches this. pecl/amqp throws "Consumer timeout exceed" out of AMQPQueue::consume(), which makes a Symfony messenger worker miss every SIGTERM whatever the timeout is. PDO/SQLite throws "database is locked" once busy_timeout expires, which kills a keepalive SIGALRM for the rest of the process's life. * ext/pcntl: keep the signals a throwing handler left in the queue When a handler threw, the signals queued behind it were recycled without ever being delivered. Put them back on the queue instead, and re-arm the interrupt so that the engine dispatches them once the exception has been handled, rather than leaving them to wait for another signal to come in. Not calling further handlers while the exception propagates is unchanged. --- NEWS | 6 ++ ext/pcntl/pcntl.c | 65 ++++++++++++++++--- .../pcntl_signal_dispatch_exception_2.phpt | 44 +++++++++++++ .../pcntl_signal_dispatch_exception_3.phpt | 47 ++++++++++++++ ...ntl_signal_dispatch_exception_pending.phpt | 24 +++++++ ...dispatch_exception_pending_user_frame.phpt | 33 ++++++++++ ext/zend_test/test.c | 17 +++++ ext/zend_test/test.stub.php | 2 + ext/zend_test/test_arginfo.h | 8 ++- 9 files changed, 235 insertions(+), 11 deletions(-) create mode 100644 ext/pcntl/tests/pcntl_signal_dispatch_exception_2.phpt create mode 100644 ext/pcntl/tests/pcntl_signal_dispatch_exception_3.phpt create mode 100644 ext/pcntl/tests/pcntl_signal_dispatch_exception_pending.phpt create mode 100644 ext/pcntl/tests/pcntl_signal_dispatch_exception_pending_user_frame.phpt diff --git a/NEWS b/NEWS index 113e79508dbd..8d34aad3fe67 100644 --- a/NEWS +++ b/NEWS @@ -65,6 +65,12 @@ PHP NEWS is_cacheable_stream_path()). (ndossche) . Fix zend_analyze_calls() call_stack buffer overrun. (Mrmaxmeier) +- PCNTL: + . Fixed pcntl_signal_dispatch() dropping the queued signals when it runs while + an exception is pending. (nicolas-grekas) + . Fixed pcntl_signal_dispatch() dropping the signals queued behind a handler + that throws. (nicolas-grekas) + - PDO: . Fixed PDOStatement::getColumnMeta() reading out of bounds for an invalid column index. (Ilia Alshanetsky) diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 082bdc4ba90e..b55e1417d114 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -31,6 +31,7 @@ #include "ext/standard/info.h" #include "php_signal.h" #include "php_ticks.h" +#include "zend_exceptions.h" #include "zend_fibers.h" #if defined(HAVE_GETPRIORITY) || defined(HAVE_SETPRIORITY) || defined(HAVE_WAIT3) @@ -1318,6 +1319,9 @@ void pcntl_signal_dispatch(void) { zval params[2], *handle, retval; struct php_pcntl_pending_signal *queue, *next; + zend_object *old_exception; + const zend_op *old_opline_before_exception = NULL; + const zend_op *old_opline = NULL; sigset_t mask; sigset_t old_mask; @@ -1345,8 +1349,24 @@ void pcntl_signal_dispatch(void) PCNTL_G(head) = NULL; /* simple stores are atomic */ PCNTL_G(tail) = NULL; + /* Dispatching can happen with an exception pending, e.g. from the interrupt check that runs + * right after an internal function threw. call_user_function() does nothing in that state, + * so set the exception aside while the handlers run. The frame is left as found: depending + * on the caller, the exception may not be registered on it yet, or may already be on its + * way to a catch block, and the caller takes it from there once we return. */ + old_exception = EG(exception); + if (old_exception) { + if (EG(current_execute_data)) { + old_opline = EG(current_execute_data)->opline; + } + old_opline_before_exception = EG(opline_before_exception); + EG(exception) = NULL; + } + /* Allocate */ while (queue) { + bool handler_threw = false; + if ((handle = zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo)) != NULL) { if (Z_TYPE_P(handle) != IS_LONG) { ZVAL_NULL(&retval); @@ -1365,9 +1385,7 @@ void pcntl_signal_dispatch(void) #ifdef HAVE_STRUCT_SIGINFO_T zval_ptr_dtor(¶ms[1]); #endif - if (EG(exception)) { - break; - } + handler_threw = NULL != EG(exception); } } @@ -1375,17 +1393,44 @@ void pcntl_signal_dispatch(void) queue->next = PCNTL_G(spares); PCNTL_G(spares) = queue; queue = next; + + /* No other handler can be called while the exception propagates */ + if (handler_threw) { + break; + } } - /* drain the remaining in case of exception thrown */ - while (queue) { - next = queue->next; - queue->next = PCNTL_G(spares); - PCNTL_G(spares) = queue; - queue = next; + if (old_exception) { + if (EG(current_execute_data)) { + EG(current_execute_data)->opline = old_opline; + } + EG(opline_before_exception) = old_opline_before_exception; + if (EG(exception)) { + zend_exception_set_previous(EG(exception), old_exception); + } else { + EG(exception) = old_exception; + } } - PCNTL_G(pending_signals) = 0; + if (UNEXPECTED(queue)) { + /* Put back what the throwing handler did not get to, instead of dropping it, and ask + * the engine to come back once the exception has been handled. Signals are still + * blocked here, so PCNTL_G(head) cannot have been repopulated in the meantime. */ + next = queue; + + while (next->next) { + next = next->next; + } + + PCNTL_G(head) = queue; + PCNTL_G(tail) = next; + + if (PCNTL_G(async_signals)) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } + } else { + PCNTL_G(pending_signals) = 0; + } /* Re-enable queue */ PCNTL_G(processing_signal_queue) = 0; diff --git a/ext/pcntl/tests/pcntl_signal_dispatch_exception_2.phpt b/ext/pcntl/tests/pcntl_signal_dispatch_exception_2.phpt new file mode 100644 index 000000000000..ebd868df5d4b --- /dev/null +++ b/ext/pcntl/tests/pcntl_signal_dispatch_exception_2.phpt @@ -0,0 +1,44 @@ +--TEST-- +pcntl_signal_dispatch() keeps the signals left in the queue by a throwing handler +--EXTENSIONS-- +pcntl +posix +--FILE-- +getMessage() . "\n"; +} + +echo "Handlers called: " . implode(', ', $called) . "\n"; + +pcntl_signal_dispatch(); + +echo "Handlers called: " . implode(', ', $called) . "\n"; + +?> +--EXPECT-- +Exception in signal handler +Handlers called: SIGUSR1 +Handlers called: SIGUSR1, SIGUSR2, SIGHUP diff --git a/ext/pcntl/tests/pcntl_signal_dispatch_exception_3.phpt b/ext/pcntl/tests/pcntl_signal_dispatch_exception_3.phpt new file mode 100644 index 000000000000..ff877e3c0400 --- /dev/null +++ b/ext/pcntl/tests/pcntl_signal_dispatch_exception_3.phpt @@ -0,0 +1,47 @@ +--TEST-- +pcntl_signal_dispatch() delivers the signals a throwing handler left behind once its exception is handled +--EXTENSIONS-- +pcntl +posix +--FILE-- +getMessage() . "\n"; +} + +// No explicit dispatch: the engine delivers what the throwing handler left behind +// on its own, as soon as the exception has been handled +usleep(1000); + +echo "Handlers called: " . implode(', ', $called) . "\n"; + +?> +--EXPECT-- +Exception in signal handler +Handlers called: SIGUSR1, SIGUSR2, SIGHUP diff --git a/ext/pcntl/tests/pcntl_signal_dispatch_exception_pending.phpt b/ext/pcntl/tests/pcntl_signal_dispatch_exception_pending.phpt new file mode 100644 index 000000000000..5a7968e9b382 --- /dev/null +++ b/ext/pcntl/tests/pcntl_signal_dispatch_exception_pending.phpt @@ -0,0 +1,24 @@ +--TEST-- +pcntl_signal_dispatch() runs the handlers of the signals raised while an internal function ran and then threw +--EXTENSIONS-- +pcntl +zend_test +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Handler called +Exception after raise() diff --git a/ext/pcntl/tests/pcntl_signal_dispatch_exception_pending_user_frame.phpt b/ext/pcntl/tests/pcntl_signal_dispatch_exception_pending_user_frame.phpt new file mode 100644 index 000000000000..bae00348c144 --- /dev/null +++ b/ext/pcntl/tests/pcntl_signal_dispatch_exception_pending_user_frame.phpt @@ -0,0 +1,33 @@ +--TEST-- +pcntl_signal_dispatch() with an exception pending after an internal function called from a user frame +--EXTENSIONS-- +pcntl +zend_test +--FILE-- + +--EXPECTF-- +Handler called from test() + +Fatal error: Uncaught Exception: Exception after raise() in %s:%d +Stack trace: +#0 %s(%d): zend_test_raise_and_throw(%d) +#1 %s(%d): test() +#2 {main} + thrown in %s on line %d diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 576bacd5e4a9..c4be7ff42fb6 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -37,6 +37,7 @@ #include "zend_call_stack.h" #include "zend_exceptions.h" #include "zend_mm_custom_handlers.h" +#include // `php.h` sets `NDEBUG` when not `PHP_DEBUG` which will make `assert()` from // assert.h a no-op. In order to have `assert()` working on NDEBUG builds, we @@ -672,6 +673,22 @@ static ZEND_FUNCTION(zend_test_crash) php_printf("%s", invalid); } +static ZEND_FUNCTION(zend_test_raise_and_throw) +{ + zend_long signo; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(signo) + ZEND_PARSE_PARAMETERS_END(); + + if (raise((int) signo) != 0) { + zend_throw_error(NULL, "raise() failed"); + RETURN_THROWS(); + } + + zend_throw_exception(NULL, "Exception after raise()", 0); +} + static bool has_opline(zend_execute_data *execute_data) { return execute_data diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 9116245c30f4..dfe04caabbb4 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -298,6 +298,8 @@ function zend_get_map_ptr_last(): int {} function zend_test_crash(?string $message = null): void {} + function zend_test_raise_and_throw(int $signal): void {} + function zend_test_fill_packed_array(array &$array): void {} /** @return resource */ diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 039757207e69..c08feb900466 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: bf65e1dd1eeeeec46687a76a7ea6554cd1971dfc */ + * Stub hash: d87db0f02d40732749355ee65828221c3fd14cdb */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -133,6 +133,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_crash, 0, 0, IS_VOID, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_raise_and_throw, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, signal, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_fill_packed_array, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(1, array, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -294,6 +298,7 @@ static ZEND_FUNCTION(zend_test_zend_call_stack_use_all); static ZEND_FUNCTION(zend_test_is_string_marked_as_valid_utf8); static ZEND_FUNCTION(zend_get_map_ptr_last); static ZEND_FUNCTION(zend_test_crash); +static ZEND_FUNCTION(zend_test_raise_and_throw); static ZEND_FUNCTION(zend_test_fill_packed_array); static ZEND_FUNCTION(zend_test_create_throwing_resource); static ZEND_FUNCTION(get_open_basedir); @@ -401,6 +406,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_test_is_string_marked_as_valid_utf8, arginfo_zend_test_is_string_marked_as_valid_utf8) ZEND_FE(zend_get_map_ptr_last, arginfo_zend_get_map_ptr_last) ZEND_FE(zend_test_crash, arginfo_zend_test_crash) + ZEND_FE(zend_test_raise_and_throw, arginfo_zend_test_raise_and_throw) ZEND_FE(zend_test_fill_packed_array, arginfo_zend_test_fill_packed_array) ZEND_FE(zend_test_create_throwing_resource, arginfo_zend_test_create_throwing_resource) ZEND_FE(get_open_basedir, arginfo_get_open_basedir)