美文网首页PHP实战PHP经验分享
php的引用类型底层解析

php的引用类型底层解析

作者: 程序员小饭 | 来源:发表于2020-01-29 15:24 被阅读0次

    我们来先看一段代码

    <?php
    $a = "string";
    $b = &$a;
    echo $a;
    echo $b;
    
    $b = "hello";
    echo $b;
    echo $a;
    
    unset($b);
    echo $b;
    echo $a;
    
    ?>
    输出结果为
    string
    string
    hello
    hello
    (空)
    hello
    

    为什么会输出这样的结果呢?我们来分析一下
    首先我们看一下引用类型的结构

    struct _zend_reference {
        zend_refcounted_h gc;
        zval              val;
    };
    

    我们可以看到,引用类型是一个变量zval和一个zend_refcounted_h组成
    先看第一段的
    $a = "string";
    $b = &$a;
    echo $a;
    echo $b;
    执行过程

    (gdb) p *z
    $1 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
        res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
          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}}
    
    //我们可以看到$a的u1的type为10,所以说明$a已经是引用类型了,对应的内存地址为0x7ffff5a020a8
    
    (gdb) p *$1.value.ref
    $2 = {gc = {refcount = 2, u = {v = {type = 10 '\n', flags = 0 '\000', gc_info = 0}, type_info = 10}}, val = {value = {lval = 17733632, dval = 8.7615783471909966e-317,
          counted = 0x10e9800, str = 0x10e9800, arr = 0x10e9800, obj = 0x10e9800, res = 0x10e9800, ref = 0x10e9800, ast = 0x10e9800, zv = 0x10e9800, ptr = 0x10e9800, ce = 0x10e9800,
          func = 0x10e9800, ww = {w1 = 17733632, w2 = 0}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {
          next = 28776, cache_slot = 28776, lineno = 28776, num_args = 28776, fe_pos = 28776, fe_iter_idx = 28776, access_flags = 28776, property_guard = 28776, extra = 28776}}}
    
    //我们可以看到在$a的引用内部 是由gc和val组成,而且val就是一个zval,对应的type是6,字符串类型
    
    
    (gdb) p *$1.value.ref.val.value.str
    $3 = {gc = {refcount = 1, u = {v = {type = 6 '\006', flags = 7 '\a', gc_info = 0}, type_info = 1798}}, h = 9223378990886268924, len = 6, val = "s"}
    (gdb) p *$1.value.ref.val.value.str.val@6
    $4 = "string"
    //对应的打印出ref中的str类型的字符串
    
    
    (gdb) p z
    $5 = (zval *) 0x7ffff5a14090
    (gdb) p *z
    $6 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
        res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
          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的u1的type为10,所以说明$b已经是引用类型了,对应的内存地址为0x7ffff5a020a8   和$a共用一个地址
    
    (gdb) p $6.value.ref
    $7 = (zend_reference *) 0x7ffff5a020a8
    (gdb) p *$6.value.ref
    $8 = {gc = {refcount = 2, u = {v = {type = 10 '\n', flags = 0 '\000', gc_info = 0}, type_info = 10}}, val = {value = {lval = 17733632, dval = 8.7615783471909966e-317,
          counted = 0x10e9800, str = 0x10e9800, arr = 0x10e9800, obj = 0x10e9800, res = 0x10e9800, ref = 0x10e9800, ast = 0x10e9800, zv = 0x10e9800, ptr = 0x10e9800, ce = 0x10e9800,
          func = 0x10e9800, ww = {w1 = 17733632, w2 = 0}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {
          next = 28776, cache_slot = 28776, lineno = 28776, num_args = 28776, fe_pos = 28776, fe_iter_idx = 28776, access_flags = 28776, property_guard = 28776, extra = 28776}}}
    //$b中的ref也是由gc和zval组成,而且对应的zval中的u1的type为6,是字符串类型
    
    (gdb) p *$6.value.ref.val.value.str
    $9 = {gc = {refcount = 1, u = {v = {type = 6 '\006', flags = 7 '\a', gc_info = 0}, type_info = 1798}}, h = 9223378990886268924, len = 6, val = "s"}
    (gdb) p *$6.value.ref.val.value.str.val@6
    $10 = "string"
    //打印出字符串
    

    接下来我们看看
    $b = "hello";
    echo $b;
    echo $a;

    (gdb) p *z
    $11 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
        res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
          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的u1的type为10,所以说明$b已经是引用类型了,对应的内存地址为0x7ffff5a020a8 
    
    (gdb) p *$11.value.ref
    $12 = {gc = {refcount = 2, u = {v = {type = 10 '\n', flags = 0 '\000', gc_info = 0}, type_info = 10}}, val = {value = {lval = 140737314679872, dval = 6.9533472271273708e-310,
          counted = 0x7ffff5a5ec40, str = 0x7ffff5a5ec40, arr = 0x7ffff5a5ec40, obj = 0x7ffff5a5ec40, res = 0x7ffff5a5ec40, ref = 0x7ffff5a5ec40, ast = 0x7ffff5a5ec40,
          zv = 0x7ffff5a5ec40, ptr = 0x7ffff5a5ec40, ce = 0x7ffff5a5ec40, func = 0x7ffff5a5ec40, ww = {w1 = 4121291840, w2 = 32767}}, u1 = {v = {type = 6 '\006',
            type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 28776, cache_slot = 28776, lineno = 28776, num_args = 28776,
          fe_pos = 28776, fe_iter_idx = 28776, access_flags = 28776, property_guard = 28776, extra = 28776}}}
    //$b中的ref是由gc和zval组成,而且对应的zval中的u1的type为6,是字符串类型
    (gdb) p *$11.value.ref.val.value.str
    $13 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372247569412249, len = 5, val = "h"}
    (gdb) p *$11.value.ref.val.value.str.val@5
    $14 = "hello"
    //打印出对应的字符串
    
    
    (gdb) p *z
    $15 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
        res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
          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}}
    
    //我们可以看到$a的u1的type为10,所以说明$a已经是引用类型了,对应的内存地址为0x7ffff5a020a8 和b一样
    
    (gdb) p *$15.value.ref
    $16 = {gc = {refcount = 2, u = {v = {type = 10 '\n', flags = 0 '\000', gc_info = 0}, type_info = 10}}, val = {value = {lval = 140737314679872, dval = 6.9533472271273708e-310,
          counted = 0x7ffff5a5ec40, str = 0x7ffff5a5ec40, arr = 0x7ffff5a5ec40, obj = 0x7ffff5a5ec40, res = 0x7ffff5a5ec40, ref = 0x7ffff5a5ec40, ast = 0x7ffff5a5ec40,
          zv = 0x7ffff5a5ec40, ptr = 0x7ffff5a5ec40, ce = 0x7ffff5a5ec40, func = 0x7ffff5a5ec40, ww = {w1 = 4121291840, w2 = 32767}}, u1 = {v = {type = 6 '\006',
            type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 28776, cache_slot = 28776, lineno = 28776, num_args = 28776,
          fe_pos = 28776, fe_iter_idx = 28776, access_flags = 28776, property_guard = 28776, extra = 28776}}}
    //$a中的ref是由gc和zval组成,而且对应的zval中的u1的type为6,是字符串类型
    (gdb) p *$15.value.ref.val.value.str
    $17 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372247569412249, len = 5, val = "h"}
    (gdb) p *$15.value.ref.val.value.str.val@5
    $18 = "hello"
    打印字符串
    

    接下来我们再来看看
    unset(b); echob;
    echo $a;

    (gdb) p *z
    $1 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
        res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
          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}}
    //大家其实可以看到 在unset($b)的操作过程中,仅仅是把b中的u1的type改为了0,为null类型,其余的地址等信息都未改变,所以对应的$a是不会有任何改变的
    
    所以后面在打印$a的过程中,一切都是正常的,以下为$a的打印过程
    (gdb) p *z
    $2 = {value = {lval = 140737314300072, dval = 6.9533472083627576e-310, counted = 0x7ffff5a020a8, str = 0x7ffff5a020a8, arr = 0x7ffff5a020a8, obj = 0x7ffff5a020a8,
        res = 0x7ffff5a020a8, ref = 0x7ffff5a020a8, ast = 0x7ffff5a020a8, zv = 0x7ffff5a020a8, ptr = 0x7ffff5a020a8, ce = 0x7ffff5a020a8, func = 0x7ffff5a020a8, ww = {w1 = 4120912040,
          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}}
    (gdb) p *$2.value.ref
    $3 = {gc = {refcount = 1, u = {v = {type = 10 '\n', flags = 0 '\000', gc_info = 0}, type_info = 10}}, val = {value = {lval = 140737314679872, dval = 6.9533472271273708e-310,
          counted = 0x7ffff5a5ec40, str = 0x7ffff5a5ec40, arr = 0x7ffff5a5ec40, obj = 0x7ffff5a5ec40, res = 0x7ffff5a5ec40, ref = 0x7ffff5a5ec40, ast = 0x7ffff5a5ec40,
          zv = 0x7ffff5a5ec40, ptr = 0x7ffff5a5ec40, ce = 0x7ffff5a5ec40, func = 0x7ffff5a5ec40, ww = {w1 = 4121291840, w2 = 32767}}, u1 = {v = {type = 6 '\006',
            type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 28776, cache_slot = 28776, lineno = 28776, num_args = 28776,
          fe_pos = 28776, fe_iter_idx = 28776, access_flags = 28776, property_guard = 28776, extra = 28776}}}
    (gdb) p *$2.value.ref.val.value.str
    $4 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372247569412249, len = 5, val = "h"}
    (gdb) p *$2.value.ref.val.value.str.val@5
    $5 = "hello"
    

    相关文章

      网友评论

        本文标题:php的引用类型底层解析

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