Commit a66ffb80fd7 for php.net

commit a66ffb80fd7779dea8928ce6eafbad5932c91529
Author: Gina Peter Banyard <girgias@php.net>
Date:   Fri Jul 31 12:42:43 2026 +0100

    Zend: move '\' and lowercase handling to zend_fetch_function() (#22966)

    Rather than having the logic be repeated over and over at the call sites.

diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index 33128e4a7e3..714fa156522 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -3830,32 +3830,11 @@ static zend_always_inline bool zend_is_callable_check_func(const zval *callable,
 	int call_via_handler = 0;
 	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);
-			}
-		}
+		zend_function *func = zend_fetch_function(Z_STR_P(callable));
 		if (EXPECTED(func != NULL)) {
 			fcc->function_handler = func;
 			return 1;
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index a91f0d2c933..4b070d9d5d5 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 91cab1b63d2..bc93aa8892a 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/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 7fa76c121ca..6682346a5b0 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));