美文网首页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中的引用类型

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

  • php7引用计数

    1. 什么是引用计数 在《php7 zval及变量存储方式》的2.3节中我们说到,对于复杂类型的变量(string...

  • php7的写时复制机制

    1. 什么是写时复制 在《php7引用计数》的文章中,我们知道,对于复制类型的变量,在赋值时,我们并没有重新复制一...

  • Android NDK 9 JNI 数据类型和方法调用

    一、基本类型 二、引用类型性 JNI 中的引用类型主要包括: 类; 对象; 数组。 和 Java 中的引用类型的对...

  • 面试题笔记

    Java中引用类型的区别,具体的使用场景 Java中引用类型分为四类:强引用、软引用、弱引用、虚引用。 强引用:强...

  • 引用类型对象拷贝

    引用类型有哪些?非引用类型有哪些 引用类型引用类型(对象、数组、函数、正则):指的是那些保存在堆内存中的对象,变量...

  • 引用类型、对象拷贝

    1.引用类型有哪些?非引用类型有哪些 引用类型引用类型(对象、数组、函数、正则):指的是那些保存在堆内存中的对象,...

  • Go 学习之路:引用类型与值类型

    Golang中只有三种引用类型:slice(切片)、map(字典)、channel(管道); 引用类型 引用类型理...

  • Go 学习:引用类型与值类型

    Golang中只有三种引用类型:slice(切片)、map(字典)、channel(管道); 引用类型 引用类型理...

  • 高性能PHP7【笔记】

    一、搭建环境 二、PHP7新特性 A.OOP特性 1.类型声明 PHP7支持的形参类型声明的类型有整型、浮点型、字...

网友评论

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

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