# UNDF: UNDF-2026-000000214 diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -5475,15 +5475,20 @@ static uint32_t zend_get_arg_offset_by_name( if (EXPECTED(*cache_slot == unique_id)) { return *(uintptr_t *)(cache_slot + 1); } - // TODO: Use a hash table? - uint32_t num_args = fbc->common.num_args; - for (uint32_t i = 0; i < num_args; i++) { - const zend_arg_info *arg_info = &fbc->common.arg_info[i]; - if (zend_string_equals(arg_name, arg_info->name)) { - if (...) { - *cache_slot = unique_id; - *(uintptr_t *)(cache_slot + 1) = i; - } - return i; - } - } + /* + * Reuse the compile-time hash built by zend_get_arg_num() if available, + * falling back to linear scan only for internal functions that never go + * through the compile path. O(1) for user functions; O(M) only for + * internal functions on first hit per cache slot. + */ + if (fbc->op_array.arg_name_map) { + zval *zv = zend_hash_find(fbc->op_array.arg_name_map, arg_name); + if (zv) { + uint32_t i = (uint32_t)Z_LVAL_P(zv) - 1; + *cache_slot = unique_id; + *(uintptr_t *)(cache_slot + 1) = i; + return i; + } + } else { + uint32_t num_args = fbc->common.num_args; + for (uint32_t i = 0; i < num_args; i++) { + const zend_arg_info *arg_info = &fbc->common.arg_info[i]; + if (zend_string_equals(arg_name, arg_info->name)) { + *cache_slot = unique_id; + *(uintptr_t *)(cache_slot + 1) = i; + return i; + } + } + }