首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 擴展開發:HashTable 進階操作 — 遍歷、析構、排序與極值查找

     編輯:PHP 相關知識

前言

HashTable 是 Zend Engine 核心數據結構,支撐 PHP 數組、對象屬性、符號表等。上一節介紹了基本增刪改查,本文深入講解遍歷回調、內部指針迭代、析構銷毀、比較排序、極值查找等高級 API,並給出 C 代碼實現模式。

回調遍歷:zend_hash_apply 系列

基本形式:無參數回調

typedef int (*apply_func_t)(void *pDest TSRMLS_DC);
void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC);

回調返回值控制流程:

常量含義PHP 等價
ZEND_HASH_APPLY_KEEP繼續下一個元素continue
ZEND_HASH_APPLY_STOP立即停止遍歷break
ZEND_HASH_APPLY_REMOVE刪除當前元素並繼續unset($arr[$key]); continue;

帶參數回調:zend_hash_apply_with_argument

typedef int (*apply_func_arg_t)(void *pDest, void *argument TSRMLS_DC);
void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t func, void *data TSRMLS_DC);

帶鍵值回調:zend_hash_apply_with_arguments (罕用)

typedef int (*apply_func_args_t)(void *pDest, int num_args, va_list args, zend_hash_key *hash_key);
void zend_hash_apply_with_arguments(HashTable *ht, apply_func_args_t func, int num_args, ...);

完整示例:打印 HashTable 所有鍵值

// 回調:接收 zval** 與 hash_key
int php_sample_print_zval_and_key(zval **val, int num_args, va_list args, zend_hash_key *hash_key) {
    TSRMLS_FETCH();
    // 複製 zval 避免破壞原數據
    zval tmp = **val;
    zval_copy_ctor(&tmp);
    INIT_PZVAL(&tmp);
    convert_to_string(&tmp);

    php_printf("Key: ");
    if (hash_key->nKeyLength) {
        // 字符串鍵
        PHPWRITE(hash_key->arKey, hash_key->nKeyLength);
    } else {
        // 數字鍵
        php_printf("%lu", hash_key->h);
    }
    php_printf(" => Value: ");
    PHPWRITE(Z_STRVAL(tmp), Z_STRLEN(tmp));
    php_printf("\n");

    zval_dtor(&tmp);
    return ZEND_HASH_APPLY_KEEP;
}

// 調用
zend_hash_apply_with_arguments(ht, php_sample_print_zval_and_key, 0);

關鍵點:判斷鍵類型用 nKeyLength 而非 arKey,因內核可能在 arKey 留有髒數據;空字符串鍵 nKeyLength=1 (含結束符)。

內部指針迭代:無回調遍歷

對應 PHP 用戶空間 reset/key/current/next/prev/end 族函數:

PHP 函數Zend API
reset()zend_hash_internal_pointer_reset(ht)
key()zend_hash_get_current_key_ex(ht, &str, &len, &idx, dup, pos)
current()zend_hash_get_current_data(ht, (void**)&data)
next()zend_hash_move_forward(ht)
prev()zend_hash_move_backwards(ht)
end()zend_hash_internal_pointer_end(ht)
each()move_forward + get_current_key + get_current_data

標準迭代模板

void php_sample_iterate_hash(HashTable *ht) {
    for (zend_hash_internal_pointer_reset(ht);
         zend_hash_has_more_elements(ht) == SUCCESS;
         zend_hash_move_forward(ht)) {

        char *key; uint key_len; ulong idx; int type;
        zval **ppzval;

        // 獲取鍵類型與值
        type = zend_hash_get_current_key_ex(ht, &key, &key_len, &idx, 0, NULL);
        if (zend_hash_get_current_data(ht, (void**)&ppzval) == FAILURE) continue;

        // 複製並轉字符串輸出
        zval tmp = **ppzval;
        zval_copy_ctor(&tmp);
        INIT_PZVAL(&tmp);
        convert_to_string(&tmp);

        php_printf("Key: ");
        if (type == HASH_KEY_IS_STRING) {
            PHPWRITE(key, key_len);
        } else {
            php_printf("%lu", idx);
        }
        php_printf(" => ");
        PHPWRITE(Z_STRVAL(tmp), Z_STRLEN(tmp));
        php_printf("\n");

        zval_dtor(&tmp);
    }
}

zend_hash_get_current_key_ex 返回值

  • HASH_KEY_IS_STRING:字符串鍵,arKey/nKeyLength 有效。
  • HASH_KEY_IS_LONG:數字鍵,h 有效。
  • HASH_KEY_NON_EXISTANT:指針越界(已到末尾)。

