PHP 類別中的 static 關鍵字定義靜態屬性與方法,它們屬於類別本身而非具體實例。本文從語法特性、記憶體模型、繼承機制、self/parent/static 延遲靜態綁定差異,深入到 Zend 內核編譯流程與 Hash 表儲存結構,全方位解析靜態成員的本質與最佳實務。
ClassName::$prop 或 ClassName::method() 存取$this:靜態方法內不可使用 $this,僅能用 self::、parent::、static::(PHP 5.3+ 延遲靜態綁定)| 操作符 | 綁定時機 | 繼承行為 | 適用場景 |
|---|---|---|---|
self:: | 編譯期(早期綁定) | 永遠指向「定義該程式碼的類別」,即使被子類別覆寫 | 存取當前類別私有/靜態成員 |
parent:: | 編譯期 | 指向直接父類別 | 呼叫父類別被覆寫的方法、存取父類別靜態成員 |
static:: | 執行期(晚期靜態綁定 LSB) | 指向「實際被呼叫的類別」(子類別) | 可被繼承覆寫的靜態方法/屬性,實現多態 |
<?php
class Base {
public static $val = 'Base';
public static function who() { echo self::$val . " (self)\n"; }
public static function whoLate() { echo static::$val . " (static)\n"; }
}
class Child extends Base {
public static $val = 'Child';
}
Base::who(); // Base (self)
Base::whoLate(); // Base (static)
Child::who(); // Base (self) ← 注意!self 綁定在 Base
Child::whoLate(); // Child (static) ← static 綁定在 Child
?>
若父子類別各自宣告 public static $x,則各自擁有獨立記憶體空間:
class ParentClass { public static $count = 0; }
class ChildClass extends ParentClass { public static $count = 0; }
ParentClass::$count = 10;
ChildClass::$count = 20;
echo ParentClass::$count; // 10
echo ChildClass::$count; // 20 互不影響
存取父類別被遮蔽的靜態成員需用 parent::$count。
靜態呼叫 Class::method() 省略 new Class() 實例化開銷(記憶體分配、建構子執行、物件結構初始化),對無狀態工具類方法(如驗證、格式化、工廠方法)效能提升明顯。
基於 zend_compile.c、zend_language_parser.y 分析:
class → zend_do_begin_class_declaration() 初始化類別結構,註冊進 CG(class_table),設為 CG(active_class_entry)static 修飾詞 → 設定 ZEND_ACC_STATIC 標誌,經 zend_do_verify_access_types() 檢驗(不可用於介面、abstract 屬性等)zend_do_declare_property() → zend_declare_property_ex()if (access_type & ZEND_ACC_STATIC) {
target_symbol_table = &ce->default_static_members; // 靜態成員表
} else {
target_symbol_table = &ce->default_properties; // 實例屬性預設值表
}
zend_hash_update(target_symbol_table, ...);
結論:靜態屬性與實例屬性均儲存在類別結構 zend_class_entry 的屬性哈希表中,僅是目標表不同:
- default_static_members:靜態成員(單一拷貝)
- default_properties:非靜態屬性預設值(實例化時複製給物件)
static:: (LSB) 而非 self::const,編譯期優化、語意更清晰readonly 靜態屬性:初始化後不可修改,適合單例模式