# UNDF: UNDF-2026-000000213 diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3753,13 +3753,30 @@ static uint32_t zend_get_arg_num(const zend_function *fn, const zend_string *ar { - // TODO: Caching? - for (uint32_t i = 0; i < fn->common.num_args; i++) { - zend_arg_info *arg_info = &fn->op_array.arg_info[i]; - if (zend_string_equals(arg_info->name, arg_name)) { - return i + 1; - } - } - - /* Either an invalid argument name, or collected into a variadic argument. */ - return (uint32_t) -1; + /* + * Build a HashTable from arg_name -> (1-based position) on first call, + * cache it on the zend_op_array. Subsequent calls are O(1) lookups. + * Replaces O(M) linear scan — was O(N*M) for N named args, M params. + */ + if (!fn->op_array.arg_name_map) { + HashTable *ht = emalloc(sizeof(HashTable)); + zend_hash_init(ht, fn->common.num_args, NULL, NULL, 0); + for (uint32_t i = 0; i < fn->common.num_args; i++) { + zend_arg_info *arg_info = &fn->op_array.arg_info[i]; + zval pos; + ZVAL_LONG(&pos, i + 1); + zend_hash_add(ht, arg_info->name, &pos); + } + ((zend_op_array *)&fn->op_array)->arg_name_map = ht; + } + + zval *zv = zend_hash_find(fn->op_array.arg_name_map, arg_name); + if (zv) { + return (uint32_t)Z_LVAL_P(zv); + } + return (uint32_t) -1; }