析構與銷毀

刪除單個元素

// 字符串鍵
zend_hash_del(ht, arKey, nKeyLen);
// 數字鍵
zend_hash_index_del(ht, h);

返回 SUCCESS/FAILURE。刪除時會調用 HashTable 初始化時指定的析構函數 (如 ZVAL_PTR_DTOR)。

清空與銷毀整個 HashTable

// 僅清空元素,保留 HashTable 結構
zend_hash_clean(ht);

// 完全銷毀:clean + 釋放 buckets 數組 + 釋放 HashTable 結構體
zend_hash_destroy(ht);

HashTable 生命周期完整示例

int sample_strvec_handler(int argc, char **argv TSRMLS_DC) {
    HashTable *ht;
    ALLOC_HASHTABLE(ht);                          // 1. 分配結構體
    if (zend_hash_init(ht, argc, NULL, ZVAL_PTR_DTOR, 0) == FAILURE) { // 2. 初始化
        FREE_HASHTABLE(ht);
        return FAILURE;
    }

    // 3. 插入數據
    while (argc--) {
        zval *val;
        MAKE_STD_ZVAL(val);
        ZVAL_STRING(val, argv[argc], 1);
        if (zend_hash_next_index_insert(ht, (void**)&val, sizeof(zval*)) == FAILURE) {
            zval_ptr_dtor(&val);  // 插入失敗需手動銷毀 zval
        }
    }

    // 4. 業務處理
    process_hashtable(ht);

    // 5. 銷毀
    zend_hash_destroy(ht);  // 調用析構函數銷毀所有 zval,釋放 buckets
    FREE_HASHTABLE(ht);     // 釋放 HashTable 結構體
    return SUCCESS;
}

比較、排序與極值

自定義比較函數

typedef int (*compare_func_t)(void *a, void *b TSRMLS_DC);
// 返回 -1 (ab),同 qsort

查找極值:zend_hash_minmax

int zend_hash_minmax(HashTable *ht, compare_func_t compar, int flag, void **pData TSRMLS_DC);
// flag=0 返回最小值,flag=1 返回最大值,結果存入 pData

示例:查找函數表中字母序首尾的函數名

int fname_compare(zend_function *a, zend_function *b TSRMLS_DC) {
    return strcasecmp(a->common.function_name, b->common.function_name);
}

void php_sample_funcname_sort(TSRMLS_D) {
    zend_function *fe;
    if (zend_hash_minmax(EG(function_table), fname_compare, 0, (void**)&fe) == SUCCESS) {
        php_printf("Min function: %s\n", fe->common.function_name);
    }
    if (zend_hash_minmax(EG(function_table), fname_compare, 1, (void**)&fe) == SUCCESS) {
        php_printf("Max function: %s\n", fe->common.function_name);
    }
}

兩個 HashTable 比較:zend_hash_compare

int zend_hash_compare(HashTable *hta, HashTable *htb,
                      compare_func_t compar, zend_bool ordered TSRMLS_DC);
// 先比元素個數,再逐元素調用 compar 比較
// ordered=1 要求順序一致,0 則無序比較(集合語義)

排序:zend_hash_sort

typedef void (*sort_func_t)(void **buckets, size_t count, size_t size, compare_func_t comp TSRMLS_DC);
int zend_hash_sort(HashTable *ht, sort_func_t sort_func, compare_func_t compar, int renumber TSRMLS_DC);
  • sort_func:通常傳 zend_qsort (快速排序實現)。
  • renumber=1:排序後重建數字索引 (0,1,2...),丟棄原鍵;0 保持原鍵不變。

按 zval 值排序數組示例

int array_data_compare(zval **a, zval **b TSRMLS_DC) {
    // 簡單數值比較,實際需處理類型轉換
    return Z_LVAL_PP(a) - Z_LVAL_PP(b);
}

zend_hash_sort(target_hash, zend_qsort, array_data_compare, 1 TSRMLS_CC);

總結

  • 遍歷首選回調 API:zend_hash_apply_with_argument 平衡靈活性與性能。
  • 內部指針迭代:適合需要精確控制步進、雙向遍歷的場景。
  • 生命周期管理:ALLOC_HASHTABLE → init → 操作 → destroy → FREE,嚴格配對。
  • 排序/比較:提供 compare_func_t,配合 zend_qsort 實現高效排序。
  • 極值查找:zend_hash_minmax 避免全量遍歷排序的開銷。

掌握這些 API,能在擴展中高效操作 PHP 數組、符號表、對象屬性等核心結構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved