首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP變數作用域完全解析:區域、全域、超全域與靜態變數

     編輯:PHP 相關知識

變數作用域四大類型

類型宣告位置可見範圍生命週期關鍵字
區域變數函數/方法內部僅限該函數內部函數執行期間無
全域變數函數/類外部 (腳本頂層)全腳本 (需 global 關鍵字引入函數)腳本執行期間global
靜態變數函數內部僅限該函數內部腳本執行期間 (保持值)static
超全域變數PHP 內建任何位置直接可用請求期間無 (預定義)

區域變數:函數的私有空間

<?php
$x = 10; // 全域變數

function demo() {
    $x = 20; // 區域變數,與外部 $x 無關
    echo "函數內: $x\n"; // 20
}

demo();
echo "函數外: $x\n"; // 10 (未被修改)

// 參數也是區域變數
function add($a, $b) { // $a, $b 為區域變數
    return $a + $b;
}

全域變數:跨函數共享狀態

global 關鍵字引入

<?php
$counter = 0;

function increment() {
    global $counter; // 引入全域變數參考
    $counter++;
    echo "計數器: $counter\n";
}

increment(); // 1
increment(); // 2
echo "最終: $counter\n"; // 2

$GLOBALS 超全域陣列 (替代方案)

<?php
$config = ['debug' => true, 'version' => '1.0'];

function getConfig(string $key) {
    // 直接存取 $GLOBALS,不需 global 關鍵字
    return $GLOBALS['config'][$key] ?? null;
}

echo getConfig('debug'); // 1 (true)
echo getConfig('version'); // 1.0

⚠️ global vs $GLOBALS 差異

<?php
$var = 1;

function testGlobal() {
    global $var;
    $var = 2; // 修改全域變數
}

function testGlobals() {
    $GLOBALS['var'] = 3; // 修改全域變數
}

testGlobal();
echo $var; // 2

testGlobals();
echo $var; // 3

// 關鍵差異:global 建立別名,$GLOBALS 直接操作陣列元素
function testReference() {
    global $var;
    $var = &$GLOBALS['var']; // 這行其實多餘,global 已建立別名
}

靜態變數:函數的「記憶」

<?php
function counter() {
    static $count = 0; // 僅初始化一次
    $count++;
    return $count;
}

echo counter(); // 1
echo counter(); // 2
echo counter(); // 3

// 實用場景:單例模式、快取、遞歸深度追蹤
function getDbConnection() {
    static $pdo = null;
    if ($pdo === null) {
        $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    }
    return $pdo;
}

// 遞歸深度計數
function recursiveDepth($n) {
    static $depth = 0;
    $depth++;
    if ($n <= 0) {
        $result = $depth;
        $depth = 0; // 重置
        return $result;
    }
    return recursiveDepth($n - 1);
}

超全域變數:PHP 內建的全域狀態

變數用途範例
$GLOBALS所有全域變數的陣列$GLOBALS['myVar']
$_SERVER伺服器與執行環境資訊$_SERVER['REQUEST_URI'], $_SERVER['HTTP_USER_AGENT']
$_GETURL 查詢字串參數$_GET['id']
$_POSTPOST 請求體表單資料$_POST['username']
$_REQUEST$_GET + $_POST + $_COOKIE 合併$_REQUEST['token']
$_SESSION會話資料$_SESSION['user_id']
$_COOKIECookie 資料$_COOKIE['remember_me']
$_FILES上傳檔案資訊$_FILES['avatar']['tmp_name']
$_ENV環境變數$_ENV['APP_ENV']
$php_errormsg最後一個錯誤訊息 (已棄用)-
$http_response_headerHTTP 回應標頭 (file_get_contents 等)-

作用域解析規則:LEGB 法則 (PHP 版)

PHP 變數查找順序:

  1. Local (區域) - 函數內部宣告、參數
  2. Enclosing (外層函數) - PHP 不支援巢狀函數閉包捕獲 (需用 use)
  3. Global (全域) - global $var 或 $GLOBALS['var']
  4. Built-in (超全域/內建) - $_GET, $_POST 等
<?php
$globalVar = 'global';

function outer() {
    $outerVar = 'outer';
    
    // PHP 5.3+ 匿名函數可用 use 捕獲外層變數
    $closure = function() use ($outerVar) {
        global $globalVar;
        echo "Closure: outer=$outerVar, global=$globalVar\n";
    };
    
    $closure();
}

outer();
// 輸出: Closure: outer=outer, global=global

類別屬性 vs 變數作用域

<?php
class Demo {
    public $publicProp = 'public';
    protected $protectedProp = 'protected';
    private $privateProp = 'private';
    
    public function method() {
        // 類內部:$this->property 存取屬性
        echo $this->publicProp;     // 可
        echo $this->protectedProp;  // 可
        echo $this->privateProp;    // 可
        
        // 區域變數
        $local = 'local';
    }
}

$obj = new Demo();
echo $obj->publicProp;      // 可
// echo $obj->protectedProp; // Fatal: Cannot access protected
// echo $obj->privateProp;   // Fatal: Cannot access private

// 靜態屬性:類級別,類似全域但有命名空間
class Counter {
    public static $count = 0;
    
    public static function increment() {
        self::$count++; // 或 static::$count (晚期靜態綁定)
    }
}

Counter::increment();
echo Counter::$count; // 1

最佳實踐與避坑指南

✅ 推薦做法

  1. 避免全域變數:使用依賴注入、單例、服務容器傳遞共享狀態
  2. 函數參數化:需要外部資料時透過參數傳入,而非 global
  3. 靜態變數用於快取/單例:函數級單例、資料庫連線快取
  4. 超全域變數僅在入口處存取:控制器/處理器層,不在業務邏輯/模型層直接用 $_POST

❌ 反模式

<?php
// 反模式 1:濫用 global
$db = new PDO(...);
function getUser($id) {
    global $db; // 難測試、隱性依賴、耦合
    return $db->query("SELECT * FROM users WHERE id=$id")->fetch();
}

// 反模式 2:超全域變數滲透到核心邏輯
function calculatePrice() {
    $qty = $_POST['qty']; // 業務邏輯耦合 HTTP 請求
    return $qty * 100;
}

// 反模式 3:靜態變數洩露狀態
function processBatch($items) {
    static $processed = []; // 跨呼叫殘留,導致 Bug
    foreach ($items as $item) {
        $processed[] = $item;
    }
    return $processed;
}

✅ 正確模式:依賴注入

<?php
// 服務類
class UserRepository {
    private PDO $pdo;
    
    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }
    
    public function findById(int $id): ?array {
        $stmt = $this->pdo->prepare("SELECT * FROM users WHERE id = ?");
        $stmt->execute([$id]);
        return $stmt->fetch() ?: null;
    }
}

// 使用:在組合根/入口注入
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$repo = new UserRepository($pdo);
$user = $repo->findById(123);
// 易測試:可傳入 Mock PDO

總結

  • 區域變數:預設、最安全、函數內部私有
  • 全域變數:global 或 $GLOBALS 引入,慎用,優先依賴注入
  • 靜態變數:static 關鍵字,函數級持久化,適合快取/單例
  • 超全域變數:$_GET, $_POST, $_SESSION 等,僅在應用邊界層使用
  • 類別屬性:public/protected/private + $this->,靜態屬性用 self::/static::
  • 核心原則:最小作用域、顯式依賴、避免隱性共享狀態
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved