HashTable 是 PHP 內核的「心臟」——陣列、變量符號表、類屬性、函數表、常量表、資源列表……無一不建立在 HashTable 之上。理解其結構與 API,是進階 PHP 開發者、擴展作者、性能調優者的必修課。本文從數據結構、核心 API、衝突解決、遍歷機制四個維度系統梳理。
hash = hash * 33 + char,速度極快、分佈均勻。key、h (預計算哈希值)、val (指向 zval)、next/prev (同桶鏈表指針)。foreach、reset/next/prev/end 等順序遍歷,也是「陣列有序」的根本原因。typedef struct _Bucket {
ulong h; // 數字鍵或字串鍵哈希值
uint nKeyLength; // 鍵長度,0 表示數字鍵
void *pData; // 指向值 (zval*)
void *pDataPtr; // 指針值 (IS_PTR)
struct _Bucket *pListNext; // 全局鏈表 next
struct _Bucket *pListLast; // 全局鏈表 prev
struct _Bucket *pNext; // 衝突鏈表 next
struct _Bucket *pLast; // 衝突鏈表 prev
char arKey[1]; // 柔性數組存鍵名
} Bucket;
typedef struct _hashtable {
uint nTableSize; // 桶數組大小 (2 的冪)
uint nTableMask; // nTableSize - 1,用於索引計算
uint nNumOfElements; // 當前元素數
ulong nNextFreeElement; // 下一個可用數字索引
Bucket *pInternalPointer; // 內部指針 (foreach 用)
Bucket *pListHead; // 全局鏈表頭
Bucket *pListTail; // 全局鏈表尾
Bucket **arBuckets; // 桶指針數組 (Bucket**)
dtor_func_t pDestructor; // 元素銷毀回調
zend_bool persistent; // 是否持久化 (共享內存)
unsigned char nApplyCount; // 遞歸保護計數
zend_bool bApplyProtection;
} HashTable;
| 類別 | 代表函數 | 用途 |
|---|---|---|
| 初始化/銷毀 | zend_hash_init, zend_hash_destroy, zend_hash_clean | 建立/釋放 HashTable |
| 增/改 | zend_hash_add, zend_hash_update, zend_hash_quick_add, zend_hash_index_update | 插入或覆蓋鍵值對 |
| 查找 | zend_hash_find, zend_hash_quick_find, zend_hash_index_find, zend_hash_exists | 按鍵/索引查找值 |
| 刪除 | zend_hash_del_key_or_index, zend_hash_graceful_destroy | 刪除指定鍵/索引 |
| 遍歷 | zend_hash_internal_pointer_reset_ex, zend_hash_move_forward_ex, zend_hash_get_current_data_ex | 內部指針遍歷 (foreach 底層) |
| 複製/合併 | zend_hash_copy, zend_hash_merge, zend_hash_merge_ex | 陣列合併、符號表繼承 |
| 排序/比較 | zend_hash_sort, zend_hash_compare, zend_hash_minmax | ksort/array_diff 底層 |
| 統計/資訊 | zend_hash_num_elements, zend_hash_get_pointer, zend_hash_display | 調試、序列化 |
// 索引 = h & nTableMask (等價 h % nTableSize,但位運算更快)
index = p->h & ht->nTableMask;
zend_hash_do_resize 擴容(2倍)。zend_hash_init(ht, estimated_size, ...)。雙鏈表設計 同時滿足:
這就是為什麼 PHP 陣列「既是哈希表又是有序列表」的根本原因。
key >= nNextFreeElement,自動更新 nNextFreeElement = key + 1。zend_hash_next_free_element() 返回下一個可用數字索引,支援 $arr[] = value 語法。nApplyCount 防止 zend_hash_apply 回調中修改同一 HashTable 導致無限遞歸,超過限制直接報錯。
| PHP 語言特性 | 底層 HashTable 角色 |
|---|---|
陣列 [] | 單一 HashTable,支援混合鍵 |
全局變量 $GLOBALS | 符號表 EG(symbol_table) |
| 局部變量作用域 | 每個函數/方法一個符號表 EG(active_symbol_table) |
| 類屬性/靜態成員 | 類結構體 default_properties / static_members |
| 函數表/類表/常量表 | EG 中對應 HashTable |
| 資源列表 | EG(regular_list) |