上一篇文章中,我们看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
是个什么类型
我们可以很清楚看到当前
$a
的value.u1.type=4
就是对应的IS_LONG
类型,那么经过$b=&a
操作之后,在看一下$a
ref2.png
此时我们可以注意到
$a
的u1.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}}
可以很清晰的看到此时的$b
的u1.type
已经被定义为0
就是IS_UNDEF
所以我们上面那段代码,在unset($b)
之后$a
依然是1。
这就是php7中的引用类型,理解了他,就很好理解刚才那段php代码了。
网友评论