解析PHP8底层内核源码-数组(四)

广告:宝塔服务器面板,一键全能部署及管理,送你10850元礼包,点我领取~~~

解析PHP8底层内核源码-数组(四)

本篇文章给大家介绍《解析PHP8底层内核源码-数组(四)》。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

相关文章推荐:《解析PHP8底层内核源码-数组(一)》《解析PHP8底层内核源码-数组(二)》《解析PHP8底层内核源码-数组(三)》

在Runningprocess 里已经知道代码需要经过词法分析 语法分析 编译 执行 四大步骤

《深入解析PHP底层之Running process》

PHP 8会在编译阶段(将AST抽象语法树编译成opcode时)就创建一个数组常量。这个数组常量和数字常量、字符串常量一样,是在编译阶段就确定并分配内存的。因此数组的初始化发生在编译阶段。

PHP的数组初始化代码 部分如下

//如果开启zend_debug #if !ZEND_DEBUG && defined(HAVE_BUILTIN_CONSTANT_P)# define zend_new_array(size) \(__builtin_constant_p(size) ? \((((uint32_t)(size)) <= HT_MIN_SIZE) ? \_zend_new_array_0() \//走 _zend_new_array_0: \_zend_new_array((size)) \) \: \_zend_new_array((size)) \)#else//没有开启 也就是一般模式 走 _zend_new_array# define zend_new_array(size) \_zend_new_array(size)#endifZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent){_zend_hash_init_int(ht, nSize, pDestructor, persistent);}ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_0(void){        //分配内存空间HashTable *ht = emalloc(sizeof(HashTable));         //初始化_zend_hash_init_int(ht, HT_MIN_SIZE, ZVAL_PTR_DTOR, 0);return ht;}//初始化方法static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent){GC_SET_REFCOUNT(ht, 1);GC_TYPE_INFO(ht) = GC_ARRAY | (persistent ? ((GC_PERSISTENT|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT) : 0);HT_FLAGS(ht) = HASH_FLAG_UNINITIALIZED;ht->nTableMask = HT_MIN_MASK;HT_SET_DATA_ADDR(ht, &uninitialized_bucket);ht->nNumUsed = 0;ht->nNumOfElements = 0;ht->nInternalPointer = 0;ht->nNextFreeElement = ZEND_LONG_MIN;ht->pDestructor = pDestructor;ht->nTableSize = zend_hash_check_size(nSize);}//初始化 bucket 也就是 ardataZEND_API void ZEND_FASTCALL zend_hash_real_init(HashTable *ht, zend_bool packed){IS_CONSISTENT(ht);HT_ASSERT_RC1(ht);//调用 zend_hash_real_init_ex方法zend_hash_real_init_ex(ht, packed);}//zend_hash_real_init_ex方法static zend_always_inline void zend_hash_real_init_ex(HashTable *ht, bool packed){HT_ASSERT_RC1(ht);ZEND_ASSERT(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED);if (packed) {//如果是packed_array zend_hash_real_init_packed_ex(ht);} else {//如果是 hash_arrayzend_hash_real_init_mixed_ex(ht);}}//paced_array 初始化bucket 的代码static zend_always_inline void zend_hash_real_init_packed_ex(HashTable *ht){void *data;if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {data = pemalloc(HT_SIZE_EX(ht->nTableSize, HT_MIN_MASK), 1);} else if (EXPECTED(ht->nTableSize == HT_MIN_SIZE)) {data = emalloc(HT_SIZE_EX(HT_MIN_SIZE, HT_MIN_MASK));} else {data = emalloc(HT_SIZE_EX(ht->nTableSize, HT_MIN_MASK));}HT_SET_DATA_ADDR(ht, data);/* Don't overwrite iterator count. */ht->u.v.flags = HASH_FLAG_PACKED | HASH_FLAG_STATIC_KEYS;HT_HASH_RESET_PACKED(ht);}//hash_array 初始化bucket的代码static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht){void *data;uint32_t nSize = ht->nTableSize;if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), 1);} else if (EXPECTED(nSize == HT_MIN_SIZE)) {data = emalloc(HT_SIZE_EX(HT_MIN_SIZE, HT_SIZE_TO_MASK(HT_MIN_SIZE)));ht->nTableMask = HT_SIZE_TO_MASK(HT_MIN_SIZE);HT_SET_DATA_ADDR(ht, data);/* Don't overwrite iterator count. */ht->u.v.flags = HASH_FLAG_STATIC_KEYS;#ifdef __SSE2__do {__m128i xmm0 = _mm_setzero_si128();xmm0 = _mm_cmpeq_epi8(xmm0, xmm0);_mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  0), xmm0);_mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  4), xmm0);_mm_storeu_si128((__m128i*)&HT_HASH_EX(data,  8), xmm0);_mm_storeu_si128((__m128i*)&HT_HASH_EX(data, 12), xmm0);} while (0);#elif defined(__aarch64__)do {int32x4_t t = vdupq_n_s32(-1);vst1q_s32((int32_t*)&HT_HASH_EX(data,  0), t);vst1q_s32((int32_t*)&HT_HASH_EX(data,  4), t);vst1q_s32((int32_t*)&HT_HASH_EX(data,  8), t);vst1q_s32((int32_t*)&HT_HASH_EX(data, 12), t);} while (0);#elseHT_HASH_EX(data,  0) = -1;HT_HASH_EX(data,  1) = -1;HT_HASH_EX(data,  2) = -1;HT_HASH_EX(data,  3) = -1;HT_HASH_EX(data,  4) = -1;HT_HASH_EX(data,  5) = -1;HT_HASH_EX(data,  6) = -1;HT_HASH_EX(data,  7) = -1;HT_HASH_EX(data,  8) = -1;HT_HASH_EX(data,  9) = -1;HT_HASH_EX(data, 10) = -1;HT_HASH_EX(data, 11) = -1;HT_HASH_EX(data, 12) = -1;HT_HASH_EX(data, 13) = -1;HT_HASH_EX(data, 14) = -1;HT_HASH_EX(data, 15) = -1;#endifreturn;} else {data = emalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)));}ht->nTableMask = HT_SIZE_TO_MASK(nSize);HT_SET_DATA_ADDR(ht, data);HT_FLAGS(ht) = HASH_FLAG_STATIC_KEYS;HT_HASH_RESET(ht);}//数组赋值和更新值static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, zend_ulong h, zval *pData, uint32_t flag){uint32_t nIndex;uint32_t idx;Bucket *p;IS_CONSISTENT(ht);HT_ASSERT_RC1(ht);if ((flag & HASH_ADD_NEXT) && h == ZEND_LONG_MIN) {h = 0;}if (HT_FLAGS(ht) & HASH_FLAG_PACKED) {if (h < ht->nNumUsed) {p = ht->arData + h;if (Z_TYPE(p->val) != IS_UNDEF) {replace:if (flag & HASH_ADD) {return NULL;}if (ht->pDestructor) {ht->pDestructor(&p->val);}ZVAL_COPY_VALUE(&p->val, pData);return &p->val;} else { /* we have to keep the order :( */goto convert_to_hash;}} else if (EXPECTED(h < ht->nTableSize)) {add_to_packed:p = ht->arData + h;/* incremental initialization of empty Buckets */if ((flag & (HASH_ADD_NEW|HASH_ADD_NEXT)) != (HASH_ADD_NEW|HASH_ADD_NEXT)) {if (h > ht->nNumUsed) {Bucket *q = ht->arData + ht->nNumUsed;while (q != p) {ZVAL_UNDEF(&q->val);q++;}}}ht->nNextFreeElement = ht->nNumUsed = h + 1;goto add;} else if ((h >> 1) < ht->nTableSize &&           (ht->nTableSize >> 1) < ht->nNumOfElements) {zend_hash_packed_grow(ht);goto add_to_packed;} else {if (ht->nNumUsed >= ht->nTableSize) {ht->nTableSize += ht->nTableSize;}convert_to_hash:zend_hash_packed_to_hash(ht);}} else if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {if (h < ht->nTableSize) {zend_hash_real_init_packed_ex(ht);goto add_to_packed;}zend_hash_real_init_mixed(ht);} else {if ((flag & HASH_ADD_NEW) == 0 || ZEND_DEBUG) {p = zend_hash_index_find_bucket(ht, h);if (p) {ZEND_ASSERT((flag & HASH_ADD_NEW) == 0);goto replace;}}ZEND_HASH_IF_FULL_DO_RESIZE(ht);/* If the Hash table is full, resize it */}idx = ht->nNumUsed++;nIndex = h | ht->nTableMask;p = ht->arData + idx;Z_NEXT(p->val) = HT_HASH(ht, nIndex);HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);if ((zend_long)h >= ht->nNextFreeElement) {ht->nNextFreeElement = (zend_long)h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX;}add:ht->nNumOfElements++;p->h = h;p->key = NULL;ZVAL_COPY_VALUE(&p->val, pData);return &p->val;}
登录后复制

_zend_hash_init_int 流程图如下

_zend_hash_init_int方法流程图(初始化hash)
zend_hash_real_init_ex方法流程图(初始化 bucket)

在PHP 8中,数组的初始化其实是分两步的。

第1步:分配HashTable结构体内存

第2步: 初始化HashTable结构体各个字段

第3步:分配bucket数组内存,修改一些字段值。

对于第3步,并不是每次都进行。比如像“$a = array()”这种写法,由于数组为空,PHP 不会额外申请bucket数组内存。而对于“$a = array(1, 2, 3)”这种写法,由于数组非空,因此PHP 需要执行第3步 分配bucket数组内存,修改一些字段值。

zend_hash_real_init_packed_ex(当为packed_array 时候 bucket的初始化流程图)
zend_hash_real_init_mixed_ex 初始化为hash_array bucket的流程图

▏本文经原作者PHP崔雪峰同意,发布在9543建站博客,原文地址:https://zhuanlan.zhihu.com/p/361006441

以上就是解析PHP8底层内核源码-数组(四)的详细内容,更多请关注9543建站博客其它相关文章!

9543建站博客
一个专注于网站开发、微信开发的技术类纯净博客。
作者头像
admin创始人

肥猫,知名SEO博客站长,14年SEO经验。

上一篇:利用 Opcache 扩展提升 PHP7 性能
下一篇:实用PHP时间函数:strtotime()

发表评论

关闭广告
关闭广告