美文网首页PHP开发技巧
php7中的引用类型

php7中的引用类型

作者: 尤旭 | 来源:发表于2019-03-22 16:34 被阅读0次

    上一篇文章中,我们看zval类型中并没有记录引用计数的相关信息,那么php7中在进行&定义的时候是怎么处理的呢?
    我们看下php中的引用类型的结构体定义如下

    struct _zend_reference {
        zend_refcounted_h gc;
        zval              val;
    };
    

    其中zend_refcounted_h结构体为

    typedef struct _zend_refcounted_h {
        uint32_t         refcount;          /* reference counter 32-bit */
        union {
            struct {
                ZEND_ENDIAN_LOHI_3(
                    zend_uchar    type,
                    zend_uchar    flags,    /* used for strings & objects */
                    uint16_t      gc_info)  /* keeps GC root number (or 0) and color */
            } v;
            uint32_t type_info;
        } u;
    } zend_refcounted_h;
    

    _zend_reference的gc中记录了引用计数,php7中的复杂类型的引用计数都会记录在自身结构体中的头部gc中。
    比方说

    $a = "abc";
    $b = $a;
    

    在底层的结构体中,两个zval都是type=6的字符串,他们的str会指向同一个zend_string,而这个zend_string的引用计数会记录成2.
    在谈回今天所说的引用类型,我们来从一段php代码开始

    $a = 1;
    echo $a;
    $b = &$a;
    echo $a;
    echo $b;
    unset($b);
    echo $a;
    echo $b;
    

    大家想想这个结果会是什么?很多人会想,这个结果$a应该也会被一起unsset了,那么我们来gdb看看。先来看看这个$a是个什么类型

    ref1.png
    我们可以很清楚看到当前$avalue.u1.type=4就是对应的IS_LONG类型,那么经过$b=&a操作之后,在看一下$a
    ref2.png
    此时我们可以注意到$au1.type=10那么他对应的类型就是IS_REFERENCE是引用类型,此时$a已经不是一个数字型而是一个引用类型了。当他的type=10的时候,我们来查看他value中的ref
    (gdb) p $2.value.ref
    $3 = (zend_reference *) 0x7ffff5e030a8
    

    是一个zend_reference类型,我们进去看下

    (gdb) p *$2.value.ref
    $4 = {gc = {refcount = 2, u = {v = {type = 10 '\n', flags = 0 '\000', gc_info = 0}, 
          type_info = 10}}, val = {value = {lval = 1, dval = 4.9406564584124654e-324, 
          counted = 0x1, str = 0x1, arr = 0x1, obj = 0x1, res = 0x1, ref = 0x1, ast = 0x1, 
          zv = 0x1, ptr = 0x1, ce = 0x1, func = 0x1, ww = {w1 = 1, w2 = 0}}, u1 = {v = {
            type = 4 '\004', type_flags = 0 '\000', const_flags = 0 '\000', 
            reserved = 0 '\000'}, type_info = 4}, u2 = {next = 7366768, cache_slot = 7366768, 
          lineno = 7366768, num_args = 7366768, fe_pos = 7366768, fe_iter_idx = 7366768, 
          access_flags = 7366768, property_guard = 7366768, extra = 7366768}}}
    

    可以看到,他指向到一个zend_reference结构体的值,他的refcout引用计数为2,并且在val中直接存储了一个zval,这个zval就是我们之前定义的字符数字1。
    接下来看一看$b

    (gdb) p  *z
    $6 = {value = {lval = 140737318498472, dval = 6.9533474157912783e-310, 
        counted = 0x7ffff5e030a8, str = 0x7ffff5e030a8, arr = 0x7ffff5e030a8, 
        obj = 0x7ffff5e030a8, res = 0x7ffff5e030a8, ref = 0x7ffff5e030a8, ast = 0x7ffff5e030a8, 
        zv = 0x7ffff5e030a8, ptr = 0x7ffff5e030a8, ce = 0x7ffff5e030a8, func = 0x7ffff5e030a8, 
        ww = {w1 = 4125110440, w2 = 32767}}, u1 = {v = {type = 10 '\n', type_flags = 4 '\004', 
          const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 1034}, u2 = {next = 0, 
        cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, 
        property_guard = 0, extra = 0}}
    

    我们可以看到这个$b也是一个引用类型,并且看他的res值是0x7ffff5e030a8和我们之前的变量$a是一个逻辑地址,证明他们是指向同一个zend_reference,所以这时候zend_reference里面的refcout等于2的原因。
    此时我们进行了unset($b)的操作,接下来看看$a$b分别是什么样的zval
    看看此时的$a的zval

    $7 = {value = {lval = 140737318498472, dval = 6.9533474157912783e-310, 
        counted = 0x7ffff5e030a8, str = 0x7ffff5e030a8, arr = 0x7ffff5e030a8, 
        obj = 0x7ffff5e030a8, res = 0x7ffff5e030a8, ref = 0x7ffff5e030a8, ast = 0x7ffff5e030a8, 
        zv = 0x7ffff5e030a8, ptr = 0x7ffff5e030a8, ce = 0x7ffff5e030a8, func = 0x7ffff5e030a8, 
        ww = {w1 = 4125110440, w2 = 32767}}, u1 = {v = {type = 10 '\n', type_flags = 4 '\004', 
          const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 1034}, u2 = {next = 0, 
        cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, 
        property_guard = 0, extra = 0}}
    

    依然是一个引用类型,指向了0x7ffff5e030a8,我们进一步看看此时的zend_reference

    (gdb) p *$7.value.ref
    $8 = {gc = {refcount = 1, u = {v = {type = 10 '\n', flags = 0 '\000', gc_info = 0}, 
          type_info = 10}}, val = {value = {lval = 1, dval = 4.9406564584124654e-324, 
          counted = 0x1, str = 0x1, arr = 0x1, obj = 0x1, res = 0x1, ref = 0x1, ast = 0x1, 
          zv = 0x1, ptr = 0x1, ce = 0x1, func = 0x1, ww = {w1 = 1, w2 = 0}}, u1 = {v = {
            type = 4 '\004', type_flags = 0 '\000', const_flags = 0 '\000', 
            reserved = 0 '\000'}, type_info = 4}, u2 = {next = 7366768, cache_slot = 7366768, 
          lineno = 7366768, num_args = 7366768, fe_pos = 7366768, fe_iter_idx = 7366768, 
          access_flags = 7366768, property_guard = 7366768, extra = 7366768}}}
    

    此时的refcount=1了,是因为我们unset($b)了,所以现在只有一个$a指向了这个zend_reference.
    那么我们继续看看此时的$b是什么样

    (gdb) p *z
    $9 = {value = {lval = 140737318498472, dval = 6.9533474157912783e-310, 
        counted = 0x7ffff5e030a8, str = 0x7ffff5e030a8, arr = 0x7ffff5e030a8, 
        obj = 0x7ffff5e030a8, res = 0x7ffff5e030a8, ref = 0x7ffff5e030a8, ast = 0x7ffff5e030a8, 
        zv = 0x7ffff5e030a8, ptr = 0x7ffff5e030a8, ce = 0x7ffff5e030a8, func = 0x7ffff5e030a8, 
        ww = {w1 = 4125110440, w2 = 32767}}, u1 = {v = {type = 0 '\000', type_flags = 0 '\000', 
          const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 0}, u2 = {next = 0, 
        cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, 
        property_guard = 0, extra = 0}}
    

    可以很清晰的看到此时的$bu1.type已经被定义为0就是IS_UNDEF
    所以我们上面那段代码,在unset($b)之后$a依然是1。
    这就是php7中的引用类型,理解了他,就很好理解刚才那段php代码了。

    相关文章

      网友评论

        本文标题:php7中的引用类型

        本文链接:https://www.haomeiwen.com/subject/nrrrvqtx.html