PHP 規定:靜態屬性聲明處的初始值必須是編譯時常量(字面量、常量表達式),不能包含變量、函數調用、對象實例化、new 表達式。
class Config {
public static string $APP_NAME = 'MyApp'; // 字面量 ✓
public static int $MAX_RETRY = 3; // 整數 ✓
public static float $RATE = 1.5; // 浮點數 ✓
public static bool $DEBUG = true; // 布爾 ✓
public static array $ALLOWED = ['a', 'b']; // 數組字面量 (PHP 7.0+) ✓
public static $COMPUTED = 10 + 5 * 2; // 常量表達式 (PHP 5.6+) ✓
}
class ServiceContainer {
private static ?Database $db = null; // 聲明類型,不初始化
public static function getDb(): Database {
if (self::$db === null) {
self::$db = new Database(DB_CONFIG); // 這裡才 new
}
return self::$db;
}
}
// 用法
$db = ServiceContainer::getDb();
模式:**單例/工廠模式**,首次訪問時懶加載,解決依賴順序與資源消耗問題。
class GlobalState {
public static $externalService;
}
// 引導文件中
GlobalState::$externalService = new ExternalApiClient($key);
缺點:破壞封裝、難以測試、隱式依賴、**僅適用於極簡腳本/框架引導階段**。
class SecureConfig {
private static string $secretKey;
private static array $permissions;
// 靜態初始化器 - 必須手動調用一次
public static function initialize(array $config): void {
self::$secretKey = $config['secret'] ?? throw new Exception('Secret required');
self::$permissions = $config['perms'] ?? [];
}
public static function getSecretKey(): string {
if (self::$secretKey === '') throw new LogicException('Not initialized');
return self::$secretKey;
}
}
// 應用啟動時調用一次
SecureConfig::initialize($configFromEnv);
優勢:封裝性強、強制初始化、可驗證配置、便於單元測試 Mock。
class AppConfig {
public static function boot(array $env): void {
self::DB_DSN = $env['DB_DSN'] ?? 'sqlite::memory:';
self::CACHE_TTL = (int)($env['CACHE_TTL'] ?? 3600);
}
// PHP 8.1+ 只讀靜態屬性:初始化後不可修改
public static readonly string $DB_DSN;
public static readonly int $CACHE_TTL;
}
// 啟動流程
AppConfig::boot($_ENV);
// 之後全程只讀,防止意外篡改
$dsn = AppConfig::DB_DSN;
| 錯誤代碼 | 錯誤原因 | 正確寫法 |
|---|---|---|
static $obj = new StdClass(); | 聲明處不允許 new | 用懶加載 getter 或靜態初始化器 |
static $arr = [$config['key']]; | 變量非編譯時常量 | 延遲到初始化方法構建數組 |
static $val = strlen('abc'); | 函數調用非常量表達式 | 預計算常量或延遲計算 |
static $const = SOME_CONST; | 常量引用是允許的 (PHP 5.6+) | 這其實是合法的 ✓ |
| 繼承時重新聲明 static | 父子類 static 獨立存儲 | 理解 Late Static Binding (static::) |
// ❌ 反模式:到處靜態調用,強耦合,不可測試
class UserController {
public function show($id) {
$user = UserModel::find($id); // 靜態調用 DB
echo UserView::render($user); // 靜態調用模板
}
}
// ✅ 現代最佳實踐:構造函數注入 + 服務容器
class UserController {
public function __construct(
private UserRepository $repo,
private TemplateRenderer $renderer
) {}
public function show(string $id): Response {
$user = $this->repo->find($id);
return new Response($this->renderer->render('user/show', ['user' => $user]));
}
}
// 容器配置 (PHP-DI / Symfony DI / Laravel Container)
$container->set(UserRepository::class, fn() => new PdoUserRepository($pdo));
$container->set(TemplateRenderer::class, fn() => new TwigRenderer($twig));
靜態屬性是「全局變量的偽裝」,能不用盡量不用。真正需要共享狀態時,請選擇單例模式、服務容器、或上下文對象傳遞,保持代碼可測試、可維護、可擴展。