首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 引用(&)完整指南:變量、函數、對象與性能深度解析

     編輯:PHP 相關知識

核心概念:PHP 引用不是指針

PHP 引用(在變量/函數/對象前加 &)意味著不同名稱訪問同一內容。這與 C 指針本質不同:

  • C 指針存儲內存地址,可進行指針運算
  • PHP 引用是符號表別名,由 Zend 引擎實現「寫時複製」(Copy-on-Write)

1. 變量引用

<?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 觸發複製)。

2. 函數引用傳參(傳址調用)

<?php
function test(&$a) {
    $a += 100;
}
$b = 1;
echo $b; // 1
test($b);  // 傳遞別名,無需在調用處加 &
echo $b; // 101
?>

注意:test(1) 會報錯,因為字面量無法綁定引用。call_user_func_array 等回調函數傳參需顯式 &$var。

3. 函數引用返回

<?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() 才建立引用綁定,普通調用返回值拷貝。常用於對象鏈式操作或內部狀態暴露。

4. 對象引用(PHP 5+ 行為變化)

<?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 (受影響)
?>

版本差異:

  • PHP 4:對象賦值為拷貝,需 $b =& new A 實現引用
  • PHP 5+:new 自動返回引用,$c = $b 等同 $c =& $b
  • 需獨立副本時使用 clone:$c = clone $b

5. 實戰案例:多對象共享狀態

<?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'); // 同樣影響所有引用者
?>

輸出驗證:所有持有者看到的都是同一對象的最新狀態。

6. 引用的性能影響與最佳實踐

寫時複製(COW)機制

$a = range('a', 'n'); // 大數組
$b = $a;                // 未寫入,共享內存 (refcount=2)
function printArray($arr) { echo count($arr); }
printArray($a);         // 值傳遞,Zend 認為可能寫入,觸發分離複製 => $b 獨佔內存
// 若改為 printArray(&$arr) 引用傳遞,同樣觸發分離
// 正確做法:只讀場景用值傳遞,避免不必要的內存分配

性能測試結論

  • 錯誤使用引用(如只讀卻強制引用)導致性能下降 30%+
  • 大數組/對象在「確需修改原值」時才用引用傳參
  • 循環中 foreach ($arr as &$v) 結束後務必 unset($v) 解除引用,防止污染後續循環

7. 進階特殊引用

global 引用

$var =& $GLOBALS['var']; // 等價於 global $var;
function test() {
    global $var;
    $var =& $GLOBALS['other']; // 僅改變局部引用綁定
    $GLOBALS['var'] =& $other; // 修改全局綁定
}

$this 引用

對象方法中 $this 永遠是調用對象的引用,不可重新綁定。

取消引用

$a = 1; $b =& $a; unset($a); // $b 仍存在,值為 1
// unset 僅斷開變量名與內容綁定,不銷毀內容(refcount>0時)

總結:何時使用引用?

場景建議理由
函數需修改外部變量✅ 使用引用參數避免大數據拷貝,語義明確
大數組只讀傳遞❌ 禁止引用觸發不必要的 COW 分離
對象方法鏈式調用✅ 返回 $thisPHP5+ 默認對象引用行為
foreach 修改元素✅ as &$v + unset($v)直接操作原數組值
閉包捕獲外部變量✅ use (&$var)需修改外部變量時

深入理解 PHP 引用機制,是編寫高性能、低 Bug 代碼的關鍵。記住:引用是為了解決「共享修改」問題,而非優化手段。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved