賦值運算子(=)是 PHP 程式中最頻繁使用的運算子,但其行為細節(運算式值、複合賦值、傳值 vs 引用、物件賦值語意)常被忽略。本文系統整理賦值運算子完整語法、記憶體模型與實務陷阱。
<?php
$a = 3; // 將 3 賦予 $a,整個運算式值為 3
echo $a = 5; // 輸出 5,且 $a 變為 5
// 串聯賦值:從右向左結合
$a = $b = $c = 10; // $c=10, $b=10, $a=10
?>
<?php
$a = ($b = 4) + 5; // $b=4,運算式值 4,+5 = 9 賦予 $a
// $a=9, $b=4
?>
| 運算子 | 名稱 | 等價展開 | 範例 |
|---|---|---|---|
+= | 加法賦值 | $a = $a + $b | $a=10; $a+=5; // 15 |
-= | 減法賦值 | $a = $a - $b | $a=10; $a-=3; // 7 |
*= | 乘法賦值 | $a = $a * $b | $a=10; $a*=2; // 20 |
/= | 除法賦值 | $a = $a / $b | $a=10; $a/=2; // 5.0 |
%= | 取模賦值 | $a = $a % $b | $a=10; $a%=3; // 1 |
.= | 字串串接賦值 | $a = $a . $b | $a='Hi'; $a.='!'; // 'Hi!' |
&= | 位元 AND 賦值 | $a = $a & $b | $a=6; $a&=3; // 2 |
|= | 位元 OR 賦值 | $a = $a | $b | $a=4; $a|=3; // 7 |
^= | 位元 XOR 賦值 | $a = $a ^ $b | $a=5; $a^=3; // 6 |
<<= | 左移賦值 | $a = $a << $b | $a=1; $a<<=2; // 4 |
>>= | 右移賦值 | $a = $a >> $b | $a=8; $a>>=1; // 4 |
<?php
// 字串累積(效能優於反覆串接新字串)
$sql = 'SELECT * FROM users';
$sql .= ' WHERE active = 1';
$sql .= ' ORDER BY created_at DESC';
// 位元旗標:權限管理
class Permission {
const READ = 1; // 0b001
const WRITE = 2; // 0b010
const EXEC = 4; // 0b100
}
$userPerm = Permission::READ;
$userPerm |= Permission::WRITE; // 加入寫入權限 0b011 = 3
if ($userPerm & Permission::READ) { /* 可讀 */ }
$userPerm ^= Permission::WRITE; // 移除寫入(切換)
?>
<?php
$a = 10;
$b = $a; // 複製值
$b = 20;
echo $a; // 10(互不影響)
// 陣列亦為傳值複製(Copy-on-Write 優化)
$arr1 = [1,2,3];
$arr2 = $arr1;
$arr2[0] = 99;
var_dump($arr1); // [1,2,3] 未變
var_dump($arr2); // [99,2,3]
?>
&<?php
$a = 10;
$b = &$a; // $b 參考 $a 同一記憶體位址
$b = 20;
echo $a; // 20(同步變化)
// 陣列引用
$arr1 = [1,2,3];
$arr2 = &$arr1;
$arr2[0] = 99;
var_dump($arr1); // [99,2,3] 同步變化
?>
PHP 5 起,物件變數儲存的是「物件識別碼」(handle),而非物件本體。
<?php
class Foo { public $val = 1; }
$obj1 = new Foo();
$obj2 = $obj1; // 複製 handle,指向同一物件
$obj2->val = 2;
echo $obj1->val; // 2(影響同一物件)
$obj3 = new Foo();
$obj1 = $obj3; // $obj1 指向新物件,$obj2 仍指向舊物件
echo $obj2->val; // 2
// 真正複製物件:使用 clone
$obj4 = clone $obj1;
$obj4->val = 99;
echo $obj1->val; // 2(互不影響)
?>
<?php
$arr = [1,2,3];
foreach ($arr as $v) { $v *= 2; }
var_dump($arr); // [1,2,3] 原陣列未變
?>
&$v(可修改原陣列)<?php
$arr = [1,2,3];
foreach ($arr as &$v) { $v *= 2; }
unset($v); // 重要:解除參考,避免後續誤用
var_dump($arr); // [2,4,6]
?>
$k => &$v<?php
$arr = ['a'=>1, 'b'=>2];
foreach ($arr as $k => &$v) {
$v = strtoupper($k) . $v;
}
unset($v);
var_dump($arr); // ['a'=>'A1', 'b'=>'B2']
?>
| 錯誤模式 | 問題 | 修正 |
|---|---|---|
if ($a = 5) | 賦值而非比較,永遠為 true | if ($a == 5) 或 if (5 === $a)(Yoda 條件) |
foreach 後未 unset($v) | $v 保留最後元素參考,後續操作污染陣列 | 習慣加上 unset($v) |
函式參數預期引用未加 & | 函式內修改不影響外部 | 函式簽名 function(&$var),呼叫端 func($var) |
| 大量陣列複製導致記憶體飆升 | Copy-on-Write 機制下寫入才複製,但大陣列仍需謹慎 | 傳遞引用、使用產生器、或資料庫分頁 |
賦值運算子是 PHP 程式的基石,深入理解「運算式有值」、「複合賦值語法糖」、「傳值/引用/物件 handle 三種記憶體語意」與「foreach 參考陷阱」,能寫出記憶體效率高、行為可預測、無副作用驚喜的程式碼。