Commit 40b904316c9 for php

commit 40b904316c90fe9450a4cdfc65faf047e8153fcf
Author: Gina Peter Banyard <girgias@php.net>
Date:   Fri Oct 2 19:15:11 2026 +0100

    Zend: add a new zend_call_method_if_exists_ex() (#24082)

diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index e16a7bdae80..45c4ef34e85 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -40,6 +40,8 @@ PHP 8.7 INTERNALS UPGRADE NOTES
 - Added a new zend_object_copy() function which increases the refcount of
   a zend_object* and returns it. Allowing more descriptive code rather than
   doing an assignment followed by GC_ADDREF().
+- Added zend_call_method_if_exists_ex() which accepts named parameters and
+  retrieving the callability failure error message.

 ========================
 2. Build system changes
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index c94a71cf56b..425139e3a00 100644
--- a/Zend/zend_API.h
+++ b/Zend/zend_API.h
@@ -913,12 +913,21 @@ static zend_always_inline void zend_call_known_instance_method_with_1_params(
 ZEND_API void zend_call_known_instance_method_with_2_params(
 		zend_function *fn, zend_object *this_ptr, zval *retval_ptr, zval *param1, zval *param2);

-/* Call method if it exists. Return FAILURE if method does not exist or call failed.
- * If FAILURE is returned, retval will be UNDEF. As such, destroying retval unconditionally
- * is legal. */
-ZEND_API zend_result zend_call_method_if_exists(
-		zend_object *this_ptr, zend_string *method_name, zval *retval,
-		uint32_t param_count, zval *params);
+/* Call method if it exists.
+ * Return FAILURE if method does not exist.
+ * If FAILURE is returned, retval will be UNDEF, and error will contain the validation failure message if provided.
+ * As such, destroying retval unconditionally is legal.
+ */
+ZEND_API zend_result zend_call_method_if_exists_ex(
+		zend_object *this_ptr, zend_string *method_name, zval *retval_ptr,
+		uint32_t param_count, zval *params, HashTable *named_params, char **error);
+
+static zend_always_inline zend_result zend_call_method_if_exists(
+		zend_object *this_ptr, zend_string *method_name,
+		zval *retval_ptr, uint32_t param_count, zval *params
+) {
+	return zend_call_method_if_exists_ex(this_ptr, method_name, retval_ptr, param_count, params, NULL, NULL);
+}

 ZEND_API zend_result zend_delete_global_variable(zend_string *name);

diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index a1b7c16ba73..beb3f9f995a 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -1184,21 +1184,21 @@ ZEND_API void zend_call_known_instance_method_with_2_params(
 	zend_call_known_instance_method(fn, this_ptr, retval_ptr, 2, params);
 }

-ZEND_API zend_result zend_call_method_if_exists(
-		zend_object *this_ptr, zend_string *method_name, zval *retval,
-		uint32_t param_count, zval *params)
-{
+ZEND_API zend_result zend_call_method_if_exists_ex(
+		zend_object *this_ptr, zend_string *method_name, zval *retval_ptr,
+		uint32_t param_count, zval *params, HashTable *named_params, char **error
+) {
 	zval zval_method;
 	zend_fcall_info_cache fcc;

 	ZVAL_STR(&zval_method, method_name);

-	if (UNEXPECTED(!zend_is_callable_ex(&zval_method, this_ptr, IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL, &fcc, NULL))) {
-		ZVAL_UNDEF(retval);
+	if (UNEXPECTED(!zend_is_callable_ex(&zval_method, this_ptr, IS_CALLABLE_SUPPRESS_DEPRECATIONS, NULL, &fcc, error))) {
+		ZVAL_UNDEF(retval_ptr);
 		return FAILURE;
 	}

-	zend_call_known_fcc(&fcc, retval, param_count, params, NULL);
+	zend_call_known_fcc(&fcc, retval_ptr, param_count, params, named_params);
 	/* Need to free potential trampoline (__call/__callStatic) copied function handler before releasing the closure */
 	zend_release_fcall_info_cache(&fcc);
 	return SUCCESS;