PHP内核介绍及扩展开发指南—基础知识

2019-04-08 22:59:29王冬梅


  1.1.3 zval状态切换

  当在一个zval上进行的所有赋值操作都是引用或者都是非引用时,一个is_ref就足够应付了。然而,世界总不会那么美好,PHP无法对用户进行这种限制,当我们混合使用引用和非引用赋值时,就必须要进行特别处理了。

  情况I、看如下PHP代码:

<!--p $a = 1; $b = &$a; $c = &$b; $d = $c; // 在一堆引用赋值中,插入一个非引用-->



  全过程如下所示:

  这段代码的前三句将把a、b和c指向一个zval,其is_ref=1, refcount=3;第四句是个非引用赋值,通常情况下只需要增加引用计数即可,然而目标zval属于引用变量,单纯的增加引用计数显然是错误的, Zend的解决办法是为d单独生成一份zval副本。

  全过程如下所示:

1.1.1 参数传递

  PHP函数参数的传递和变量赋值是一样的,非引用传递相当于非引用赋值,引用传递相当于引用赋值,并且也有可能会导致执行zval状态切换。这在后面还将提到。

  1.2 HashTable结构

  HashTable是Zend引擎中最重要、使用最广泛的数据结构,它被用来存储几乎所有的东西。

  1.1.1 数据结构

  HashTable数据结构定义如下:

typedef struct bucket {
ulong h; // 存放hash
uint nKeyLength;
void *pData; // 指向value,是用户数据的副本
void *pDataPtr;
struct bucket *pListNext; // pListNext和pListLast组成
struct bucket *pListLast; // 整个HashTable的双链表
struct bucket *pNext; // pNext和pLast用于组成某个hash对应
struct bucket *pLast; // 的双链表
char arKey[1]; // key
} Bucket;
typedef struct _hashtable {
uint nTableSize;
uint nTableMask;
uint nNumOfElements;
ulong nNextFreeElement;
Bucket *pInternalPointer; /* Used for element traversal */
Bucket *pListHead;
Bucket *pListTail;
Bucket **arBuckets; // hash数组
dtor_func_t pDestructor; // HashTable初始化时指定,销毁Bucket时调用
zend_bool persistent; // 是否采用C的内存分配例程
unsigned char nApplyCount;
zend_bool bApplyProtection;
#if ZEND_DEBUG
int inconsistent;
#endif
} HashTable;

总的来说,Zend的HashTable是一种链表散列,同时也为线性遍历进行了优化,图示如下:


HashTable中包含两种数据结构,一个链表散列和一个双向链表,前者用于进行快速键-值查询,后者方便线性遍历和排序,一个Bucket同时存在于这两个数据结构中。
  关于该数据结构的几点解释:
  l 链表散列中为什么使用双向链表?
相关文章 大家在看