變量作用域決定了變量「在哪裡可見、活多久、誰能改」。PHP 劃分為超全局、全局、局部、靜態四類,本文通過代碼實驗徹底釐清邊界與陷阱。
| 類型 | 定義位置 | 可見範圍 | 生命週期 | 典型代表 |
|---|---|---|---|---|
| 超全局 | PHP 內建 | 腳本任意位置 | 請求結束 | $GLOBALS, $_GET, $_POST, $_SESSION, $_COOKIE, $_SERVER, $_REQUEST, $_ENV, $_FILES |
| 全局 | 函數/類/控制結構之外 | 全域(函數內需 global 或 $GLOBALS) | 腳本結束 | 頂層 $var = 1; |
| 局部 | 函數/類/if/for/while 內部 | 僅當前作用域 | 作用域結束即銷毀 | 函數參數、內部 $x = 1; |
| 靜態 | 函數內 static $v、類內 static $prop | 局部可見,但值保留 | 腳本結束 | function f() { static $c=0; } |
<?php
$a = 0;
function foo() {
global $a;
echo $a; // 0
}
function foo2() {
echo $GLOBALS['a']; // 0
}
foo(); foo2();
差異:global $a 建立別名引用;$GLOBALS['a'] 直接訪問超全局陣列,無需預聲明。
<?php
function normal() {
$v = 0; $v++; return $v;
}
echo normal(); // 1
echo normal(); // 1(每次重新初始化)
function static_var() {
static $v = 0; $v++; return $v;
}
echo static_var(); // 1
echo static_var(); // 2(值跨調用保留)
<?php
class T {
public static $v = 10;
public function a() {
static $var = 10; // 方法級靜態
$var++;
echo $var . "<br>";
}
public static function aa() {
self::$v++;
echo self::$v . "<br>";
}
}
$o1 = new T(); $o1->a(); // 11
$o2 = new T(); $o2->a(); // 12(方法靜態變量跨實例共享!)
T::aa(); // 11
$o1->aa(); // 12
$o2->aa(); // 13(類靜態屬性全域共享)
如上例 a() 中的 static $var,雖非類屬性,但屬於函數級靜態,所有實例調用共享同一記憶體槽。需區分:
static $var 在方法內 → 函數級靜態,跨實例共享self::$prop → 類級靜態,跨實例共享$this->prop → 實例屬性,隔離未 unset 前佔用記憶體至腳本結束,大數據結構請用完即 unset() 或改用靜態緩存模式。
function test($_GET) { } // Fatal Error:不能用超全局名作參數
需要跨函數/文件共享?
├─ 是 → 數據屬於請求上下文(用戶、配置、緩存)?
│ ├─ 是 → $_SESSION / $_REQUEST / 依賴注入容器
│ └─ 否 → 類靜態屬性 / 單例服務 / $GLOBALS(慎用)
└─ 否 → 僅當前函數/方法需要?
├─ 需跨調用保留狀態? → 函數 static 變量
└─ 單次調用即拋棄? → 普通局部變量
declare(strict_types=1); 搭配靜態分析(PHPStan Level 8+)捕獲作用域濫用。徹底理解作用域,是寫出可測試、可維護、無副作用 PHP 代碼的前提。