php引用計數與變量引用
每個php5.5變量都存儲在一個叫做zval的變量容器中。
一個zval變量容器,除了包含變量的類型與值外,還包含兩個字節的額外信息:
1、第一個是“is_ref”,是個bool型,用來標識這個變量是否屬於引用集合(reference set),若屬於則其值為1,否則為0。
有個這個變量php引擎就能夠將普通變量與引用變量區分開來。
2、第二個是“refcount”,用來表示指向這個zval變量(符號)的個數。每個符號都有作用域(scope),那些主腳本和函數或者方法也都有作用域。
所有的符號都存在一個符號表中。
當一個變量被賦值一個常量值時,就會生成一個zval變量容器,如下例:
<?php
$a = "Hello world";
?>
這個時候執行以下程序得到$a變量指向zval容器中的is_ref與refcount值
<?php
$a = "Hello world";
print_r(xdebug_debug_zval('a'));
?>
a: (refcount=1, is_ref=0)='Hello world'
下面,我們進行如下實驗,來探討引用賦值與普通賦值。
首先,使$b指向$a,查看is_ref、 refcount,如下:
<?php
$a = "Hello world";
$b = $a;
print_r(xdebug_debug_zval('a'));
print_r(xdebug_debug_zval('b'));
?>
a: (refcount=2, is_ref=0)='Hello world'
b: (refcount=2, is_ref=0)='Hello world'
讓$b引用$a,查看is_ref refcount,如下
<?php
$a = "Hello world";
$b = &$a;
print_r(xdebug_debug_zval('a'));
print_r(xdebug_debug_zval('b'));
?>
a: (refcount=2, is_ref=1)='Hello world'
b: (refcount=2, is_ref=1)='Hello world'
從上我們可以分析出,當有變量引用相應zval容器時,is_ref為1。
我們進一步分析,我們把$b 引用$a,$c指向$a,如下
<?php
$a = "Hello world";
$b = &$a;
$c = $a;
print_r(xdebug_debug_zval('a'));
print_r(xdebug_debug_zval('b'));
print_r(xdebug_debug_zval('c'));
?>
打印結果如下
a: (refcount=2, is_ref=1)='Hello world'
b: (refcount=2, is_ref=1)='Hello world'
c: (refcount=1, is_ref=0)='Hello world'
可見,這個時候php5.5引擎為$c重新建立了一個zval容器,容器中的數據類型、值與$a指向的容器中的完全相同,不同的是其refcount與is_ref的值。
因此,我們可以看出,php5.5的zval容器中的is_ref變量要麼標識引用集合,要麼標識普通集合,當兩者都有時,他將克隆zval容器,來解決沖突問題。
總結:
1、在php5.5以後,“變量賦值”都是指向賦值,即將某個變量指向特定的zval容器。
2、“變量引用”則是將變量與變量進行綁定,若綁定的變量中有一個變量改變了指向,則相互綁定的其他變量的指向也隨著改變。
若變量重新引用變量,則其原來的變量綁定解除,轉而綁定新的變量。如下代碼:
<?php
function foo(&$var)
{
$var =& $GLOBALS["baz"];
}
foo($bar);
?>
這將使 foo 函數中的 $var 變量在函數調用時和 $bar 綁定在一起,但接著又被重新綁定到了 $GLOBALS["baz"] 上面。不可能通過引用機制將 $bar 在函數調用范圍內綁定到別的變量上面,因為在函數 foo 中並沒有變量$bar(它被表示為 $var,但是 $var 只有變量內容而沒有調用符號表中的名字到值的綁定)。可以使用引用返回來引用被函數選擇的變量。