[php-src] master: Use zend_hash_find_ptr_lc() for case-insensitive name lookups (#22565)

Jorg Adam Sowa via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Jorg Adam Sowa (jorgsowa)
Committer: GitHub (web-flow)
Pusher: Girgias
Date: 2026-07-03T00:53:33+01:00

Commit: https://github.com/php/php-src/commit/39b66bae333eabb173476d09de1106890bc94980
Raw diff: https://github.com/php/php-src/commit/39b66bae333eabb173476d09de1106890bc94980.diff

Use zend_hash_find_ptr_lc() for case-insensitive name lookups (#22565)

A number of call sites lowercased a class/function/method name into a
temporary string solely to use it as a lookup key in the class table,
function table, module registry, etc., then released it. Replace those
with zend_hash_find_ptr_lc() / zend_hash_str_find_ptr_lc(), which
lowercase into a stack buffer (for short names), perform the lookup, and
free the temporary internally.

This removes the manual tolower + release boilerplate at 17 sites and
avoids a heap allocation for the common short-name case. No behavior
change.

Changed paths:
  M  Zend/Optimizer/zend_optimizer.c
  M  Zend/zend_builtin_functions.c
  M  Zend/zend_inheritance.c
  M  ext/hash/hash.c
  M  ext/opcache/ZendAccelerator.c
  M  ext/pdo/pdo_dbh.c
  M  ext/reflection/php_reflection.c
  M  sapi/cli/php_cli.c
  M  sapi/phpdbg/phpdbg_bp.c
  M  sapi/phpdbg/phpdbg_list.c


Diff:

diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c
index d10b4d83fc3e..0173d2ef47a2 100644
--- a/Zend/Optimizer/zend_optimizer.c
+++ b/Zend/Optimizer/zend_optimizer.c
@@ -104,9 +104,7 @@ zend_result zend_optimizer_eval_special_func_call(
 		zval *result, const zend_string *name, zend_string *arg) {
 	if (zend_string_equals_literal(name, "function_exists") ||
 			zend_string_equals_literal(name, "is_callable")) {
-		zend_string *lc_name = zend_string_tolower(arg);
-		const zend_internal_function *func = zend_hash_find_ptr(EG(function_table), lc_name);
-		zend_string_release_ex(lc_name, 0);
+		const zend_internal_function *func = zend_hash_find_ptr_lc(EG(function_table), arg);
 
 		if (func && func->type == ZEND_INTERNAL_FUNCTION
 				&& func->module->type == MODULE_PERSISTENT
@@ -120,9 +118,7 @@ zend_result zend_optimizer_eval_special_func_call(
 		return FAILURE;
 	}
 	if (zend_string_equals_literal(name, "extension_loaded")) {
-		zend_string *lc_name = zend_string_tolower(arg);
-		zend_module_entry *m = zend_hash_find_ptr(&module_registry, lc_name);
-		zend_string_release_ex(lc_name, 0);
+		zend_module_entry *m = zend_hash_find_ptr_lc(&module_registry, arg);
 
 		if (!m) {
 			if (PG(enable_dl)) {
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index 2dceac2512db..87acf073a2da 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -953,7 +953,6 @@ ZEND_FUNCTION(method_exists)
 {
 	zval *klass;
 	zend_string *method_name;
-	zend_string *lcname;
 	zend_class_entry *ce;
 	zend_function *func;
 
@@ -974,9 +973,7 @@ ZEND_FUNCTION(method_exists)
 		RETURN_THROWS();
 	}
 
-	lcname = zend_string_tolower(method_name);
-	func = zend_hash_find_ptr(&ce->function_table, lcname);
-	zend_string_release_ex(lcname, 0);
+	func = zend_hash_find_ptr_lc(&ce->function_table, method_name);
 
 	if (func) {
 		/* Exclude shadow properties when checking a method on a specific class. Include
@@ -2219,7 +2216,6 @@ ZEND_FUNCTION(extension_loaded)
 ZEND_FUNCTION(get_extension_funcs)
 {
 	zend_string *extension_name;
-	zend_string *lcname;
 	bool array;
 	zend_module_entry *module;
 	zend_function *zif;
@@ -2228,9 +2224,7 @@ ZEND_FUNCTION(get_extension_funcs)
 		RETURN_THROWS();
 	}
 	if (strncasecmp(ZSTR_VAL(extension_name), "zend", sizeof("zend"))) {
-		lcname = zend_string_tolower(extension_name);
-		module = zend_hash_find_ptr(&module_registry, lcname);
-		zend_string_release_ex(lcname, 0);
+		module = zend_hash_find_ptr_lc(&module_registry, extension_name);
 	} else {
 		module = zend_hash_str_find_ptr(&module_registry, "core", sizeof("core") - 1);
 	}
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 8df6a5599d30..848b9d209b2d 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -264,11 +264,7 @@ static zend_class_entry *lookup_class_ex(
 	bool in_preload = CG(compiler_options) & ZEND_COMPILE_PRELOAD;
 
 	if (UNEXPECTED(!EG(active) && !in_preload)) {
-		zend_string *lc_name = zend_string_tolower(name);
-
-		ce = zend_hash_find_ptr(CG(class_table), lc_name);
-
-		zend_string_release(lc_name);
+		ce = zend_hash_find_ptr_lc(CG(class_table), name);
 
 		if (register_unresolved && !ce) {
 			zend_error_noreturn(
@@ -2530,7 +2526,6 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
 	size_t i, j = 0;
 	zend_trait_precedence *cur_precedence;
 	zend_trait_method_reference *cur_method_ref;
-	zend_string *lc_trait_name;
 	zend_string *lcname;
 	HashTable **exclude_tables = NULL;
 	zend_class_entry **aliases = NULL;
@@ -2545,9 +2540,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
 		while ((cur_precedence = precedences[i])) {
 			/** Resolve classes for all precedence operations. */
 			cur_method_ref = &cur_precedence->trait_method;
-			lc_trait_name = zend_string_tolower(cur_method_ref->class_name);
-			trait = zend_hash_find_ptr(EG(class_table), lc_trait_name);
-			zend_string_release_ex(lc_trait_name, 0);
+			trait = zend_hash_find_ptr_lc(EG(class_table), cur_method_ref->class_name);
 			if (!trait || !(trait->ce_flags & ZEND_ACC_LINKED)) {
 				zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(cur_method_ref->class_name));
 			}
@@ -2574,9 +2567,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
 				zend_class_entry *exclude_ce;
 				uint32_t trait_num;
 
-				lc_trait_name = zend_string_tolower(class_name);
-				exclude_ce = zend_hash_find_ptr(EG(class_table), lc_trait_name);
-				zend_string_release_ex(lc_trait_name, 0);
+				exclude_ce = zend_hash_find_ptr_lc(EG(class_table), class_name);
 				if (!exclude_ce || !(exclude_ce->ce_flags & ZEND_ACC_LINKED)) {
 					zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(class_name));
 				}
@@ -2619,9 +2610,7 @@ static void zend_traits_init_trait_structures(zend_class_entry *ce, zend_class_e
 			lcname = zend_string_tolower(cur_method_ref->method_name);
 			if (cur_method_ref->class_name) {
 				/* For all aliases with an explicit class name, resolve the class now. */
-				lc_trait_name = zend_string_tolower(cur_method_ref->class_name);
-				trait = zend_hash_find_ptr(EG(class_table), lc_trait_name);
-				zend_string_release_ex(lc_trait_name, 0);
+				trait = zend_hash_find_ptr_lc(EG(class_table), cur_method_ref->class_name);
 				if (!trait || !(trait->ce_flags & ZEND_ACC_LINKED)) {
 					zend_error_noreturn(E_COMPILE_ERROR, "Could not find trait %s", ZSTR_VAL(cur_method_ref->class_name));
 				}
diff --git a/ext/hash/hash.c b/ext/hash/hash.c
index e2af98c81ddf..74abf02acd81 100644
--- a/ext/hash/hash.c
+++ b/ext/hash/hash.c
@@ -102,9 +102,7 @@ static struct mhash_bc_entry mhash_to_hash[MHASH_NUM_ALGOS] = {
 
 PHP_HASH_API const php_hash_ops *php_hash_fetch_ops(zend_string *algo) /* {{{ */
 {
-	zend_string *lower = zend_string_tolower(algo);
-	const php_hash_ops *ops = zend_hash_find_ptr(&php_hash_hashtable, lower);
-	zend_string_release(lower);
+	const php_hash_ops *ops = zend_hash_find_ptr_lc(&php_hash_hashtable, algo);
 
 	return ops;
 }
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 83e933b01181..6f606d9d37d5 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -3847,9 +3847,7 @@ static zend_result preload_resolve_deps(preload_error *error, const zend_class_e
 	memset(error, 0, sizeof(preload_error));
 
 	if (ce->parent_name) {
-		zend_string *key = zend_string_tolower(ce->parent_name);
-		const zend_class_entry *parent = zend_hash_find_ptr(EG(class_table), key);
-		zend_string_release(key);
+		const zend_class_entry *parent = zend_hash_find_ptr_lc(EG(class_table), ce->parent_name);
 		if (!parent) {
 			error->kind = "Unknown parent ";
 			error->name = ZSTR_VAL(ce->parent_name);
diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c
index 6a47ec30c862..dcd9f7b126df 100644
--- a/ext/pdo/pdo_dbh.c
+++ b/ext/pdo/pdo_dbh.c
@@ -1484,7 +1484,6 @@ static zend_function *dbh_method_get(zend_object **object, zend_string *method_n
 {
 	zend_function *fbc = NULL;
 	pdo_dbh_object_t *dbh_obj = php_pdo_dbh_fetch_object(*object);
-	zend_string *lc_method_name;
 
 	if ((fbc = zend_std_get_method(object, method_name, key)) == NULL) {
 		/* not a pre-defined method, nor a user-defined method; check
@@ -1497,9 +1496,7 @@ static zend_function *dbh_method_get(zend_object **object, zend_string *method_n
 			}
 		}
 
-		lc_method_name = zend_string_tolower(method_name);
-		fbc = zend_hash_find_ptr(dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH], lc_method_name);
-		zend_string_release_ex(lc_method_name, 0);
+		fbc = zend_hash_find_ptr_lc(dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH], method_name);
 	}
 
 out:
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index a9f6b579977e..37c45cb02168 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -859,7 +859,6 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze
 {
 	smart_str param_indent = {0};
 	zend_function *overwrites;
-	zend_string *lc_name;
 
 	/* TBD: Repair indenting of doc comment (or is this to be done in the parser?)
 	 * What's "wrong" is that any whitespace before the doc comment start is
@@ -885,13 +884,11 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze
 		if (fptr->common.scope != scope) {
 			smart_str_append_printf(str, ", inherits %s", ZSTR_VAL(fptr->common.scope->name));
 		} else if (fptr->common.scope->parent) {
-			lc_name = zend_string_tolower(fptr->common.function_name);
-			if ((overwrites = zend_hash_find_ptr(&fptr->common.scope->parent->function_table, lc_name)) != NULL) {
+			if ((overwrites = zend_hash_find_ptr_lc(&fptr->common.scope->parent->function_table, fptr->common.function_name)) != NULL) {
 				if (fptr->common.scope != overwrites->common.scope && !(overwrites->common.fn_flags & ZEND_ACC_PRIVATE)) {
 					smart_str_append_printf(str, ", overwrites %s", ZSTR_VAL(overwrites->common.scope->name));
 				}
 			}
-			zend_string_release_ex(lc_name, 0);
 		}
 	}
 	if (fptr->common.prototype && fptr->common.prototype->common.scope) {
diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c
index 9a05f8e68547..e5e573d1533c 100644
--- a/sapi/cli/php_cli.c
+++ b/sapi/cli/php_cli.c
@@ -1093,10 +1093,9 @@ static int do_cli(int argc, char **argv) /* {{{ */
 		case PHP_CLI_MODE_REFLECTION_EXT_INFO:
 			{
 				size_t len = strlen(reflection_what);
-				char *lcname = zend_str_tolower_dup(reflection_what, len);
 				zend_module_entry *module;
 
-				if ((module = zend_hash_str_find_ptr(&module_registry, lcname, len)) == NULL) {
+				if ((module = zend_hash_str_find_ptr_lc(&module_registry, reflection_what, len)) == NULL) {
 					if (!strcmp(reflection_what, "main")) {
 						display_ini_entries(NULL);
 					} else {
@@ -1107,7 +1106,6 @@ static int do_cli(int argc, char **argv) /* {{{ */
 					php_info_print_module(module);
 				}
 
-				efree(lcname);
 				break;
 			}
 
diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c
index 4dfa89d4b0fe..b9c3436280cf 100644
--- a/sapi/phpdbg/phpdbg_bp.c
+++ b/sapi/phpdbg/phpdbg_bp.c
@@ -966,13 +966,7 @@ static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_symbol(zend_function *f
 	}
 
 	if (ops->function_name) {
-		phpdbg_breakbase_t *brake;
-		zend_string *fname = zend_string_tolower(ops->function_name);
-
-		brake = zend_hash_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], fname);
-
-		zend_string_release(fname);
-		return brake;
+		return zend_hash_find_ptr_lc(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], ops->function_name);
 	} else {
 		return zend_hash_str_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], ZEND_STRL("main"));
 	}
@@ -982,17 +976,11 @@ static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_method(zend_op_array *o
 {
 	HashTable *class_table;
 	phpdbg_breakbase_t *brake = NULL;
-	zend_string *class_lcname = zend_string_tolower(ops->scope->name);
-
-	if ((class_table = zend_hash_find_ptr(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], class_lcname))) {
-		zend_string *lcname = zend_string_tolower(ops->function_name);
-
-		brake = zend_hash_find_ptr(class_table, lcname);
 
-		zend_string_release(lcname);
+	if ((class_table = zend_hash_find_ptr_lc(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], ops->scope->name))) {
+		brake = zend_hash_find_ptr_lc(class_table, ops->function_name);
 	}
 
-	zend_string_release(class_lcname);
 	return brake;
 } /* }}} */
 
diff --git a/sapi/phpdbg/phpdbg_list.c b/sapi/phpdbg/phpdbg_list.c
index 505b04e81f91..3931fc9c1525 100644
--- a/sapi/phpdbg/phpdbg_list.c
+++ b/sapi/phpdbg/phpdbg_list.c
@@ -88,15 +88,12 @@ PHPDBG_LIST(method) /* {{{ */
 
 	if (phpdbg_safe_class_lookup(param->method.class, strlen(param->method.class), &ce) == SUCCESS) {
 		zend_function *function;
-		char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
 
-		if ((function = zend_hash_str_find_ptr(&ce->function_table, lcname, strlen(lcname)))) {
+		if ((function = zend_hash_str_find_ptr_lc(&ce->function_table, param->method.name, strlen(param->method.name)))) {
 			phpdbg_list_function(function);
 		} else {
 			phpdbg_error("Could not find %s::%s", param->method.class, param->method.name);
 		}
-
-		efree(lcname);
 	} else {
 		phpdbg_error("Could not find the class %s", param->method.class);
 	}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.