diff --git a/Zend/tests/error_handler/gh_21699.phpt b/Zend/tests/error_handler/gh_21699.phpt index 49b58365dabf..58f062773f55 100644 --- a/Zend/tests/error_handler/gh_21699.phpt +++ b/Zend/tests/error_handler/gh_21699.phpt @@ -24,7 +24,7 @@ Stack trace: #1 %s(%d): bar->test() #2 {main} -Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d +Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, cannot use "self"/"parent"/"static" in callables in %s:%d Stack trace: #0 %s(%d): bar->test() #1 {main} diff --git a/Zend/tests/error_handler/gh_21699_parent.phpt b/Zend/tests/error_handler/gh_21699_parent.phpt index 73cae41f3f5b..b2c985b25e19 100644 --- a/Zend/tests/error_handler/gh_21699_parent.phpt +++ b/Zend/tests/error_handler/gh_21699_parent.phpt @@ -25,7 +25,7 @@ Stack trace: #1 %s(%d): Child->test() #2 {main} -Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d +Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, cannot use "self"/"parent"/"static" in callables in %s:%d Stack trace: #0 %s(%d): Child->test() #1 {main} diff --git a/Zend/tests/error_handler/gh_21699_static.phpt b/Zend/tests/error_handler/gh_21699_static.phpt index 4d9604ebe77b..43c942983ec3 100644 --- a/Zend/tests/error_handler/gh_21699_static.phpt +++ b/Zend/tests/error_handler/gh_21699_static.phpt @@ -24,7 +24,7 @@ Stack trace: #1 %s(%d): bar->test() #2 {main} -Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d +Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, cannot use "self"/"parent"/"static" in callables in %s:%d Stack trace: #0 %s(%d): bar->test() #1 {main} diff --git a/Zend/tests/gh16799.phpt b/Zend/tests/gh16799.phpt index d31d1a5705c6..51bff1fd4cff 100644 --- a/Zend/tests/gh16799.phpt +++ b/Zend/tests/gh16799.phpt @@ -19,7 +19,7 @@ Stack trace: #1 %s(%d): Test::test() #2 {main} -Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, (null) in %s:%d +Next TypeError: call_user_func(): Argument #1 ($callback) must be a valid callback, cannot use "self"/"parent"/"static" in callables in %s:%d Stack trace: #0 %s(%d): Test::test() #1 {main} diff --git a/Zend/zend_API.c b/Zend/zend_API.c index de7c3bfc341b..25ba4241e7a5 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3817,6 +3817,11 @@ static bool zend_is_callable_check_class(zend_string *name, zend_class_entry *sc ZSTR_ALLOCA_FREE(lcname, use_heap); /* User error handlers may throw from deprecations above; do not report callable as valid. */ if (UNEXPECTED(EG(exception))) { + /* We allocate an error string that will not be used as callers of zend_is_callable() expect an + * error to be assigned on failure */ + if (error && *error == NULL) { + *error = estrdup("cannot use \"self\"/\"parent\"/\"static\" in callables"); + } return false; } return ret; diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index beb3f9f995af..09cf6c18cd12 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -848,13 +848,11 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ } if (!zend_is_callable_ex(&fci->function_name, fci->object, 0, NULL, fci_cache, &error)) { + ZEND_ASSERT(error && "Should have error if not callable"); if (EG(exception)) { - if (error) { - efree(error); - } + efree(error); return SUCCESS; } - ZEND_ASSERT(error && "Should have error if not callable"); zend_string *callable_name = zend_get_callable_name_ex(&fci->function_name, fci->object); zend_throw_error(NULL, "Invalid callback %s, %s", ZSTR_VAL(callable_name), error); diff --git a/docs/source/core/data-structures/fci-fcc.rst b/docs/source/core/data-structures/fci-fcc.rst index 85a10caaca1e..b6355f420693 100644 --- a/docs/source/core/data-structures/fci-fcc.rst +++ b/docs/source/core/data-structures/fci-fcc.rst @@ -80,8 +80,9 @@ There are some functions that do not require the use of either the FCI or the FC - Check if the ``callable`` zval is a PHP callable. Returns true if it is, false otherwise. ``fcc`` is an out parameter constructing the FCC struct, see below for details. ``error`` is an optional out parameter holding the error message if ``callable`` is not a PHP - callable. Note: the signature used to be ``bool zend_is_callable(zval *callable, uint32_t - check_flags, zend_string **callable_name)`` prior to PHP 8.7. + callable. If ``error`` is non-NULL and false is returned it is *always* populated. Note: + the signature used to be ``bool zend_is_callable(zval *callable, uint32_t check_flags, + zend_string **callable_name)`` prior to PHP 8.7. - - .. code:: c diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 61b423220898..b2e7d1feaee6 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1656,7 +1656,7 @@ static bool php_curl_set_callable_handler(zend_fcall_info_cache *const handler_f } char *error = NULL; - if (UNEXPECTED(!zend_is_callable_ex(callable, /* object */ NULL, /* check_flags */ 0, /* callable_name */ NULL, handler_fcc, /* error */ &error))) { + if (UNEXPECTED(!zend_is_callable(callable, handler_fcc, /* error */ &error))) { if (!EG(exception)) { zend_argument_type_error(2 + !is_array_config, "must be a valid callback for option %s, %s", option_name, error); } diff --git a/ext/curl/multi.c b/ext/curl/multi.c index a06b204746c4..6614a52809c5 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -485,7 +485,7 @@ static bool _php_curl_multi_setopt(php_curlm *mh, zend_long option, zval *zvalue } char *error_str = NULL; - if (UNEXPECTED(!zend_is_callable_ex(zvalue, /* object */ NULL, /* check_flags */ 0, /* callable_name */ NULL, &mh->handlers.server_push, /* error */ &error_str))) { + if (UNEXPECTED(!zend_is_callable(zvalue, &mh->handlers.server_push, &error_str))) { if (!EG(exception)) { zend_argument_type_error(2, "must be a valid callback for option CURLMOPT_PUSHFUNCTION, %s", error_str); } diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index b352706c51b8..ede5ee4024f8 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -2008,13 +2008,9 @@ static zend_result php_openssl_validate_and_allocate_psk_callback( char *is_callable_error = NULL; zend_fcall_info_cache fcc = {0}; if (!zend_is_callable(callable, &fcc, &is_callable_error)) { - if (is_callable_error) { - zend_type_error("%s must be a valid callback, %s", - callback_name, is_callable_error); - efree(is_callable_error); - } else { - zend_type_error("%s must be a valid callback", callback_name); - } + zend_type_error("%s must be a valid callback, %s", + callback_name, is_callable_error); + efree(is_callable_error); return FAILURE; } @@ -2131,12 +2127,8 @@ static zend_result php_openssl_setup_server_early_data(php_stream *stream, char *is_callable_error = NULL; zend_fcall_info_cache fcc = {0}; if (!zend_is_callable(val, &fcc, &is_callable_error)) { - if (is_callable_error) { - zend_type_error("early_data_cb must be a valid callback, %s", is_callable_error); - efree(is_callable_error); - } else { - zend_type_error("early_data_cb must be a valid callback"); - } + zend_type_error("early_data_cb must be a valid callback, %s", is_callable_error); + efree(is_callable_error); return FAILURE; } @@ -2319,12 +2311,8 @@ static zend_result php_openssl_validate_and_allocate_session_callback( /* Validate callable */ zend_fcall_info_cache fcc; if (!zend_is_callable(callable, &fcc, &is_callable_error)) { - if (is_callable_error) { - zend_type_error("%s must be a valid callback, %s", callback_name, is_callable_error); - efree(is_callable_error); - } else { - zend_type_error("%s must be a valid callback", callback_name); - } + zend_type_error("%s must be a valid callback, %s", callback_name, is_callable_error); + efree(is_callable_error); return FAILURE; } diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 7db6b7218d62..eab4398a0d5e 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -1137,12 +1137,8 @@ static bool pdo_get_fcc_from_zval(zend_fcall_info_cache *fcc, zval *callable) { char *is_callable_error = NULL; if (!zend_is_callable(callable, fcc, &is_callable_error)) { - if (is_callable_error) { - zend_type_error("%s", is_callable_error); - efree(is_callable_error); - } else { - zend_type_error("User-supplied function must be a valid callback"); - } + zend_type_error("%s", is_callable_error); + efree(is_callable_error); return false; } return true; diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c index 788dceff445b..e2dfa310e614 100644 --- a/main/streams/stream_errors.c +++ b/main/streams/stream_errors.c @@ -370,10 +370,8 @@ static void php_stream_call_error_handler(const zval *handler, zval *errors_arra char *is_callable_error = NULL; if (!zend_is_callable(handler, &fcc, &is_callable_error)) { - if (is_callable_error) { - zend_type_error("stream error handler must be a valid callback, %s", is_callable_error); - efree(is_callable_error); - } + zend_type_error("stream error handler must be a valid callback, %s", is_callable_error); + efree(is_callable_error); return; }