diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 33128e4a7e37..299d5d57a249 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3818,62 +3818,32 @@ ZEND_API void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc) { } } -static zend_always_inline bool zend_is_callable_check_func(const zval *callable, const zend_execute_data *frame, zend_fcall_info_cache *fcc, bool strict_class, char **error, bool suppress_deprecation) /* {{{ */ +static zend_always_inline bool zend_is_method_callable(zend_string *callable, const zend_execute_data *frame, zend_fcall_info_cache *fcc, bool strict_class, char **error, bool suppress_deprecation) /* {{{ */ { zend_class_entry *ce_org = fcc->calling_scope; bool retval = false; - zend_string *mname, *cname; - zend_string *lmname; + zend_string *mname; const char *colon; - size_t clen; HashTable *ftable; - int call_via_handler = 0; + bool call_via_handler = false; zend_class_entry *scope; zval *zv; - ALLOCA_FLAG(use_heap) fcc->calling_scope = NULL; - if (!ce_org) { - zend_function *func; - zend_string *lmname; - - /* Check if function with given name exists. - * This may be a compound name that includes namespace name */ - if (UNEXPECTED(Z_STRVAL_P(callable)[0] == '\\')) { - /* Skip leading \ */ - ZSTR_ALLOCA_ALLOC(lmname, Z_STRLEN_P(callable) - 1, use_heap); - zend_str_tolower_copy(ZSTR_VAL(lmname), Z_STRVAL_P(callable) + 1, Z_STRLEN_P(callable) - 1); - func = zend_fetch_function(lmname); - ZSTR_ALLOCA_FREE(lmname, use_heap); - } else { - lmname = Z_STR_P(callable); - func = zend_fetch_function(lmname); - if (!func) { - ZSTR_ALLOCA_ALLOC(lmname, Z_STRLEN_P(callable), use_heap); - zend_str_tolower_copy(ZSTR_VAL(lmname), Z_STRVAL_P(callable), Z_STRLEN_P(callable)); - func = zend_fetch_function(lmname); - ZSTR_ALLOCA_FREE(lmname, use_heap); - } - } - if (EXPECTED(func != NULL)) { - fcc->function_handler = func; - return 1; - } - } - /* Split name into class/namespace and method/function names */ - if ((colon = zend_memrchr(Z_STRVAL_P(callable), ':', Z_STRLEN_P(callable))) != NULL && - colon > Z_STRVAL_P(callable) && + if ((colon = zend_memrchr(ZSTR_VAL(callable), ':', ZSTR_LEN(callable))) != NULL && + colon > ZSTR_VAL(callable) && *(colon-1) == ':' ) { size_t mlen; colon--; - clen = colon - Z_STRVAL_P(callable); - mlen = Z_STRLEN_P(callable) - clen - 2; - if (colon == Z_STRVAL_P(callable)) { + size_t class_name_len = colon - ZSTR_VAL(callable); + mlen = ZSTR_LEN(callable) - class_name_len - 2; + + if (colon == ZSTR_VAL(callable)) { if (error) *error = estrdup("invalid function name"); return 0; } @@ -3886,9 +3856,9 @@ static zend_always_inline bool zend_is_callable_check_func(const zval *callable, scope = get_scope(frame); } - cname = zend_string_init_interned(Z_STRVAL_P(callable), clen, 0); - if (ZSTR_HAS_CE_CACHE(cname) && ZSTR_GET_CE_CACHE(cname)) { - fcc->calling_scope = ZSTR_GET_CE_CACHE(cname); + zend_string *class_name = zend_string_init_interned(ZSTR_VAL(callable), class_name_len, 0); + if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { + fcc->calling_scope = ZSTR_GET_CE_CACHE(class_name); if (scope && !fcc->object) { zend_object *object = zend_get_this_object(frame); @@ -3904,11 +3874,11 @@ static zend_always_inline bool zend_is_callable_check_func(const zval *callable, fcc->called_scope = fcc->object ? fcc->object->ce : fcc->calling_scope; } strict_class = true; - } else if (!zend_is_callable_check_class(cname, scope, frame, fcc, &strict_class, error, suppress_deprecation || ce_org != NULL)) { - zend_string_release_ex(cname, 0); + } else if (!zend_is_callable_check_class(class_name, scope, frame, fcc, &strict_class, error, suppress_deprecation || ce_org != NULL)) { + zend_string_release_ex(class_name, 0); return 0; } - zend_string_release_ex(cname, 0); + zend_string_release_ex(class_name, 0); ftable = &fcc->calling_scope->function_table; if (ce_org && !instanceof_function(ce_org, fcc->calling_scope)) { @@ -3918,24 +3888,24 @@ static zend_always_inline bool zend_is_callable_check_func(const zval *callable, if (ce_org && !suppress_deprecation) { zend_error(E_DEPRECATED, "Callables of the form [\"%s\", \"%s\"] are deprecated", - ZSTR_VAL(ce_org->name), Z_STRVAL_P(callable)); + ZSTR_VAL(ce_org->name), ZSTR_VAL(callable)); } - mname = zend_string_init(Z_STRVAL_P(callable) + clen + 2, mlen, 0); + mname = zend_string_init(ZSTR_VAL(callable) + class_name_len + 2, mlen, 0); } else if (ce_org) { /* Try to fetch find static method of given class. */ - mname = Z_STR_P(callable); + mname = callable; zend_string_addref(mname); ftable = &ce_org->function_table; fcc->calling_scope = ce_org; } else { /* We already checked for plain function before. */ if (error) { - zend_spprintf(error, 0, "function \"%s\" not found or invalid function name", Z_STRVAL_P(callable)); + zend_spprintf(error, 0, "function \"%s\" not found or invalid function name", ZSTR_VAL(callable)); } return 0; } - lmname = zend_string_tolower(mname); + zend_string *lmname = zend_string_tolower(mname); if (strict_class && fcc->calling_scope && zend_string_equals_literal(lmname, ZEND_CONSTRUCTOR_FUNC_NAME)) { @@ -3980,7 +3950,7 @@ static zend_always_inline bool zend_is_callable_check_func(const zval *callable, if (fcc->object && fcc->calling_scope == ce_org) { if (strict_class && ce_org->__call) { fcc->function_handler = zend_get_call_trampoline_func(ce_org->__call, mname); - call_via_handler = 1; + call_via_handler = true; retval = true; } else { fcc->function_handler = fcc->object->handlers->get_method(&fcc->object, mname, NULL); @@ -4198,22 +4168,30 @@ ZEND_API bool zend_is_callable_at_frame( again: switch (Z_TYPE_P(callable)) { case IS_STRING: - if (object) { + /* First check for a normal function */ + if (!object) { + if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) { + return true; + } + + zend_function *func = zend_fetch_function(Z_STR_P(callable)); + if (EXPECTED(func != NULL)) { + fcc->function_handler = func; + return true; + } + /* Might be a static method */ + } else { fcc->object = object; fcc->calling_scope = object->ce; } if (check_flags & IS_CALLABLE_CHECK_SYNTAX_ONLY) { fcc->called_scope = fcc->calling_scope; - return 1; + return true; } -check_func: - ret = zend_is_callable_check_func(callable, frame, fcc, strict_class, error, check_flags & IS_CALLABLE_SUPPRESS_DEPRECATIONS); - if (fcc == &fcc_local) { - zend_release_fcall_info_cache(fcc); - } - return ret; + ret = zend_is_method_callable(Z_STR_P(callable), frame, fcc, strict_class, error, check_flags & IS_CALLABLE_SUPPRESS_DEPRECATIONS); + break; case IS_ARRAY: { @@ -4260,21 +4238,19 @@ ZEND_API bool zend_is_callable_at_frame( } } - callable = method; - goto check_func; + ret = zend_is_method_callable(Z_STR_P(method), frame, fcc, strict_class, error, check_flags & IS_CALLABLE_SUPPRESS_DEPRECATIONS); + break; } - return 0; + case IS_OBJECT: - if (Z_OBJ_HANDLER_P(callable, get_closure) && Z_OBJ_HANDLER_P(callable, get_closure)(Z_OBJ_P(callable), &fcc->calling_scope, &fcc->function_handler, &fcc->object, 1) == SUCCESS) { - fcc->called_scope = fcc->calling_scope; - fcc->closure = Z_OBJ_P(callable); - if (fcc == &fcc_local) { - zend_release_fcall_info_cache(fcc); - } - return 1; + if (Z_OBJ_HANDLER_P(callable, get_closure) && Z_OBJ_HANDLER_P(callable, get_closure)(Z_OBJ_P(callable), &fcc->calling_scope, &fcc->function_handler, &fcc->object, 1) == FAILURE) { + if (error) *error = estrdup("no array or string given"); + return 0; } - if (error) *error = estrdup("no array or string given"); - return 0; + fcc->called_scope = fcc->calling_scope; + fcc->closure = Z_OBJ_P(callable); + ret = true; + break; case IS_REFERENCE: callable = Z_REFVAL_P(callable); goto again; @@ -4282,6 +4258,11 @@ ZEND_API bool zend_is_callable_at_frame( if (error) *error = estrdup("no array or string given"); return 0; } + + if (fcc == &fcc_local) { + zend_release_fcall_info_cache(fcc); + } + return ret; } /* }}} */ diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index a91f0d2c933a..4b070d9d5d58 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1241,15 +1241,16 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner( if (!fptr) { zend_string *function_name = zend_ast_get_str(ast->child[0]); - zend_string *function_name_lc = zend_string_tolower(function_name); - fptr = zend_fetch_function(function_name_lc); + fptr = zend_fetch_function(function_name); + + /* Search for global function of the same name */ if (!fptr && ast->child[0]->attr != ZEND_NAME_FQ) { - const char *backslash = zend_memrchr(ZSTR_VAL(function_name_lc), '\\', ZSTR_LEN(function_name_lc)); + const char *backslash = zend_memrchr(ZSTR_VAL(function_name), '\\', ZSTR_LEN(function_name)); if (backslash) { - fptr = zend_fetch_function_str(backslash + 1, ZSTR_LEN(function_name_lc) - (backslash - ZSTR_VAL(function_name_lc) + 1)); + fptr = zend_fetch_function_str(backslash + 1, ZSTR_LEN(function_name) - (backslash - ZSTR_VAL(function_name) + 1)); } } - zend_string_release(function_name_lc); + if (!fptr) { zend_throw_error(NULL, "Call to undefined function %s()", ZSTR_VAL(function_name)); return FAILURE; diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 91cab1b63d2a..bc93aa8892ac 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4498,32 +4498,34 @@ static zend_never_inline void ZEND_FASTCALL init_func_run_time_cache(zend_op_arr ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name) /* {{{ */ { - zval *zv = zend_hash_find(EG(function_table), name); - - if (EXPECTED(zv != NULL)) { - zend_function *fbc = Z_FUNC_P(zv); + zend_function *fbc; + if (UNEXPECTED(ZSTR_VAL(name)[0] == '\\')) { + /* Ignore leading "\" */ + fbc = zend_hash_str_find_ptr_lc(EG(function_table), ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1); + } else { + fbc = zend_hash_find_ptr_lc(EG(function_table), name); + } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { - init_func_run_time_cache_i(&fbc->op_array); - } - return fbc; + if (EXPECTED(fbc && fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache_i(&fbc->op_array); } - return NULL; + return fbc; } /* }}} */ ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function_str(const char *name, size_t len) /* {{{ */ { - const zval *zv = zend_hash_str_find(EG(function_table), name, len); - - if (EXPECTED(zv != NULL)) { - zend_function *fbc = Z_FUNC_P(zv); + zend_function *fbc; + if (UNEXPECTED(name[0] == '\\')) { + /* Ignore leading "\" */ + fbc = zend_hash_str_find_ptr_lc(EG(function_table), name + 1, len - 1); + } else { + fbc = zend_hash_str_find_ptr_lc(EG(function_table), name, len); + } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { - init_func_run_time_cache_i(&fbc->op_array); - } - return fbc; + if (EXPECTED(fbc && fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache_i(&fbc->op_array); } - return NULL; + return fbc; } /* }}} */ ZEND_API void ZEND_FASTCALL zend_init_func_run_time_cache(zend_op_array *op_array) /* {{{ */ diff --git a/docs/release-process.md b/docs/release-process.md index 4215002a7b4d..e65a85c101a2 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -105,6 +105,7 @@ releases. * php-8.4.0 (initial GA) * php-8.4.9 (periodic bugfix or security release) +12. Ensure you are familiar with our procedure for [merging upwards][]. ## Packaging a non-stable release (alpha/beta/RC) @@ -321,7 +322,7 @@ slightly different steps. We'll call attention where the steps differ. git add main/php_version.h Zend/zend.h configure.ac git merge --continue ``` - + Be sure to set up a merge driver for the `NEWS` file as described in the [Git FAQ page on the PHP wiki][gitfaq-mandatory]. @@ -543,25 +544,38 @@ slightly different steps. We'll call attention where the steps differ. ## Packaging a stable release -1. Check out the *patch-level version branch* for the release - (e.g., `PHP-8.1.7`). +1. Check out the *patch-level version branch* for the release. + + ``` + git switch PHP-X.Y.Z + ``` > 💬 **Hint** \ > You should have created this branch when packaging the non-stable release > candidate for this version. If it is for a PHP-X.Y.0 version, then the branch > was created as part of the final planned release candidate, PHP-X.Y.0RC4. -2. If a CVE commit needs to be merged to the release, have it committed to - the base branches and [merged upwards as usual][] (e.g. commit the CVE fix - to 7.2, merge to 7.3, 7.4, etc.). Then, you can cherry-pick it into the - patch-level version branch for this release. +2. If the upcoming release is a security release, you will have been informed + about it by the security release manager (SRM) by Tuesday noon (UTC). - Commit these changes and push the patch-level version branch. Ensure - that CI is still passing (see above). + > 💬 **Hint** \ + > If you haven't set up a git remote for the security repo yet, do so: + > ```bash + > git remote add security git@github.com:php/php-src-security.git + > ``` - > 💡 **Tip** \ - > Don't forget to update `NEWS` manually in an extra commit to the - > patch-level version branch. + The SRM will provide you with a branch to merge in your + *patch-level version branch*. + + ```bash + git fetch security + git merge security/PHP-X.Y.Z-security + git push upstream PHP-X.Y.Z + ``` + + > 💬 **Hint** \ + > You do not need to merge this back into PHP-X.Y; the SRM will take care + > of it. 3. Run the `./scripts/dev/credits` script in the patch-level version branch, and commit the changes in the credits files in `ext/standard`. @@ -1182,7 +1196,7 @@ volunteers to begin the selection process for the next release managers. [Update NEWS for PHP 8.2.0RC6]: https://github.com/php/php-src/commit/4ccc414961a70200d638ca281a35f893226d74e2 [PHP 8.3 is now for PHP 8.3.21-dev]: https://github.com/php/php-src/commit/b57f425cfe20a11003253427424cc0517483550b [GitHub command line tool]: https://cli.github.com -[merged upwards as usual]: https://wiki.php.net/vcs/gitworkflow +[merging upwards]: https://wiki.php.net/vcs/gitworkflow [Update versions for PHP 8.1.7]: https://github.com/php/php-src/commit/d35e577a1bd0b35b9386cea97cddc73fd98eed6d [Update NEWS for PHP 8.1.7]: https://github.com/php/php-src/commit/b241f07f52ca9f87bf52be81817f475e6e727439 [Announce PHP 8.1.6]: https://github.com/php/web-php/commit/9f796a96c65f07e45845ec248933bfb0010b94a9 diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 7fa76c121cac..6682346a5b08 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1684,7 +1684,7 @@ ZEND_METHOD(ReflectionFunction, __construct) { zend_object *closure_obj = NULL; zend_function *fptr; - zend_string *fname, *lcname; + zend_string *fname; zval *object = ZEND_THIS; reflection_object *intern = Z_REFLECTION_P(object); @@ -1696,18 +1696,7 @@ ZEND_METHOD(ReflectionFunction, __construct) if (closure_obj) { fptr = (zend_function*)zend_get_closure_method_def(closure_obj); } else { - if (UNEXPECTED(ZSTR_VAL(fname)[0] == '\\')) { - /* Ignore leading "\" */ - ALLOCA_FLAG(use_heap) - ZSTR_ALLOCA_ALLOC(lcname, ZSTR_LEN(fname) - 1, use_heap); - zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(fname) + 1, ZSTR_LEN(fname) - 1); - fptr = zend_fetch_function(lcname); - ZSTR_ALLOCA_FREE(lcname, use_heap); - } else { - lcname = zend_string_tolower(fname); - fptr = zend_fetch_function(lcname); - zend_string_release(lcname); - } + fptr = zend_fetch_function(fname); if (fptr == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, @@ -2429,19 +2418,7 @@ ZEND_METHOD(ReflectionParameter, __construct) switch (Z_TYPE_P(reference)) { case IS_STRING: { zend_string *fname = Z_STR_P(reference); - zend_string *lcname; - if (UNEXPECTED(ZSTR_VAL(fname)[0] == '\\')) { - /* Ignore leading "\" */ - ALLOCA_FLAG(use_heap) - ZSTR_ALLOCA_ALLOC(lcname, ZSTR_LEN(fname) - 1, use_heap); - zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(fname) + 1, ZSTR_LEN(fname) - 1); - fptr = zend_fetch_function(lcname); - ZSTR_ALLOCA_FREE(lcname, use_heap); - } else { - lcname = zend_string_tolower(fname); - fptr = zend_fetch_function(lcname); - zend_string_release(lcname); - } + fptr = zend_fetch_function(fname); if (!fptr) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Function %s() does not exist", Z_STRVAL_P(reference)); diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index 2597269bad41..cf529e40085b 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -127,8 +127,6 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char php_uri *resource = NULL; int result, use_ssl, use_ssl_on_data = 0; char tmp_line[512]; - char *transport; - int transport_len; const php_uri_parser *uri_parser = php_stream_context_get_uri_parser("ftp", context); if (uri_parser == NULL) { @@ -150,7 +148,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char if (resource->port == 0) resource->port = 21; - transport_len = (int)spprintf(&transport, 0, "tcp://%s:" ZEND_LONG_FMT, ZSTR_VAL(resource->host), resource->port); + char *transport; + size_t transport_len = spprintf(&transport, 0, "tcp://%s:" ZEND_LONG_FMT, ZSTR_VAL(resource->host), resource->port); stream = php_stream_xport_create(transport, transport_len, REPORT_ERRORS, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, NULL, NULL, context, NULL, NULL); efree(transport); if (stream == NULL) { @@ -420,8 +419,6 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa zval *tmpzval; bool allow_overwrite = false; int8_t read_write = 0; - char *transport; - int transport_len; zend_string *error_message = NULL; tmp_line[0] = '\0'; @@ -554,7 +551,9 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa if (hoststart == NULL) { hoststart = ZSTR_VAL(resource->host); } - transport_len = (int)spprintf(&transport, 0, "tcp://%s:%d", hoststart, portno); + + char *transport; + size_t transport_len = spprintf(&transport, 0, "tcp://%s:%d", hoststart, portno); datastream = php_stream_xport_create(transport, transport_len, REPORT_ERRORS, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, NULL, NULL, context, &error_message, NULL); efree(transport); if (datastream == NULL) { @@ -916,18 +915,14 @@ static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, i stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, AuthFailed, - "Unable to connect to %s", url); - } + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", url); goto unlink_errexit; } if (resource->path == NULL) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, InvalidPath, - "Invalid path provided in %s", url); - } + php_stream_wrapper_warn(wrapper, context, options, InvalidPath, + "Invalid path provided in %s", url); goto unlink_errexit; } @@ -936,10 +931,8 @@ static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, i result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, UnlinkFailed, - "Error Deleting file: %s", tmp_line); - } + php_stream_wrapper_warn(wrapper, context, options, UnlinkFailed, + "Error Deleting file: %s", tmp_line); goto unlink_errexit; } @@ -1001,10 +994,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr stream = php_ftp_fopen_connect(wrapper, url_from, "r", 0, NULL, context, NULL, NULL, NULL, NULL); if (!stream) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, AuthFailed, - "Unable to connect to %s", ZSTR_VAL(resource_from->host)); - } + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", ZSTR_VAL(resource_from->host)); goto rename_errexit; } @@ -1013,10 +1004,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, RenameFailed, - "Error Renaming file: %s", tmp_line); - } + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "Error Renaming file: %s", tmp_line); goto rename_errexit; } @@ -1025,10 +1014,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, RenameFailed, - "Error Renaming file: %s", tmp_line); - } + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "Error Renaming file: %s", tmp_line); goto rename_errexit; } @@ -1059,18 +1046,14 @@ static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, in stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, AuthFailed, - "Unable to connect to %s", url); - } + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", url); goto mkdir_errexit; } if (resource->path == NULL) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, InvalidPath, - "Invalid path provided in %s", url); - } + php_stream_wrapper_warn(wrapper, context, options, InvalidPath, + "Invalid path provided in %s", url); goto mkdir_errexit; } @@ -1109,10 +1092,8 @@ static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, in php_stream_printf(stream, "MKD %s\r\n", buf); result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, MkdirFailed, - "%s", tmp_line); - } + php_stream_wrapper_warn(wrapper, context, options, MkdirFailed, + "%s", tmp_line); break; } } @@ -1154,18 +1135,14 @@ static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, in stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, AuthFailed, - "Unable to connect to %s", url); - } + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", url); goto rmdir_errexit; } if (resource->path == NULL) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, InvalidPath, - "Invalid path provided in %s", url); - } + php_stream_wrapper_warn(wrapper, context, options, InvalidPath, + "Invalid path provided in %s", url); goto rmdir_errexit; } @@ -1173,10 +1150,8 @@ static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, in result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, RmdirFailed, - "%s", tmp_line); - } + php_stream_wrapper_warn(wrapper, context, options, RmdirFailed, + "%s", tmp_line); goto rmdir_errexit; } diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index 4b2aa7116a24..9301536458a6 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -206,7 +206,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w /* Process folding headers if starting with a space or a tab. */ if (header_line && (*header_line == ' ' || *header_line == '\t')) { - char *http_folded_header_line = header_line; + const char *http_folded_header_line = header_line; size_t http_folded_header_line_length = *header_line_length; /* Remove the leading white spaces. */ while (*http_folded_header_line == ' ' || *http_folded_header_line == '\t') { @@ -232,7 +232,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w char *last_header_value = memchr(last_header_line, ':', last_header_line_length); if (last_header_value) { /* Verify there is no space in header name */ - char *last_header_name = last_header_line + 1; + const char *last_header_name = last_header_line + 1; while (last_header_name < last_header_value) { if (*last_header_name == ' ' || *last_header_name == '\t') { header_info->error = true; diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index 3f062bf2ea1e..b18832ebe410 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -220,11 +220,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c php_stream_input_t *input; if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, - Disabled, - "URL file-access is disabled in the server configuration"); - } + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "URL file-access is disabled in the server configuration"); return NULL; } @@ -241,11 +239,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (!strcasecmp(path, "stdin")) { if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, - Disabled, - "URL file-access is disabled in the server configuration"); - } + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "URL file-access is disabled in the server configuration"); return NULL; } if (!strcmp(sapi_module.name, "cli")) { @@ -302,20 +298,16 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c int dtablesize; if (strcmp(sapi_module.name, "cli")) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, - Disabled, - "Direct access to file descriptors is only available from command-line PHP"); - } + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "Direct access to file descriptors is only available from command-line PHP"); return NULL; } if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(wrapper, context, options, - Disabled, - "URL file-access is disabled in the server configuration"); - } + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "URL file-access is disabled in the server configuration"); return NULL; } diff --git a/ext/standard/tests/serialize/serialization_objects_007.phpt b/ext/standard/tests/serialize/unserialize_callback_func/autoload_must_be_called_twice.phpt similarity index 100% rename from ext/standard/tests/serialize/serialization_objects_007.phpt rename to ext/standard/tests/serialize/unserialize_callback_func/autoload_must_be_called_twice.phpt diff --git a/ext/standard/tests/serialize/bug26762.phpt b/ext/standard/tests/serialize/unserialize_callback_func/bug26762.phpt similarity index 100% rename from ext/standard/tests/serialize/bug26762.phpt rename to ext/standard/tests/serialize/unserialize_callback_func/bug26762.phpt diff --git a/ext/standard/tests/serialize/bug70213.phpt b/ext/standard/tests/serialize/unserialize_callback_func/bug70213.phpt similarity index 100% rename from ext/standard/tests/serialize/bug70213.phpt rename to ext/standard/tests/serialize/unserialize_callback_func/bug70213.phpt diff --git a/ext/standard/tests/serialize/unserialize_callback_func/deprecated_partially_supported_callable.phpt b/ext/standard/tests/serialize/unserialize_callback_func/deprecated_partially_supported_callable.phpt new file mode 100644 index 000000000000..a7806581abe5 --- /dev/null +++ b/ext/standard/tests/serialize/unserialize_callback_func/deprecated_partially_supported_callable.phpt @@ -0,0 +1,51 @@ +--TEST-- +unserialize_callback_func with partially deprecated callable string +--INI-- +unserialize_callback_func=parent::my_unserialize +--FILE-- +getMessage(), PHP_EOL; +} + +try { + $tester = new TesterChild(); + $o = $tester->unserialize($s); + var_dump($o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +echo "Done"; + +?> +--EXPECTF-- +Error: Invalid callback parent::my_unserialize, cannot access "parent" when no class scope is active + +Deprecated: Use of "parent" in callables is deprecated in %s on line %d +callback_called in TesterParent +object(Foo)#3 (0) { +} +Done diff --git a/ext/standard/tests/serialize/unserialize_callback_func/fn_with_fqn.phpt b/ext/standard/tests/serialize/unserialize_callback_func/fn_with_fqn.phpt new file mode 100644 index 000000000000..67ce94c00dad --- /dev/null +++ b/ext/standard/tests/serialize/unserialize_callback_func/fn_with_fqn.phpt @@ -0,0 +1,22 @@ +--TEST-- +unserialize_callback_func with fully qualified name function +--INI-- +unserialize_callback_func=\my_global_fn +--FILE-- + +--EXPECT-- +callback_called +object(Foo)#1 (0) { +} +Done diff --git a/ext/standard/tests/serialize/unserialize_callback_func/fn_with_fqn_namespace.phpt b/ext/standard/tests/serialize/unserialize_callback_func/fn_with_fqn_namespace.phpt new file mode 100644 index 000000000000..28718bdb2417 --- /dev/null +++ b/ext/standard/tests/serialize/unserialize_callback_func/fn_with_fqn_namespace.phpt @@ -0,0 +1,26 @@ +--TEST-- +unserialize_callback_func with fully qualified named namespaced function +--INI-- +unserialize_callback_func=\php\test\my_global_fn +--FILE-- + +--EXPECT-- +callback_called +object(Foo)#1 (0) { +} +Done diff --git a/ext/standard/tests/serialize/unserialize_callback_func/fn_with_null_bytes.phpt b/ext/standard/tests/serialize/unserialize_callback_func/fn_with_null_bytes.phpt new file mode 100644 index 000000000000..5341d4bb6dd8 --- /dev/null +++ b/ext/standard/tests/serialize/unserialize_callback_func/fn_with_null_bytes.phpt @@ -0,0 +1,25 @@ +--TEST-- +unserialize_callback_func with function name containing null bytes +--FILE-- +getMessage(), PHP_EOL; +} + +echo "Done"; +?> +--EXPECT-- +Error: Invalid callback foo, function "foo" not found or invalid function name +Done diff --git a/ext/standard/tests/serialize/serialization_objects_008.phpt b/ext/standard/tests/serialize/unserialize_callback_func/non_existing.phpt similarity index 100% rename from ext/standard/tests/serialize/serialization_objects_008.phpt rename to ext/standard/tests/serialize/unserialize_callback_func/non_existing.phpt diff --git a/ext/standard/tests/serialize/unserialize_callback_func/non_static_method_private.phpt b/ext/standard/tests/serialize/unserialize_callback_func/non_static_method_private.phpt new file mode 100644 index 000000000000..2b3fb93f3d4a --- /dev/null +++ b/ext/standard/tests/serialize/unserialize_callback_func/non_static_method_private.phpt @@ -0,0 +1,42 @@ +--TEST-- +unserialize_callback_func with private non-static method +--INI-- +unserialize_callback_func=Tester::my_unserialize +--FILE-- +getMessage(), PHP_EOL; +} + +try { + $tester = new Tester(); + $o = $tester->unserialize($s); + var_dump($o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +echo "Done"; + +?> +--EXPECT-- +Error: Invalid callback Tester::my_unserialize, non-static method Tester::my_unserialize() cannot be called statically +callback_called +object(Foo)#3 (0) { +} +Done diff --git a/ext/standard/tests/serialize/unserialize_callback_func/non_static_method_public.phpt b/ext/standard/tests/serialize/unserialize_callback_func/non_static_method_public.phpt new file mode 100644 index 000000000000..ec09c8285a5e --- /dev/null +++ b/ext/standard/tests/serialize/unserialize_callback_func/non_static_method_public.phpt @@ -0,0 +1,42 @@ +--TEST-- +unserialize_callback_func with public non-static method +--INI-- +unserialize_callback_func=Tester::my_unserialize +--FILE-- +getMessage(), PHP_EOL; +} + +try { + $tester = new Tester(); + $o = $tester->unserialize($s); + var_dump($o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +echo "Done"; + +?> +--EXPECT-- +Error: Invalid callback Tester::my_unserialize, non-static method Tester::my_unserialize() cannot be called statically +callback_called +object(Foo)#3 (0) { +} +Done diff --git a/ext/standard/tests/serialize/unserialize_callback_func/static_method.phpt b/ext/standard/tests/serialize/unserialize_callback_func/static_method.phpt new file mode 100644 index 000000000000..a96d998cd8c6 --- /dev/null +++ b/ext/standard/tests/serialize/unserialize_callback_func/static_method.phpt @@ -0,0 +1,26 @@ +--TEST-- +unserialize_callback_func with public static method +--INI-- +unserialize_callback_func=Tester::my_unserialize +--FILE-- + +--EXPECT-- +callback_called +object(Foo)#1 (0) { +} +Done diff --git a/ext/standard/tests/serialize/unserialize_callback_func/static_method_private.phpt b/ext/standard/tests/serialize/unserialize_callback_func/static_method_private.phpt new file mode 100644 index 000000000000..4bc4e760bed7 --- /dev/null +++ b/ext/standard/tests/serialize/unserialize_callback_func/static_method_private.phpt @@ -0,0 +1,41 @@ +--TEST-- +unserialize_callback_func with private static method +--INI-- +unserialize_callback_func=Tester::my_unserialize +--FILE-- +getMessage(), PHP_EOL; +} + +try { + $o = Tester::unserialize($s); + var_dump($o); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +echo "Done"; + +?> +--EXPECT-- +Error: Invalid callback Tester::my_unserialize, cannot access private method Tester::my_unserialize() +callback_called +object(Foo)#2 (0) { +} +Done diff --git a/ext/standard/tests/serialize/unserialize_callback_func_INI_modifications.phpt b/ext/standard/tests/serialize/unserialize_callback_func/unserialize_callback_func_INI_modifications.phpt similarity index 100% rename from ext/standard/tests/serialize/unserialize_callback_func_INI_modifications.phpt rename to ext/standard/tests/serialize/unserialize_callback_func/unserialize_callback_func_INI_modifications.phpt diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 458ad2f12baa..27647c907d3a 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -1256,7 +1256,7 @@ object ":" uiv ":" ["] { } /* Call unserialize callback */ - ZVAL_STR_COPY(&user_func, PG(unserialize_callback_func)); + ZVAL_STR(&user_func, zend_string_dup(PG(unserialize_callback_func), false)); ZVAL_STR(&args[0], class_name); BG(serialize_lock)++; diff --git a/main/main.c b/main/main.c index d0ffe1895e9c..753625643508 100644 --- a/main/main.c +++ b/main/main.c @@ -1884,7 +1884,7 @@ zend_result php_request_startup(void) if (PG(output_handler)) { zval oh; - ZVAL_STR_COPY(&oh, PG(output_handler)); + ZVAL_STR(&oh, zend_string_dup(PG(output_handler), false)); php_output_start_user(&oh, 0, PHP_OUTPUT_HANDLER_STDFLAGS); zval_ptr_dtor(&oh); } else if (PG(output_buffering)) { diff --git a/main/output.c b/main/output.c index 664adf7f1688..8221425f5cf6 100644 --- a/main/output.c +++ b/main/output.c @@ -468,7 +468,6 @@ PHPAPI php_output_handler *php_output_handler_create_user(zval *output_handler, char *error = NULL; php_output_handler *handler = NULL; php_output_handler_alias_ctor_t alias = NULL; - php_output_handler_user_func_t *user = NULL; switch (Z_TYPE_P(output_handler)) { case IS_NULL: @@ -480,22 +479,23 @@ PHPAPI php_output_handler *php_output_handler_create_user(zval *output_handler, break; } ZEND_FALLTHROUGH; - default: - user = ecalloc(1, sizeof(php_output_handler_user_func_t)); - if (SUCCESS == zend_fcall_info_init(output_handler, 0, &user->fci, &user->fcc, &handler_name, &error)) { + default: { + zend_fcall_info_cache *fcc = ecalloc(1, sizeof(*fcc)); + + if (zend_is_callable_ex(output_handler, NULL, 0, &handler_name, fcc, &error)) { handler = php_output_handler_init(handler_name, chunk_size, PHP_OUTPUT_HANDLER_ABILITY_FLAGS(flags) | PHP_OUTPUT_HANDLER_USER); - ZVAL_COPY(&user->zoh, output_handler); - handler->func.user = user; + zend_fcc_addref(fcc); + handler->func.user_fcc = fcc; } else { - efree(user); - } - if (error) { + efree(fcc); + ZEND_ASSERT(error); php_error_docref("ref.outcontrol", E_WARNING, "%s", error); efree(error); } if (handler_name) { zend_string_release_ex(handler_name, 0); } + } } return handler; @@ -707,8 +707,8 @@ PHPAPI void php_output_handler_dtor(php_output_handler *handler) efree(handler->buffer.data); } if (handler->flags & PHP_OUTPUT_HANDLER_USER) { - zval_ptr_dtor(&handler->func.user->zoh); - efree(handler->func.user); + zend_fcc_dtor(handler->func.user_fcc); + efree(handler->func.user_fcc); } if (handler->dtor && handler->opaq) { handler->dtor(handler->opaq); @@ -966,13 +966,12 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl /* ob_mode */ ZVAL_LONG(&ob_args[1], (zend_long) context->op); - /* Set FCI info */ - handler->func.user->fci.param_count = 2; - handler->func.user->fci.params = ob_args; - handler->func.user->fci.retval = &retval; - handler->func.user->fci.consumed_args = zend_fci_consumed_arg(0); + zend_call_known_fcc_ex(handler->func.user_fcc, &retval, 2, ob_args, NULL, zend_fci_consumed_arg(0)); - if (SUCCESS == zend_call_function(&handler->func.user->fci, &handler->func.user->fcc) && Z_TYPE(retval) != IS_UNDEF) { + zval_ptr_dtor(&ob_args[0]); + zval_ptr_dtor(&ob_args[1]); + + if (Z_TYPE(retval) != IS_UNDEF) { if (handler->flags & PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT) { // Make sure that we don't get lost in the current output buffer // by disabling it @@ -1026,9 +1025,7 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl status = PHP_OUTPUT_HANDLER_FAILURE; } - /* Free arguments and return value */ - zval_ptr_dtor(&ob_args[0]); - zval_ptr_dtor(&ob_args[1]); + /* Free return value */ zval_ptr_dtor(&retval); } else { diff --git a/main/php_output.h b/main/php_output.h index f0a26824936a..4db6809d4936 100644 --- a/main/php_output.h +++ b/main/php_output.h @@ -112,12 +112,6 @@ typedef zend_result (*php_output_handler_conflict_check_t)(const char *handler_n /* ctor for aliases */ typedef struct _php_output_handler *(*php_output_handler_alias_ctor_t)(const char *handler_name, size_t handler_name_len, size_t chunk_size, int flags); -typedef struct _php_output_handler_user_func_t { - zend_fcall_info fci; - zend_fcall_info_cache fcc; - zval zoh; -} php_output_handler_user_func_t; - typedef struct _php_output_handler { zend_string *name; int flags; @@ -129,7 +123,7 @@ typedef struct _php_output_handler { void (*dtor)(void *opaq); union { - php_output_handler_user_func_t *user; + zend_fcall_info_cache *user_fcc; php_output_handler_context_func_t internal; } func; } php_output_handler; diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 4db810b8b5e2..eb9b81b6e2c5 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -1482,7 +1482,7 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i } int ret = VCWD_MKDIR(dir, (mode_t)mode); - if (ret < 0 && (options & REPORT_ERRORS)) { + if (ret < 0) { php_stream_wrapper_warn(wrapper, context, options, MkdirFailed, "%s", strerror(errno)); return 0; diff --git a/main/streams/streams.c b/main/streams/streams.c index 192689ce036f..cb75e322d928 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1909,11 +1909,10 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const #else if (!localhost && path[n+3] != '\0' && path[n+3] != '/') { #endif - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(plain_files_wrapper, NULL, options, - ProtocolUnsupported, - "Remote host file access not supported, %s", path); - } + php_stream_wrapper_warn(plain_files_wrapper, NULL, options, + ProtocolUnsupported, + "Remote host file access not supported, %s", path); + return NULL; } @@ -1950,11 +1949,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const return wrapper; } - if (options & REPORT_ERRORS) { - php_stream_wrapper_warn(plain_files_wrapper, NULL, options, - Disabled, - "file:// wrapper is disabled in the server configuration"); - } + php_stream_wrapper_warn(plain_files_wrapper, NULL, options, + Disabled, + "file:// wrapper is disabled in the server configuration"); return NULL; }