PHP 變量作用域分為:全局作用域(腳本主流程)、局部/函數作用域(函數內部)、超全局作用域($GLOBALS、$_GET 等)。
與 C/C++ 不同,PHP 函數預設不訪問全局變量:
<?php
$a = 1;
function test() {
echo $a; // 無輸出,訪問的是未初始化的局部變量 $a
}
test();
?>
<?php
$a = 1; $b = 2;
// 方式 1:global 關鍵字(引用綁定)
function sum_global() {
global $a, $b;
$b = $a + $b; // 修改全局 $b
}
// 方式 2:$GLOBALS 超全局數組(直接訪問)
function sum_globals() {
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}
?>
static 變量僅在函數局部可見,但生命週期贯穿整個腳本執行:
<?php
function counter() {
static $count = 0; // 僅初始化一次
echo $count++;
}
counter(); // 0
counter(); // 1
counter(); // 2
?>
<?php
function recurse($limit = 10) {
static $depth = 0;
$depth++;
echo "Depth: $depth\n";
if ($depth < $limit) recurse($limit);
$depth--; // 回溯恢復
}
recurse(3);
?>
PHP 4.1.0+ 引入 9 個超全局變量,所有作用域自動可見,無需 global:
Zend Engine 1 (PHP 4) 以引用實現 global/static,導致意外行為:
<?php
function test_ref() {
global $obj;
$obj = &new StdClass; // 引用賦值
}
function test_noref() {
global $obj;
$obj = new StdClass; // 值賦值
}
test_ref();
var_dump($obj); // NULL (!)
test_noref();
var_dump($obj); // object(StdClass)#1 (0) {}
?>
原因:global $obj 建立到全局符號表的引用,$obj = &new 修改了引用指向而非對象本身。
<?php
function &get_ref() {
static $obj;
if (!isset($obj)) $obj = &new StdClass;
$obj->prop++;
return $obj;
}
function &get_noref() {
static $obj;
if (!isset($obj)) $obj = new StdClass;
$obj->prop++;
return $obj;
}
$a = get_ref(); $b = get_ref(); // $a, $b 指向同一對象,prop=2
$c = get_noref(); $d = get_noref(); // $c, $d 指向同一對象,prop=2
// 但若修改靜態變量本身的引用關係,行為不同
?>
靜態變量聲明時只能使用字面量常量,不能使用表達式:
<?php
function foo() {
static $a = 0; // ✅ 正確
static $b = 1 + 2; // ❌ Parse error
static $c = sqrt(4); // ❌ Parse error
}
?>
| 場景 | 推薦方式 | 避坑提醒 |
|---|---|---|
| 函數內讀寫全局配置 | $GLOBALS['config'] | 避免 global 產生隱式引用副作用 |
| 函數內計數/緩存 | static $var = 初始值 | 初始值必須是字面量 |
| 跨文件共享狀態 | 類靜態屬性 / 單例模式 | 優於全局變量,可控性更強 |
| 引用對象賦值 | $obj = new Class() | PHP 5+ 對象默認引用傳遞,無需 & |
PHP 7.1+ 建議使用 依賴注入容器、單例服務類、靜態類屬性 替代全局/靜態變量,實現可測試、可維護的架構:
<?php
// 推薦:服務類封裝狀態
class Config {
private static $instance;
private $data = [];
private function __construct() {}
public static function getInstance() {
return self::$instance ??= new self();
}
public function get($key) { return $this->data[$key] ?? null; }
public function set($key, $val) { $this->data[$key] = $val; }
}
// 使用
Config::getInstance()->set('debug', true);
?>