PHP 參考(Reference)讓多個名稱指向同一份記憶體內容,但與 C 指標本質不同。本文系統整理 8 大使用場景,並深入剖析 Zend 引擎的「寫時複製」優化機制。
<?php
$a = 'ABC';
$b = &$a; // $b 成為 $a 別名
$b = 'EFG';
echo $a; // EFG($a 同步改變)
?>
<?php
function add100(&$n) { $n += 100; }
$b = 1;
add100($b);
echo $b; // 101
// 注意:呼叫時不加 &,PHP 5.3+ 已棄用 call-time pass-by-reference
// call_user_func_array 需顯式傳參考
call_user_func_array('add100', [&$b]);
?>
<?php
function &getCounter() {
static $c = 0;
return $c++;
}
$a = getCounter(); // 普通賦值,得到 0
$a = 5; // 不影響靜態 $c
$a = &getCounter(); // 參考回傳,$a 與 $c 綁定
$a = 5; // $c 變為 5
?>
實務上常用於物件方法回傳內部屬性參考,如 public function &getData() { return $this->data; }。
<?php
class A { public $v = 'ABC'; }
$b = new A;
$c = $b; // PHP 5+ 此處為參考複製(handle 複製)
$b->v = 'DEF';
echo $c->v; // DEF
// 若需真正獨立副本,請用 clone
$d = clone $b;
$b->v = 'GHI';
echo $d->v; // DEF(不受影響)
?>
PHP 4 需用 $b =& new A 才達到相同效果;PHP 5 new 自動回傳參考,=& 會觸發 E_STRICT。
&,Zend 引擎「寫時複製」會自動共享記憶體;只有函式內會修改陣列時才加 &,否則反而觸發複製、降低效能。&。$var = null 解除綁定,配合 GC 回收。unset()unset($a) 僅切斷 $a 與內容的綁定,內容本身若仍有其他參考(如 $b)則不會銷毀。
global 與 $GLOBALS 參考關係<?php
$var = 'global';
function test() {
global $var; // 等同 $var =& $GLOBALS['var']
$var = 'changed'; // 影響全域
}
test();
echo $var; // changed
?>
函式內 global $var; $var =& $local; 只改變區域綁定,不影響全域;若要影響全域請用 $GLOBALS['var'] =& $local;。
<?php
$a = range('a', 'z'); // 大陣列
$b = $a; // 此時 $a、$b 指向同一記憶體(zval 引用計數 +1)
function readOnly($arr) { echo count($arr); }
readOnly($a); // 唯讀傳值 → 無複製,極快
function modify(&$arr) { $arr[] = 'new'; }
modify($a); // 參考傳遞 → 函式可能寫入 → Zend 為 $b 複製一份
?>
結論:錯誤濫用 & 反而導致不必要的記憶體複製,效能下降 30%+。預設傳值、唯讀場景不加 &、需修改原變數時才加,是最佳實踐。