變數作用域規則
| 範圍 | 說明 | 範例 |
| 全域 | 腳本最外層、include/require 引入檔案 | $a = 1; include 'b.inc'; // $a 在 b.inc 可見 |
| 區域 (函數內) | 函數內部預設為區域作用域 | function test() { echo $a; } // 無輸出,區域 $a 未定義 |
| 靜態 | 函數內 static $var,跨呼叫保持值 | function c() { static $i=0; echo $i++; } |
| 超全域 | $GLOBALS, $_GET, $_POST, $_SESSION... 所有範圍可見 | function f() { echo $GLOBALS['x']; } |
global vs $GLOBALS 差異回顧
<?php
$x = 1;
function test_global() {
global $x; // 建立區域別名指向全域 $x
$x = 2;
}
function test_globals() {
$GLOBALS['x'] = 3; // 直接操作超全域陣列
}
test_global(); // 全域 $x 變為 2
test_globals(); // 全域 $x 變為 3
// unset 差異
function unset_global() {
global $x;
unset($x); // 僅銷毀區域別名
}
function unset_globals() {
unset($GLOBALS['x']); // 真正銷毀全域變數
}
?>
變數引用機制
基本引用指派
<?php
$a = 'ABC';
$b = &$a; // $b 成為 $a 的別名
$b = 'EFG';
echo $a; // EFG(同一記憶體)
// 函數參數傳址
function add100(&$n) { $n += 100; }
$val = 1;
add100($val);
echo $val; // 101
// 函數回傳引用
function &getRef() {
static $v = 0;
return $v;
}
$r = &getRef();
$r = 5; // 修改靜態變數
echo getRef(); // 5
?>
引用計數與 Copy-on-Write (COW)
<?php
$a = 'ABC'; // 記憶體:'ABC' (refcount=1)
$b = $a; // 記憶體:'ABC' (refcount=2) - 共享
$a = 'EFG'; // 觸發 COW:複製 'ABC' 給 $b,$a 指向新 'EFG'
// 現在:$a='EFG', $b='ABC',各自獨立記憶體
?>
物件傳遞行為 (PHP 5+)
預設為「傳遞物件識別符」
<?php
class A { public $val = 'ABC'; }
$obj1 = new A();
$obj2 = $obj1; // 複製識別符,指向同一物件
$obj3 = &$obj1; // 別名,效果相同
$obj4 = clone $obj1; // 深複製,建立新物件
$obj1->val = 'DEF';
echo $obj2->val; // DEF(同一物件)
echo $obj4->val; // ABC(獨立副本)
__clone() 魔術方法自訂複製行為
<?php
class User {
public $name;
private $createdAt;
private $dbConnection; // 不可序列化資源
public function __construct($name) {
$this->name = $name;
$this->createdAt = new DateTime();
$this->dbConnection = new PDO(...);
}
public function __clone() {
// 深複製日期物件
$this->createdAt = clone $this->createdAt;
// 重新建立資料庫連線
$this->dbConnection = new PDO(...);
// 或設為 null 稍後懶載入
}
}
$u1 = new User('Alice');
$u2 = clone $u1; // 自動呼叫 __clone()
取消引用與記憶體管理
<?php
$a = 1;
$b = &$a;
unset($a); // 僅移除 $a 符號,$b 仍保留值 1
var_dump($b); // int(1)
// 循環引用記憶體洩漏範例(PHP 5.3+ 有 GC 可回收)
class Node {
public $parent;
public $children = [];
}
$parent = new Node();
$child = new Node();
$parent->children[] = $child;
$child->parent = $parent; // 循環引用
unset($parent, $child); // PHP 5.3+ 會自動回收,舊版本會洩漏
最佳實踐總結
- 函數參數:預設傳值(COW 效能佳),大型陣列/需修改原值時用
&$param
- 物件:預設傳遞識別符(類似引用),需獨立副本用
clone
- 全域變數:避免使用,改用依賴注入或單例模式
- 靜態變數:函數內
static 保持狀態,類別屬性用 static 共享
- 記憶體優化:大型資料處理完畢及時
unset(),必要時 gc_collect_cycles()