PHP 陣列、全域/區域變數符號表、物件屬性、函數表、類別表……全部建構在同一套 HashTable 之上。理解其結構,等於掌握 PHP 運行時的核心資料結構。
DJBX33A (Times 33),將字串鍵映射為無符號長整數。Bucket* 指標的桶陣列,大小為 2 的冪次,動態擴容。Bucket,解決衝突。Bucket 依插入順序串聯成雙向鏈表,支援 foreach、reset/next/prev/end 等順序操作。typedef struct _hashtable {
uint nTableSize; // 桶陣列大小
uint nTableMask; // nTableSize - 1,用於索引計算
uint nNumOfElements; // 元素個數
ulong nNextFreeElement; // 下一個數字索引
Bucket *pInternalPointer; // 內部指標(foreach 用)
Bucket *pListHead; // 全域鏈表頭
Bucket *pListTail; // 全域鏈表尾
Bucket **arBuckets; // 桶陣列指標
// ... 擴容/銷毀/複製 等回調函式指標
} HashTable;
typedef struct bucket {
ulong h; // 雜湊值
uint nKeyLength; // 鍵長度(0 表示數字索引)
void *pData; // 值指標
void *pDataPtr; // 指標值優化
struct bucket *pListNext, *pListLast; // 全域鏈表指標
struct bucket *pNext, *pLast; // 衝突鏈表指標
char arKey[1]; // 柔性陣列儲存字串鍵
} Bucket;
| 分類 | 代表函式 |
|---|---|
| 初始化/銷毀 | _zend_hash_init, zend_hash_destroy, zend_hash_clean, zend_hash_graceful_destroy |
| 增/改 | _zend_hash_add_or_update, _zend_hash_quick_add_or_update, _zend_hash_index_update_or_next_insert |
| 刪除 | zend_hash_del_key_or_index |
| 查找 | zend_hash_find, zend_hash_quick_find, zend_hash_index_find, zend_hash_exists |
| 遍歷 | zend_hash_internal_pointer_reset_ex, zend_hash_move_forward_ex, zend_hash_get_current_data_ex, zend_hash_get_current_key_ex |
| 複製/合併 | zend_hash_copy, _zend_hash_merge, zend_hash_merge_ex |
| 排序/比較 | zend_hash_sort, zend_hash_compare, zend_hash_minmax |
| 應用/回調 | zend_hash_apply, zend_hash_apply_with_argument(s), zend_hash_reverse_apply |
| 指標管理 | zend_hash_get_pointer, zend_hash_set_pointer |
| 除錯 | zend_hash_display, zend_hash_display_pListTail |
pNext/pLast 解決雜湊衝突,查找時只遍歷同桶鏈表,平均 O(1)。pListNext/pListLast 維護插入順序,foreach 直接走此鏈表,無需排序。鍵長為 0 時視為數字索引,直接用 nNextFreeElement 分配,跳過雜湊計算,極快。
負載因子超過閾值時,zend_hash_do_resize 分配 2x 桶陣列,重新雜湊所有元素(Rehash),並重建衝突鏈。pListHead/Tail 不變,遍歷順序不受影響。
zend_hash_* API 操作符號表、屬性表,而非直接存取結構體。zend_hash_internal_pointer_reset_ex + zend_hash_move_forward_ex,支援巢狀 foreach。zend_hash_apply 配合 ZEND_HASH_APPLY_REMOVE。