这个函数功能就是将关联数组展开成这样的变量:$key = val
先用zend_hash_internal_pointer_reset_ex获取头,然后遍历,获取每个元素值zend_hash_get_current_data_ex和键zend_hash_get_current_key_ex,extract有第二参数用来约束已有变量名情况下的各种处理方式,所以还需要判断是否已有同名变量:
zend_hash_exists(EG(active_symbol_table), var_name, var_name_len + 1);
最后注册变量:ZVAL_STRINGL(&final_name, var_name, var_name_len, 1);
值得关注的是这个函数:zend_hash_exists,核心代码如下:
h = zend_inline_hash_func(arKey, nKeyLength);
nIndex = h & ht->nTableMask;
p = ht->arBuckets[nIndex];
while (p != NULL) {
if (p->arKey == arKey ||
((p->h == h) && (p->nKeyLength == nKeyLength) && !memcmp(p->arKey, arKey, nKeyLength))) {
return 1;
}
p = p->pNext;
}
这里又调用了zend_inline_hash_func函数(在zend_hash.h头文件里),这个函数说实话真没看明白,黑夜路人有篇文章有详细解释,实际就是一种hash算法,实现这么一个公式hash(i) = hash(i-1) * 33 + str[i],解释一下,php版本是这么写的:(hash << 5) + hash,左移5实际上等于成2的5次方也就是32,再加一次,就等于hash*33,最后返回的是一个相对合理的一个哈希值,这个值再和哈希表的nTableMask做与运算(看样nTableMask是个重要的掩码,其值待研究)。接着用这个运算结果作为下标去获取ht->arBuckets[nIndex],有点明白了,arBuckets里存的是相同哈希(和掩码与后的结果)值的数组,相当于将数组元素按照哈希算法归类。while循环里通过bucket p->pNext遍历查找相同arKey的元素。
至此,终于明白bucket结构里pListNext、pListLast和pNext、pLast属性的区别,前者用于顺序遍历,和元素插入先后顺序有关,后者用于按照key查找,其关联关系是根据key生成的哈希值分组,如果有相同哈希值的key,这时pNext和pLast就有作用了,再做链表关联相同哈希值的key。
回头再看zend_inline_hash_func函数,因为其返回的数值是和key的长度密切相关的,key越长散列的范围就越大,相同的哈希结果就越少,按key查找也就越快,是不是可以这样理解?
补充一下路人加了注释的hashtable和bucket结构
typedef struct _hashtable {
uint nTableSize; /* 散列表大小, Hash值的区间 */
uint nTableMask; /* 等于nTableSize -1, 用于快速定位 */
uint nNumOfElements; /* HashTable中实际元素的个数 */
ulong nNextFreeElement; /* 下个空闲可用位置的数字索引 */
Bucket *pInternalPointer; /* 内部位置指针, 会被reset, current这些遍历函数使用 */
Bucket *pListHead; /* 头元素, 用于线性遍历 */
Bucket *pListTail; /* 尾元素, 用于线性遍历 */
Bucket **arBuckets; /* 实际的存储容器 */
dtor_func_t pDestructor;/* 元素的析构函数(指针) */
zend_bool persistent;
unsigned char nApplyCount; /* 循环遍历保护 */
zend_bool bApplyProtection;
#if ZEND_DEBUG
int inconsistent;
#endif
} HashTable;
typedef struct bucket {
ulong h; /* 数字索引/hash值 */
uint nKeyLength; /* 字符索引的长度 */
void *pData; /* 数据 */
void *pDataPtr; /* 数据指针 */
struct bucket *pListNext; /* 下一个元素, 用于线性遍历 */
struct bucket *pListLast; /* 上一个元素, 用于线性遍历 */
struct bucket *pNext; /* 处于同一个拉链中的下一个元素 */
struct bucket *pLast; /* 处于同一拉链中的上一个元素 */
char arKey[1]; /* 节省内存,方便初始化的技巧 */
} Bucket;