PHP 5 所有方法預設都是「虛擬」的(可被子類覆寫)。關鍵在於父類內部如何呼叫自身方法:
self::method():編譯期綁定,永遠呼叫當前類別定義的方法,不受子類覆寫影響。$this->method():運行期綁定 (多態),實際呼叫實例真實類別的方法,子類覆寫生效。<?php
class A {
public function x() { echo "A::x()\n"; }
public function y() { self::x(); echo "A::y()\n"; } // 編譯期綁定
public function z() { $this->x(); echo "A::z()\n"; } // 運行期綁定
}
class B extends A {
public function x() { echo "B::x()\n"; } // 覆寫 x()
}
$b = new B();
$b->y(); // 輸出:A::x() → A::y()
echo "--\n";
$b->z(); // 輸出:B::x() → A::z()
?>
結果解析:y() 使用 self::x() 鎖死在 A,z() 使用 $this->x() 實現多態呼叫 B::x()。
PHP 5.3 引入 static:: 關鍵字,解決靜態方法繼承時的綁定問題:
<?php
class ParentClass {
public static function say($str) {
static::doPrint($str); // LSB:運行期解析為實際調用類別
}
public static function doPrint($str) {
echo "<p>Parent says $str</p>\n";
}
}
class ChildClass extends ParentClass {
public static function doPrint($str) {
echo "<p>Child says $str</p>\n";
}
}
class AnotherChildClass extends ParentClass {
public static function doPrint($str) {
echo "<p>AnotherChild says $str</p>\n";
}
}
ChildClass::say('Hello'); // Child says Hello
AnotherChildClass::say('Hello'); // AnotherChild says Hello
?>
self::doPrint() 永遠呼叫 ParentClass::doPrint();static::doPrint() 則依實際呼叫類別 (ChildClass 或 AnotherChildClass) 解析。
| 情境 | 建議用法 |
|---|---|
| 父類定義演算法骨架,子類實作細節 | 父類用 $this->step() (Template Method Pattern) |
| 靜態工廠方法回傳子類實例 | 父類用 static::create() (LSB) |
| 內部輔助方法,不希望被覆寫 | 用 self::helper() 或 private 方法 |
Active Record 基類 find() 回傳子類集合 | 用 static::hydrate() (LSB) |
__construct() 中呼叫 $this->virtualMethod():子類尚未完全建構,可能存取未初始化屬性 → 改用 self:: 或延遲初始化。$this:致命錯誤,靜態方法無 $this,必須用 self:: 或 static::。