今回は「PHP8の基盤となるカーネルソースコードの解析 - 配列(2)」を紹介します。一定の参考値があるので、困っている友達が参考になれば幸いです。
おすすめ関連記事: 「PHP8の基礎となるカーネルソースコードの解析 - 配列(1) 」 「PHP8の基礎となるカーネルソースコードの解析 - 配列(3) 」 " PHP8 基盤となるカーネルソースコードの解析 - 配列 (4) >>
zend_array は PHP
1.packed array
2.hash で 2 種類に分かれていますarray
在上文中 补齐了zend_array的 所有值的 注释
実際、ソース コード内の順序は上記のものと少し異なります。上記の順序の方が理解しやすいと思います。
//源码里的代码 typedef struct _zend_array HashTable; struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar _unused, zend_uchar nIteratorsCount, zend_uchar _unused2) } v; uint32_t flags; } u; uint32_t nTableMask; Bucket *arData; uint32_t nNumUsed; uint32_t nNumOfElements; uint32_t nTableSize; uint32_t nInternalPointer; zend_long nNextFreeElement; dtor_func_t pDestructor; }; //我调换下顺序后的代码 struct _zend_array { zend_refcounted_h gc; /// gc 占用8个字节 用于引用计数和 字符串类型的记录 union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, // flags 8位的无符号字符, 最大值为255 标记HashTable用 PHP8 中有6个值 zend_uchar _unused, zend_uchar nIteratorsCount, //迭代器计数。foreach语句会在全局变量EG中创建一个迭代器, //迭代器包含正在遍历的HashTable和游标信息。 //nIteratorsCount记录了当前runtime正在迭代当前HashTable的迭代器的数量。 zend_uchar _unused2) } v; //这里有点不一样 看陈雷大佬书中 v结构体还包括 u.v.nApplyCount和u.v.consistency uint32_t flags; // } u; // u是是一个联合体。占用4个字节。 //可以存储一个uint32_t类型的flags,也可以存储由4个unsigned char组成的结构体v, //这里的宏ZEND_ENDIAN_LOHI_4是为了兼容不同操作系统的大小端,可以忽略。 Bucket *arData; //HashTable中存储数据的单元的指针。 // 用来存储key和value以及辅助信息的容器。 uint32_t nTableSize; // HashTable的大小。表示arData指向的bucket数组的大小,即所有bucket的数量。 //该字段取值始终是2n,最小值是8,最大值在64位系统中是0x80000000(2的31次幂)。 uint32_t nNumUsed; //指所有已使用bucket的数量,包括有效bucket和无效bucket的数量 uint32_t nNumOfElements; //有效bucket的数量。该值总是小于或等于nNumUsed uint32_t nTableMask; //索引大小。一般值为 -nTableSize。 uint32_t nInternalPointer; //全局默认游标。reset/key/current/next/prev等宏 和操作都会用到 zend_long nNextFreeElement; //下一个插入的元素的key的下标 //比如 当$a[] = 1 nNextFreeElement =1 dtor_func_t pDestructor; //指向一个函数 typedef void (*dtor_func_t)(zval *pDest); //可以看出是pDest是zval结构指针二级指针, //为什么会是二级指针,因为c语言函数传递都是值传递,要改变指针值只能将指针地址传入 //当bucket元素被更新或者被删除时,会对bucket的value调用该函数, //如果value是引用计数的类型,那么会对value引用计数减1,进而引发可能的gc。 };
メンバー理解ツールによって生成された変数図は次のとおりです
すべての展開後の結果は次のとおりです
#zend_array 構造体のメンバーコアは z_val zend_string zend_refcounted_h であることがわかります。バケットは複数の層を重ねています。バケットには、配列のキー情報が格納されます。typedef struct _Bucket { zval val; //数组的值 ( 复习下 zval只有16个字节) zend_ulong h; // key的 h 值 zend_string *key; //当数组为 hash_array时候 会用到 也就是 key的值 } Bucket;
配列の種類に関係なく、 Packed_array または hash_array の場合、最終的には Bucket
キーがすべて数値キーであり、キーが挿入順序で増加する場合、配列タイプは次のようになります。packed_array##パック配列の特徴
$a =array(1,2,3); // packed array $b =array(1=>'a',3=>'b',5=>'c'); //packed array
$a に対応する zend_array の内容は、
nNumOfElements;有効なバケットの数。
So nNumOfElements nNumused =nTableSizenTableMask; インデックス サイズ。パック配列はインデックスを使用しないため、常に -2
nNextFreeElement; 次に挿入される要素のキーの添え字
パック配列はバケット配列の連続性特性を利用します。デジタル キーのみを使用するシナリオの一部の最適化。インデックス配列は不要になったため、(nTableSize-2)* sizeof(uint32_t) バイトがメモリ空間から節約されます。また、バケットにアクセスすることでバケット配列を直接操作できるため、パフォーマンスも向上します。
パック配列の条件が満たされない場合、配列は PHP では hash_array で表されます。数値以外のすべてのキー値は hash_array で表されます
#そうだね上記の $c は hash_array で表されます
バケットは次のとおりです
#zend_array は次のとおりです
nNumused; 有効なバケットと無効なバケットの数を含む、使用されているすべてのバケットの数を参照します =4
##nNumOfElements; 有効なバケットの数。 =4So nNumOfElements nNumused =nTableSize
nTableMask; インデックス サイズ。 -8
nNextFreeElement; 次に挿入される要素 hash_array のキーの添字は、使用されていない場合は常に 0 になります
▏この記事は、php 中国語 Web サイトで公開されました。元の著者 PHP Cui Xuefeng の同意、元のアドレス: https://zhuanlan.zhihu.com/p/358354087
以上がPHP8 の基礎となるカーネル ソース コードの解析 - 配列 (2)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。