三大使用場景
| 場景 | 關鍵字位置 | 生命週期/作用域 |
| 靜態區域變數 | 函數/方法內部 | 請求級別持久化,跨呼叫保持值 |
| 靜態屬性 | 類別內部 static $var | 類別級別,所有實例共享 |
| 靜態方法 | 類別內部 static function() | 類別級別,無 $this,不可存取非靜態成員 |
場景一:靜態區域變數 (函數記憶)
<?php
function counter() {
static $count = 0; // 只初始化一次!
$count++;
echo "呼叫次數: $count\n";
}
counter(); // 1
counter(); // 2
counter(); // 3
// 實用案例:單例模式簡易版
function getDatabase() {
static $pdo = null;
if ($pdo === null) {
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
}
return $pdo;
}
// 遞迴計數器
function recursive($n) {
static $depth = 0;
$depth++;
if ($n <= 0) { $depth--; return; }
recursive($n - 1);
$depth--;
}
?>
場景二:靜態屬性與方法
<?php
class Config {
// 靜態屬性:類別級別共享
private static $settings = [];
private static $instance = null;
// 私有建構子 (單例)
private function __construct() {}
// 靜態方法:工廠/存取器
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public static function set(string $key, $value): void {
self::$settings[$key] = $value;
}
public static function get(string $key, $default = null) {
return self::$settings[$key] ?? $default;
}
// 靜態方法不可使用 $this
// public static function bad() { $this->settings = []; } // Fatal Error
}
// 使用
Config::set('app.name', 'MyApp');
echo Config::get('app.name'); // MyApp
$config = Config::getInstance();
?>
關鍵差異:self vs static (延遲靜態綁定 LSB)
<?php
class ParentClass {
public static $who = 'Parent';
public static function sayWho() {
echo "self: " . self::$who . "\n"; // 編譯期綁定 -> Parent
echo "static: " . static::$who . "\n"; // 執行期綁定 -> 實際調用類別
}
}
class ChildClass extends ParentClass {
public static $who = 'Child';
}
ParentClass::sayWho();
// self: Parent
// static: Parent
ChildClass::sayWho();
// self: Parent (self 綁定寫死在 ParentClass)
// static: Child (static 綁定到實際調用的 ChildClass)
?>
實戰:ActiveRecord 基類 (LSB 核心應用)
<?php
abstract class Model {
protected static string $table = '';
// 靜態工廠:建立查詢構建器
public static function query(): QueryBuilder {
return new QueryBuilder(static::$table); // LSB: 子類別的 table
}
// 靜態查找
public static function find(int $id): ?static {
$builder = static::query(); // LSB: 子類別的 query()
return $builder->where('id', $id)->first();
}
// 靜態建立
public static function create(array $attributes): static {
$builder = static::query();
$id = $builder->insert($attributes);
return static::find($id);
}
}
class User extends Model {
protected static string $table = 'users';
}
class Post extends Model {
protected static string $table = 'posts';
}
// 使用
$user = User::find(1); // 查 users 表
$post = Post::find(1); // 查 posts 表
$newUser = User::create(['name' => 'Alice']); // 寫 users 表
?>
靜態成員存取語法總結
| 位置 | 存取靜態屬性 | 存取靜態方法 | 存取實例屬性 | 存取實例方法 |
| 類別外部 | Class::$prop | Class::method() | 需實例 $obj->prop | 需實例 $obj->method() |
| 類別內部 (非靜態) | self::$prop / static::$prop | self::method() / static::method() | $this->prop | $this->method() |
| 類別內部 (靜態方法) | self::$prop / static::$prop | self::method() / static::method() | ❌ 不可直接存取 | ❌ 不可直接存取 |
| 父類別存取子類別靜態 | static::$prop (LSB) | static::method() (LSB) | N/A | N/A |
繼承與覆寫規則
- 子類別可覆寫父類別靜態屬性/方法
parent:: 存取父類別靜態成員
static:: 執行期解析到實際類別 (LSB)
self:: 編譯期解析到定義該方法的類別
- 介面可宣告靜態方法 (PHP 8.1+),但介面靜態方法無 LSB 行為
常見誤區與避坑
| 誤區 | 正確認知 |
| 靜態方法可用 $this | Fatal Error,靜態方法無物件上下文 |
| 靜態屬性屬於實例 | 屬於類別,所有實例共享同一記憶體位址 |
| static 變數在類別層級 | 函數內 static 是函數級,類別內 static 是類別級 |
| 介面可定義靜態屬性 | 介面只能定義常數 (PHP 8.1 前) 或靜態方法 (PHP 8.1+) |
| Trait 中 static 變數共享 | 每個使用 Trait 的類別有獨立副本 (非共享) |
效能與記憶體考量
- 靜態成員存活於整個請求週期 (PHP-FPM) 或進程生命週期 (Swoole)
- 大量靜態資料會佔用記憶體,長期運行進程需注意洩漏
- 單例模式、快取、註冊表模式常用靜態屬性
- 測試時靜態狀態污染需在
tearDown 重置
現代替代方案
| 需求 | 傳統 static | 現代替代 |
| 單例/全域存取 | static $instance | 依賴注入容器 (PSR-11) |
| 配置管理 | static $settings | 配置物件 + 容器綁定 |
| 快取 | static $cache = [] | Redis/Memcached/PSR-16 Cache |
| 資料庫連線 | static $pdo | 連線池 / 容器管理 |