PHP 5 引入靜態關鍵字(static),允許定義屬於類別而非實例的屬性、方法與常數。本文深入解析靜態成員特性、記憶體模型、呼叫語法、常見誤區與最佳實踐。
| 特性 | 實例成員 | 靜態成員 |
|---|---|---|
| 所屬 | 具體物件實例 | 類別本身 |
| 記憶體 | 每個實例獨佔一份 | 全程式共享一份 |
| 存取方式 | $obj->prop、$obj->method() | Class::$prop、Class::method() |
$this 可用性 | 可用 | 不可用(靜態方法內無 $this) |
| 繼承行為 | 多型覆寫 | 靜態綁定(預設),可用 static:: 延遲靜態綁定 |
| 常數 | 不支援 | const 隱含靜態 |
class Counter {
private static $count = 0; // 僅類別可見
public static $publicCount = 0; // 公開存取
public function __construct() {
self::$count++; // 類別內部用 self::
static::$publicCount++; // 也可用 static:: (LSB)
}
public static function getCount() {
return self::$count;
}
}
// 使用
$c1 = new Counter();
$c2 = new Counter();
echo Counter::$publicCount; // 2
echo Counter::getCount(); // 2
public/protected/private。class Utils {
public static function formatPrice($amount, $currency = 'TWD') {
return $currency . number_format($amount, 2);
}
// 靜態方法可呼叫其他靜態成員
public static function log($msg) {
self::writeLog(date('Y-m-d H:i:s') . ' ' . $msg);
}
private static function writeLog($line) {
error_log($line, 3, __DIR__ . '/app.log');
}
}
echo Utils::formatPrice(1234.5); // TWD1,234.50
class Demo {
public function instanceMethod() {
echo "Instance: " . ($this ? 'has $this' : 'no $this') . "\n";
}
public static function staticMethod() {
echo "Static: " . (isset($this) ? 'has $this' : 'no $this') . "\n";
}
}
$obj = new Demo();
$obj->instanceMethod(); // Instance: has $this
$obj->staticMethod(); // Static: no $this (PHP 7+ 嚴格模式下會 Deprecated)
Demo::staticMethod(); // Static: no $this
Demo::instanceMethod(); // Deprecated: Non-static method called statically
class Config {
const VERSION = '2.1.0';
const MAX_RETRY = 3;
const DEFAULT_OPTS = ['timeout' => 5, 'retry' => true]; // PHP 5.6+ 支援陣列常數
}
echo Config::VERSION; // 2.1.0
// Config::VERSION = '3.0'; // Fatal: Cannot override constant
常數隱含 public static,不可變更,繼承時子類別可定義同名常數覆蓋。
PHP 5.3+ 引入 static:: 關鍵字,解決繼承中靜態方法呼叫的綁定問題:
class ParentClass {
public static function who() { echo __CLASS__ . "\n"; }
public static function test() { self::who(); } // 編譯期綁定 ParentClass
public static function testLSB() { static::who(); } // 執行期綁定實際調用類別
}
class ChildClass extends ParentClass {
public static function who() { echo __CLASS__ . "\n"; }
}
ParentClass::test(); // ParentClass
ParentClass::testLSB(); // ParentClass
ChildClass::test(); // ParentClass (self:: 綁定父類)
ChildClass::testLSB(); // ChildClass (static:: 綁定子類)
| 誤區 | 正確做法 |
|---|---|
靜態方法中使用 $this | 改用 self:: 或 static:: 存取靜態成員 |
| 將大量狀態存放在靜態屬性(全域狀態) | 改用依賴注入、服務容器、單例模式管理共享狀態 |
靜態屬性初始化用 new Object() | PHP 8.1+ 支援 static $obj = new Class();,舊版改用靜態建構子模式 |
| 濫用靜態方法作為工具函數集 | 考慮命名空間函數、單一職責服務類別 |
| 忽略 LSB 導致繼承失效 | 需多型行為時統一使用 static:: |
User::createFromRequest($request)AppConfig::DB_HOSTprivate static $instance; public static function getInstance()Str::slug(), Arr::pluck()善用靜態成員能簡化共享資料存取,但過度使用會導致耦合度高、難以測試。遵循「預設實例化、必要時靜態」原則。