| 特性 | 值傳遞 | 參考傳遞 |
|---|---|---|
| 機制 | 複製值到新記憶體空間 | 傳遞記憶體位址(指標) |
| 修改影響 | 不影響原變數 | 同步修改原變數 |
| 記憶體開銷 | 較大(複製資料) | 極小(僅複製指標) |
| PHP 預設行為 | 純量、陣列、資源 | 物件(PHP 5+) |
| 語法 | func($var) | func(&$var) |
<?php
// 值傳遞:函式內修改不影響外部
function byValue($a) {
$a = $a + 1;
}
$sample = 1;
byValue($sample);
echo $sample; // 1(未變)
// 參考傳遞:函式內修改直接影響外部
function byRef(&$a) {
$a = $a + 1;
}
$sample = 1;
byRef($sample);
echo $sample; // 2(已變更)
?>
PHP 使用寫入時複製機制優化記憶體:
<?php
$bigArray = range(1, 1000000); // 大陣列
function readOnly($arr) {
// 僅讀取,不修改 → 實際未複製,極快
return count($arr);
}
function modify($arr) {
$arr[0] = 999; // 修改 → 觸發 COW,此時才複製整個陣列
return $arr;
}
// 唯讀:極快,記憶體不增
$start = memory_get_usage();
readOnly($bigArray);
echo "唯讀記憶體增量:" . (memory_get_usage() - $start) . " bytes<br>";
// 修改:複製整個陣列,記憶體激增
$start = memory_get_usage();
modify($bigArray);
echo "修改記憶體增量:" . (memory_get_usage() - $start) . " bytes<br>";
?>
<?php
function changeInt($n) { $n = 10; }
function changeIntRef(&$n) { $n = 10; }
$val = 5;
changeInt($val); echo $val; // 5
changeIntRef($val); echo $val; // 10
?>
<?php
function modifyArray($arr) {
$arr[] = 'new'; // 觸發 COW 複製
}
function modifyArrayRef(&$arr) {
$arr[] = 'new'; // 直接修改原陣列
}
$arr = [1, 2, 3];
modifyArray($arr);
print_r($arr); // [1,2,3] 未變
modifyArrayRef($arr);
print_r($arr); // [1,2,3,'new'] 已變
?>
⚠️ 關鍵誤區:物件預設不是「參考傳遞」,而是「物件控制代碼的值傳遞」。
<?php
class User {
public $name;
public function __construct($name) { $this->name = $name; }
}
function changeProperty(User $user) {
$user->name = 'Modified'; // 修改同一物件的屬性 ✅
}
function replaceObject(User $user) {
$user = new User('Replaced'); // 區域變數指向新物件,外部不受影響 ❌
}
function replaceObjectRef(User &$user) {
$user = new User('Replaced Ref'); // 參考傳遞,外部變數也指向新物件 ✅
}
$u = new User('Original');
changeProperty($u);
echo $u->name; // "Modified" - 同一物件,屬性被改
$u = new User('Original');
replaceObject($u);
echo $u->name; // "Original" - 物件未被替換
$u = new User('Original');
replaceObjectRef($u);
echo $u->name; // "Replaced Ref" - 參考傳遞,變數本身被替換
?>
<?php
function parseData(string $input, array &$result, &$error = null): bool {
// 解析邏輯...
if ($success) {
$result = $parsedData;
return true;
} else {
$error = "解析失敗:$reason";
return false;
}
}
if (parseData($json, $data, $err)) {
// 使用 $data
} else {
logError($err);
}
?>
<?php
function processLargeDataset(array &$data) {
// 直接操作原陣列,避免 COW 複製開銷
foreach ($data as &$row) { // 逐行參考修改
$row['processed'] = true;
}
unset($row); // 解除最後一個參考
}
?>
<?php
class Config {
private static $instance = null;
private $settings = [];
public static function &getInstance(): Config {
if (!self::$instance) {
self::$instance = new self();
}
return self::$instance; // 回傳參考
}
public function &getSettings(): array {
return $this->settings; // 允許直接修改設定陣列
}
}
$config = &Config::getInstance();
$settings = &$config->getSettings();
$settings['debug'] = true; // 直接修改單例內部設定
?>
<?php
$arr = [1, 2, 3];
foreach ($arr as &$val) {
$val *= 2;
}
unset($val); // ⚠️ 必須 unset,否則 $val 仍參考最後一個元素
// 後續錯誤範例
foreach ($arr as $val) { // 無 & 符號
// $val 現在是值複製,但因上一個參考未解除
// 實際上會修改 $arr[2] 的值!
echo $val; // 輸出:2, 4, 4 (最後一個被覆寫)
}
?>
&$varPHP 參考傳遞(&$var)讓函式能直接修改呼叫端變數,適用於多返回值、大資料就地修改、物件替換等場景。但現代 PHP 依賴 Copy-on-Write 使值傳遞在唯讀情況下極高效。核心原則:**預設用值傳遞,有明確修改需求時才用參考,物件屬性修改無需參考,物件變數替換才需參考。**