• 技术文章 >后端开发 >PHP8

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

    藏色散人藏色散人2021-06-10 15:08:48转载55

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

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

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

    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)
    #endif
    ZEND_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 也就是 ardata
    ZEND_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_array
    zend_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);
    #else
    HT_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;
    #endif
    return;
    } 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 流程图如下

    9e4318b0a2a99ee1ff050211d4f64d5.png
    _zend_hash_init_int方法流程图(初始化hash)
    f6d86a080461c28dc5f0da4c856953c.png
    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数组内存,修改一些字段值。

    8fcf4068aa8f6068f6b78272871cdfe.png


    87cdbb5e505c6a9df2ad1db03d94ce7.png
    zend_hash_real_init_packed_ex(当为packed_array 时候 bucket的初始化流程图)
    f3b38199fc994c24018eb5e0b7e04b9.png
    zend_hash_real_init_mixed_ex 初始化为hash_array bucket的流程图

    ▏本文经原作者PHP崔雪峰同意,发布在php中文网,原文地址:https://zhuanlan.zhihu.com/p/361006441

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

    声明:本文转载于:PHP崔雪峰,如有侵犯,请联系admin@php.cn删除
    专题推荐:PHP8 数组
    上一篇:解析PHP8底层内核源码-数组(三) 下一篇:没有了
    第16期线上培训班

    相关文章推荐

    • 聊聊PHP8的一些语法新特性• 解析PHP8底层内核源码-数组(一)• 解析PHP8底层内核源码-数组(二)• 深入解析PHP8底层内核源码之SAPI(一)• 解析PHP8底层内核源码-数组(三)

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网