前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >php的引用类型底层解析

php的引用类型底层解析

作者头像
程序员小饭
发布2020-09-07 15:30:29
3.9K0
发布2020-09-07 15:30:29
举报
文章被收录于专栏:golang+phpgolang+php

我们来先看一段代码

<?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";

(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); echo
b); echo

b; 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"
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档