PHP 對多型提供原生支援:執行時根據物件實際類型決定呼叫哪個方法,無需像 C++ 虛函數表那樣編譯期綁定。序列化則提供物件狀態持久化能力,但需注意其限制與安全風險。
<?php
abstract class Shape {
abstract public function draw(): void;
abstract public function area(): float;
}
class Circle extends Shape {
private $radius;
public function __construct(float $radius) { $this->radius = $radius; }
public function draw(): void { echo "繪製圓形 (r={$this->radius})\n"; }
public function area(): float { return M_PI * $this->radius ** 2; }
}
class Rectangle extends Shape {
private $width, $height;
public function __construct(float $w, float $h) { $this->width = $w; $this->height = $h; }
public function draw(): void { echo "繪製矩形 ({$this->width}x{$this->height})\n"; }
public function area(): float { return $this->width * $this->height; }
}
class Board {
public function niceDrawing(Shape $shape): void {
// 多型: $shape 實際類型決定呼叫哪個 draw()
$shape->draw();
echo "面積: " . $shape->area() . "\n";
}
}
$board = new Board();
$board->niceDrawing(new Circle(5)); // 繪製圓形, 面積: 78.54
$board->niceDrawing(new Rectangle(4,6)); // 繪製矩形, 面積: 24
?>
Shape 定義 draw()、area(),強制子類別實作。Shape $shape 確保傳入物件符合契約 (PHP 7.0+)。<?php
class User {
public $id;
public $name;
private $passwordHash; // 敏感資料不應序列化
private $dbConnection; // 資源型別無法序列化
public function __construct($id, $name, $passwordHash) {
$this->id = $id;
$this->name = $name;
$this->passwordHash = $passwordHash;
}
// 自訂序列化:排除敏感/資源屬性
public function __sleep(): array {
return ['id', 'name']; // 僅回傳需序列化的屬性名稱
}
// 反序列化還原
public function __wakeup(): void {
// 重新建立資料庫連線、載入密碼雜湊等
// $this->dbConnection = new PDO(...);
}
// PHP 7.4+ 新介面 (推薦)
// public function __serialize(): array { return ['id'=>$this->id, 'name'=>$this->name]; }
// public function __unserialize(array $data): void { $this->id=$data['id']; $this->name=$data['name']; }
}
$user = new User(1, 'John', '$2y$10$...');
$serialized = serialize($user);
echo $serialized; // O:4:"User":2:{s:2:"id";i:1;s:4:"name";s:4:"John";}
$restored = unserialize($serialized);
echo $restored->name; // John
?>
| 主題 | 建議 |
|---|---|
| 敏感資料 | 實作 __sleep 排除密碼、Token、金鑰;或改用 JSON 序列化 (json_encode) 手動控制輸出欄位 |
| 資源型別 | PDO、Socket、GD 影像資源無法序列化,__wakeup 中重建 |
| 版本相容 | 類別結構更 (增減屬性) 導致反序列化失敗,建議加入版本號屬性並在 __wakeup 遷移 |
| 安全性 | 絕不 unserialize 使用者可控輸入,會導致物件注入 (POP 鏈) 攻擊,改用 json_decode、igbinary、msgpack |
| 效能 | igbinary、msgpack 二進位格式比內建 serialize 快 2-5 倍、體積小 30-50% |
| Session 儲存 | PHP Session 預設使用 serialize,大型物件會拖慢 Session 讀寫,建議只存 ID、必要資料 |
SoftDeletes, Loggable)。__call/__callStatic (動態方法)、__get/__set (屬性存取攔截)、__invoke (可呼叫物件)。new class implements Logger { ... } 臨時建立一次性實作 (PHP 7.0+)。static:: 在繼承體系中指向實際被呼叫的類別 (PHP 5.3+)。readonly (PHP 8.1+) 確保初始化後不可變更。UserRepository 只管資料存取)。LoggerInterface, CacheableInterface)。PDO 介面而非具體類別)。掌握多型、序列化邊界與 SOLID 原則,能寫出可維護、可測試、可擴充的 PHP 物件導向代碼,是從「寫腳本」邁向「軟體工程」的關鍵。