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;