核心概念
- 函數內靜態變量:函數調用間保持值,生命週期 = 腳本執行期間,作用域 = 函數內部。
- 類靜態屬性:屬於類而非實例,所有實例共享,支援繼承、晚靜態綁定 (LSB)。
- 類靜態方法:無需實例化調用,
$this 不可用,支援 LSB。
函數靜態變量
<?php
function counter() {
static $count = 0; // 僅初始化一次
$count++;
echo "調用第 $count 次\n";
}
counter(); // 1
counter(); // 2
counter(); // 3
// 遞歸計數
function traverse($node, &$depth = 0) {
static $maxDepth = 0;
$depth++;
$maxDepth = max($maxDepth, $depth);
// ... 遍歷子節點
$depth--;
return $maxDepth;
}
// 緩存昂貴計算結果
function expensiveConfig() {
static $cache = null;
if ($cache === null) {
// 模擬讀取配置文件、解析 YAML
$cache = ['debug' => true, 'db' => '...'];
}
return $cache;
}
類靜態屬性與方法
<?php
class Child {
public static int $count = 0; // 靜態屬性(PHP 7.4+ 類型聲明)
public string $name;
public function __construct(string $name) {
$this->name = $name;
self::$count++; // 類內訪問:self:: 或 static::
}
public function joinGame(): void {
echo "{$this->name} 加入遊戲,當前 " . self::$count . " 人\n";
}
// 靜態方法
public static function getCount(): int {
return self::$count;
}
// 晚靜態綁定 (LSB) 示例
public static function whoAmI(): string {
return static::class; // 運行時解析為調用類
}
}
class BraveChild extends Child {
public static int $count = 0; // 子類獨立計數
}
$c1 = new Child('李逵');
$c1->joinGame(); // 1
$c2 = new Child('張飛');
$c2->joinGame(); // 2
$b1 = new BraveChild('孫悟空');
$b1->joinGame(); // 1 (獨立計數)
echo Child::getCount(); // 2
echo BraveChild::getCount(); // 1
echo Child::whoAmI(); // Child
echo BraveChild::whoAmI(); // BraveChild
晚靜態綁定 (Late Static Binding) 核心
| 關鍵字 | 解析時機 | 指向 |
self:: | 編譯期 | 定義該代碼的類 |
static:: | 運行期 | 實際調用的類 |
parent:: | 編譯期 | 父類 |
static::class | 運行期 | 調用類全限定名 |
<?php
class Base {
public static function factory(): self {
return new static(); // LSB:返回調用類的實例
}
public static function getName(): string {
return self::class; // 總是 Base
}
public static function getCalled(): string {
return static::class; // 調用者類名
}
}
class User extends Base {}
class Admin extends Base {}
$user = User::factory(); // User 實例
$admin = Admin::factory(); // Admin 實例
echo User::getName(); // Base
echo User::getCalled(); // User
echo Admin::getCalled(); // Admin
實戰設計模式
1. 單例模式
<?php
class Database {
private static ?self $instance = null;
private PDO $pdo;
private function __construct() {
$this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
}
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getPdo(): PDO {
return $this->pdo;
}
// 禁止克隆、序列化
private function __clone() {}
public function __wakeup() { throw new \Exception("Cannot unserialize singleton"); }
}
2. 註冊表/服務容器
<?php
class Container {
private static array $services = [];
public static function bind(string $key, callable $factory): void {
self::$services[$key] = $factory;
}
public static function make(string $key) {
if (!isset(self::$services[$key])) {
throw new \Exception("Service $key not found");
}
return self::$services[$key]();
}
}
// 使用
Container::bind('db', fn() => new PDO(...));
$db = Container::make('db');
3. 靜態緩存模式
<?php
class Config {
private static array $cache = [];
public static function get(string $key, $default = null) {
if (!isset(self::$cache[$key])) {
self::$cache[$key] = self::loadFromFile($key) ?? $default;
}
return self::$cache[$key];
}
private static function loadFromFile(string $key) {
// 讀取 config/{$key}.php 返回陣列
}
}
常見誤區與避坑
| 誤區 | 正確理解 |
| 靜態屬性 = 全局變量 | 受類作用域限制,`Class::$prop` 訪問,支持 `private/protected` 封裝 |
| 繼承共享父類靜態屬性 | 子類**不**自動共享,除非子類未重新聲明(PHP 8.1+ 提示 Deprecated) |
| 靜態方法可用 $this | 不可用,靜態上下文無實例 |
| 靜態變量線程安全 | PHP 單線程請求模型下安全,Swoole/Workerman 多進程需額外同步 |
| 靜態屬性可序列化 | `serialize()` 不包含靜態屬性,需手動實現 `__sleep/__wakeup` |
總結
靜態變量是**類級/函數級狀態**的核心工具。善用 static:: (LSB) 實現多態工廠、單例、容器;函數靜態變量用於緩存、計數、遞歸輔助。**嚴禁濫用作為全局狀態容器**,導致耦合、難測試、併發隱患。