PHP 類別除實例化物件外,若僅定義 static 成員(屬性/方法),即可當作「單例容器」使用,無需 new。結合方法內部的 static 區域變數,可實現「私有資料儲存」與「跨腳本存取」的雙重效果,類似 C++ 的 private static 成員或 Java 的 Singleton 模式。
<?php
class DataStore {
// 靜態方法:設定/取得鍵值
public static function value($key, $val = null) {
// 函式級靜態變數:生命週期贯穿整個請求,對外不可見
static $data = array();
// 判斷參數數量決定「寫入」或「讀取」
if (func_num_args() > 1) {
$data[$key] = $val; // 寫入
} else {
return isset($data[$key]) ? $data[$key] : null; // 讀取
}
}
}
// 測試
DataStore::value('site_name', 'My Blog');
DataStore::value('admin_email', 'admin@example.com');
echo DataStore::value('site_name'); // 輸出:My Blog
echo DataStore::value('admin_email'); // 輸出:admin@example.com
?>
| 機制 | 說明 | 效果 |
|---|---|---|
static $data 區域變數 | PHP 函式內 static 變數僅初始化一次,後續呼叫保持值 | 資料在同一請求的所有呼叫間共享,且外部無法直接存取 $data |
func_num_args() / func_get_arg() | 動態檢測實際傳入參數數量 | 單一方法同時支援「單參數讀取」與「雙參數寫入」兩種介面 |
static 方法 | 無需實例化即可呼叫 DataStore::value() | 語法簡潔,類似全域函式但具命名空間隔離 |
class Config {
public static function get($key, $default = null) {
static $cache = null;
if ($cache === null) {
$cache = parse_ini_file('/path/to/config.ini', true);
}
$keys = explode('.', $key);
$val = $cache;
foreach ($keys as $k) {
if (!isset($val[$k])) return $default;
$val = $val[$k];
}
return $val;
}
}
// 使用
$dbHost = Config::get('database.host');
$debug = Config::get('app.debug', false);
class ServiceLocator {
private static $services = array();
public static function register($name, $instance) {
self::$services[$name] = $instance;
}
public static function get($name) {
if (!isset(self::$services[$name])) {
throw new Exception("Service '$name' not registered.");
}
return self::$services[$name];
}
}
// 註冊
ServiceLocator::register('db', new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'));
ServiceLocator::register('cache', new Redis());
// 任意處取用
$db = ServiceLocator::get('db');
$cache = ServiceLocator::get('cache');
$GLOBALS)class Flash {
public static function set($key, $msg) {
static $bag = array();
$bag[$key] = $msg;
}
public static function get($key, $remove = true) {
static $bag = array();
if (!isset($bag[$key])) return null;
$msg = $bag[$key];
if ($remove) unset($bag[$key]);
return $msg;
}
}
// Controller
Flash::set('success', '文章已發布!');
// View
if ($msg = Flash::get('success')) echo "<div class='alert'>$msg</div>";
| 優點 | 缺點/限制 |
|---|---|
| 零配置、零依賴,純 PHP 實作 | 資料僅存活於「單一 HTTP 請求」,跨請求需搭配 Session/快取/資料庫 |
| 封裝性佳,避免全域命名空間汙染 | 不支援序列化、持久化、分散式共享 |
| 語法直覺,IDE 自動完成友善 | 多執行緒環境(如 Swoole)下 static 變數為進程級共享,需注意並發安全 |
| 效能極高(記憶體陣列直接存取) | 資料結構僅限陣列/純量,無查詢、索引、關聯功能 |
app() 助手函式即類似概念)利用 static 方法 + 函式級 static 變數,即可在 PHP 中建立輕量、封裝良好的「請求級資料容器」。這不僅是語法技巧,更是「單一職責」與「依賴倒置」原則在動態語言中的輕量落地。理解其生命週期與作用域限制,善用於適當層級,能讓程式碼更乾淨、更易維護。