在繼承體系中,父類定義演算法骨架,將具體步驟延遲到子類實作,是「模板方法模式」的核心。PHP 支援在父類中透過 $this->method() 呼叫子類覆寫的方法(動態派發),也支援靜態語境下的 static::method()(後期靜態綁定)。本文系統解析兩種機制與實戰應用。
非靜態方法呼叫預設即為動態派發:實際執行的是物件實際類別的方法。
abstract class BaseApp {
final public function run(): void {
$this->initialize(); // 步驟 1:子類實作
$this->execute(); // 步驟 2:子類實作
$this->finalize(); // 步驟 3:子類實作
}
abstract protected function initialize(): void;
abstract protected function execute(): void;
abstract protected function finalize(): void;
}
class DefaultApp extends BaseApp {
protected function initialize(): void { echo "Init\n"; }
protected function execute(): void { echo "Execute\n"; }
protected function finalize(): void { echo "Finalize\n"; }
}
$app = new DefaultApp();
$app->run();
// 輸出:Init → Execute → Finalize
關鍵點:
final 防止子類破壞流程。protected abstract 強制子類實作。$this-> 會解析到 DefaultApp 的實作。PHP 5.3+ 引入 static:: 關鍵字,在靜態方法中解析為實際被呼叫的類別,而非定義該方法的類別。
class ParentModel {
public static function getTable(): string {
return static::TABLE; // LSB:解析為 ChildModel::TABLE
}
public static function findAll(): array {
$table = static::getTable();
return Database::query("SELECT * FROM $table");
}
}
class UserModel extends ParentModel {
protected const TABLE = 'users';
}
class OrderModel extends ParentModel {
protected const TABLE = 'orders';
}
UserModel::findAll(); // SELECT * FROM users
OrderModel::findAll(); // SELECT * FROM orders
class A {
public static function who() { echo __CLASS__ . "\n"; }
public static function testSelf() { self::who(); } // 編譯期綁定 → A
public static function testStatic() { static::who(); } // 執行期綁定 → 實際類別
}
class B extends A {
public static function who() { echo __CLASS__ . "\n"; }
}
B::testSelf(); // A
B::testStatic(); // B
abstract class ActiveRecord {
protected static string $table = '';
protected array $attributes = [];
public static function find(int $id): ?self {
$sql = "SELECT * FROM " . static::$table . " WHERE id = ?";
$row = Database::fetch($sql, [$id]);
return $row ? new static($row) : null; // new static() 也是 LSB
}
public function __construct(array $data = []) {
$this->attributes = $data;
}
public function save(): bool {
if (empty($this->attributes['id'])) {
return $this->insert();
}
return $this->update();
}
protected function insert(): bool {
$cols = implode(',', array_keys($this->attributes));
$ph = ':' . implode(',:', array_keys($this->attributes));
$sql = "INSERT INTO " . static::$table . " ($cols) VALUES ($ph)";
return Database::execute($sql, $this->attributes);
}
// update/delete 同理...
}
class User extends ActiveRecord {
protected static string $table = 'users';
}
$user = User::find(1);
$user->email = 'new@example.com';
$user->save();
interface Configurable {
public function getConfig(): array;
}
abstract class ServiceBase {
public function __construct() {
if (!$this instanceof Configurable) {
throw new LogicException(static::class . ' must implement Configurable');
}
$this->configure($this->getConfig());
}
abstract protected function configure(array $config): void;
}
class EmailService extends ServiceBase implements Configurable {
public function getConfig(): array { return ['host' => 'smtp.example.com']; }
protected function configure(array $config): void { /* ... */ }
}
Using $this when not in object context。靜態語境請用 static:: 或 self::。init() 方法在建構後手動呼叫,或採用工廠模式。private function step() 子類定義同名方法會視為新方法,動態派發失效。請用 protected。new static() 需要 PHP 5.3+,舊版本請用 get_called_class()。父類呼叫子類方法是框架設計(Laravel、ThinkPHP、Yii 等)的基石。掌握 動態派發($this->)處理實例流程、後期靜態綁定(static::)處理靜態工廠/ActiveRecord,並嚴守 final/protected abstract 契約,即能構建可擴充、可測試的繼承體系。