PHP 引用(在變量/函數/對象前加 &)意味著不同名稱訪問同一內容。這與 C 指針本質不同:
<?php
$a = "ABC";
$b =& $a; // $b 是 $a 的別名
echo $a; // ABC
echo $b; // ABC
$b = "EFG"; // 修改 $b
echo $a; // EFG (受影響)
echo $b; // EFG
?>
內存模型:初始時 `$a`、`$b` 指向同一 zval 結構體(引用計數 refcount=2)。發生寫操作時,Zend 為 `$b` 分離新 zval(分離前 refcount>1 觸發複製)。
<?php
function test(&$a) {
$a += 100;
}
$b = 1;
echo $b; // 1
test($b); // 傳遞別名,無需在調用處加 &
echo $b; // 101
?>
注意:test(1) 會報錯,因為字面量無法綁定引用。call_user_func_array 等回調函數傳參需顯式 &$var。
<?php
function &test() {
static $b = 0;
$b++;
echo $b . "\n";
return $b;
}
$a = test(); // 輸出 1,賦值拷貝
$a = 5;
$a = test(); // 輸出 2,仍是拷貝
$a =& test(); // 輸出 3,綁定引用
$a = 5; // 修改靜態變量 $b 為 5
$a = test(); // 輸出 6 ($b 已變為 5,+1=6)
?>
規則:只有 $a =& test() 才建立引用綁定,普通調用返回值拷貝。常用於對象鏈式操作或內部狀態暴露。
<?php
class A { public $abc = "ABC"; }
$b = new A;
$c = $b; // PHP5+ 默認引用賦值
echo $b->abc; // ABC
echo $c->abc; // ABC
$b->abc = "DEF";
echo $c->abc; // DEF (受影響)
?>
版本差異:
$b =& new A 實現引用new 自動返回引用,$c = $b 等同 $c =& $bclone:$c = clone $b<?php
class Foo {
protected $name;
public function __construct($str) { $this->name = $str; }
public function __toString() { return 'my name is "'.$this->name.'"'; }
public function setName($str) { $this->name = $str; }
}
class MasterOne { protected $foo; public function __construct($f) { $this->foo = $f; } public function setFooName($str) { $this->foo->setName($str); } }
class MasterTwo { protected $foo; public function __construct($f) { $this->foo = $f; } public function setFooName($str) { $this->foo->setName($str); } }
$bar = new Foo('bar');
$baz =& $bar; // 別名
$m1 = new MasterOne($bar);
$m2 = new MasterTwo($bar);
$bar->setName('baz'); // 影響 $baz, $m1->foo, $m2->foo
$m2->setFooName('MasterTwo\'s Foo'); // 同樣影響所有引用者
?>
輸出驗證:所有持有者看到的都是同一對象的最新狀態。
$a = range('a', 'n'); // 大數組
$b = $a; // 未寫入,共享內存 (refcount=2)
function printArray($arr) { echo count($arr); }
printArray($a); // 值傳遞,Zend 認為可能寫入,觸發分離複製 => $b 獨佔內存
// 若改為 printArray(&$arr) 引用傳遞,同樣觸發分離
// 正確做法:只讀場景用值傳遞,避免不必要的內存分配
foreach ($arr as &$v) 結束後務必 unset($v) 解除引用,防止污染後續循環$var =& $GLOBALS['var']; // 等價於 global $var;
function test() {
global $var;
$var =& $GLOBALS['other']; // 僅改變局部引用綁定
$GLOBALS['var'] =& $other; // 修改全局綁定
}
對象方法中 $this 永遠是調用對象的引用,不可重新綁定。
$a = 1; $b =& $a; unset($a); // $b 仍存在,值為 1
// unset 僅斷開變量名與內容綁定,不銷毀內容(refcount>0時)
| 場景 | 建議 | 理由 |
|---|---|---|
| 函數需修改外部變量 | ✅ 使用引用參數 | 避免大數據拷貝,語義明確 |
| 大數組只讀傳遞 | ❌ 禁止引用 | 觸發不必要的 COW 分離 |
| 對象方法鏈式調用 | ✅ 返回 $this | PHP5+ 默認對象引用行為 |
| foreach 修改元素 | ✅ as &$v + unset($v) | 直接操作原數組值 |
| 閉包捕獲外部變量 | ✅ use (&$var) | 需修改外部變量時 |
深入理解 PHP 引用機制,是編寫高性能、低 Bug 代碼的關鍵。記住:引用是為了解決「共享修改」問題,而非優化手段。