前言
去年我參加了很多次會議,其中八次會議里我進行了相關發言,這其中我多次談到了 PHP 的引用問題,因為很多人對它的理解有所偏差。在深入討論這個問題之前,我們先回顧一下引用的基本概念,明確什么是“引用傳遞”。
在 PHP 中引用意味著用不同的名字訪問同一個變量內容,不論你用哪個名字對變量做出了運算,其他名字訪問的內容也將改變。
讓我們通過代碼來加深對此的理解。 首先我們寫幾個簡單的語句,把一個變量賦值給另一個變量,并且改變另一個變量:
<?php$a = 23;$b = $a;$b = 42;var_dump($a); // int(23)var_dump($b); // int(42)
這個腳本顯示 $a 值仍然為 23 ,而 $b 則等于 42 。出現這個情況的原因是我們得到的是一個拷貝(具體發生了什么稍后講解。。。)現在我們使用引用來做同樣的事情:
<?php$a = 23;$b = &$a;$b = 42;var_dump($a); // int(42)var_dump($b); // int(42)?>
現在 $a 的值也改變成了 42 。 事實上,$a 和 $b 之間沒有任何區別,它們都使用了同一個變量容器(又名: zval )。 將這兩者分開的唯一方法是使用 unset() 函數銷毀其中任何一個變量。
在 PHP 中,引用不僅能用在普通語句中,還能用于函數參數和返回值:
<?phpfunction &foo(&$param) { $param = 42; return $param;}$a = 23;echo "/$a before calling foo(): $a/n";$b = foo($a);echo "/$a after the call to foo(): $a/n";$b = 23;echo "/$a after touching the returned variable: $a/n";?>
你認為上面的結果是什么呢?―― 沒錯,就像下面這樣:
$a before calling foo(): 23$a after the call to foo(): 42$a after touching the returned variable: 42
這里我們初始化了一個變量,并把它作為一個引用參數傳給了一個函數。函數改變了它,它有了新值。該函數返回同一個變量,我們更改了返回的變量和它的原始值。。。 等等!它沒變,不是嗎?。?―― 沒錯,可引用就是這樣。 具體發生了如下事情:該函數返回了一個引用,引用了 $a 的變量容器 zval,并且通過 = 賦值操作符為它創建了一個副本。
為了修復這個問題,我們需要添加一個額外的 & 操作符:
$b = &foo($a);
結果和我們所期望的一樣:
$a before calling foo(): 23$a after the call to foo(): 42$a after touching the returned value: 23
總結一下: PHP 的引用就是同一個變量的別名,想要正確的使用它們可能很難。想要詳細了解引用計數,這里有份基礎資料,請參閱 手冊中的引用計數基本知識 。
PHP 5 發布時最大的變動是『對象處理方式』。一般我們理解為:
在 PHP 4 中,對象被當成變量來對待,所以當對象作為函數傳參時,他們是被復制的。但在 PHP 5 中,他們永遠是『引用傳參』。
以上的理解并不完全正確。其主要目的是遵循『面對對象模式』:對象傳參給函數或者方法后,這個函數發送一個指令給對象(例如調用了一個方法)以此來改變對象的狀態(例如對象的屬性)。因此傳參進去的對象必須為同一個。 PHP 4 的面對對象用戶使用『引用傳參』來解決這個問題,不過很難做到完美。PHP 5 引進了獨立于變量容器的『對象存儲器』。當一個對象賦值給變量時,變量不再存儲整個對象(屬性表和其他的『類』信息),而是存儲這個對象所在 存儲器的引用 ―― 當我們復制一個對象變量時,我們復制的是這個『存儲器的引用』。這很容易被誤解為『引用』,但是『存儲器的引用』與『引用』是完全不同的概念。下面的示例代碼有助于我們更好地區分:
新聞熱點
疑難解答