php7中的引用类型

上一篇文章中,我们看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中的引用类型)