php的引用類型底層解析

我們來先看一段代碼

<code>
$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/<code>

為什麼會輸出這樣的結果呢?我們來分析一下
首先我們看一下引用類型的結構

<code>struct _zend_reference {
zend_refcounted_h gc;
zval val;
};/<code>

我們可以看到,引用類型是一個變量zval和一個zend_refcounted_h組成
先看第一段的
$a = "string";
$b = &$a;
echo $a;
echo $b;
執行過程

<code>(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"
//打印出字符串/<code>

接下來我們看看
$b = "hello";
echo $b;
echo $a;

<code>(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"
打印字符串/<code>

接下來我們再來看看

unset(b);echo b;

echo $a;

<code>(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"
/<code>

轉載地址:https://www.jianshu.com/p/ead3c22f0951

侵聯繫刪


分享到:


相關文章: