自 PHP 5.4 引入以來,Trait 成為單繼承語言中實現橫向代碼復用的核心機制。本文系統講解 Trait 的語法、進階用法、衝突解決及最佳實踐。
// 1. 定義 Trait
trait Loggable {
public function log($message) {
echo "[LOG] " . date('Y-m-d H:i:s') . " $message\n";
}
}
// 2. 在類中使用
class UserService {
use Loggable;
public function register($username) {
$this->log("新用戶註冊: $username");
// 業務邏輯...
}
}
$service = new UserService();
$service->register("Alice"); // 輸出: [LOG] 2024-01-15 10:30:00 新用戶註冊: Alice
trait Loggable { /* ... */ }
trait Cacheable {
public function cache($key, $data) { /* ... */ }
}
class ArticleService {
use Loggable, Cacheable; // 同時引入多個 Trait
}
trait BaseTrait {
public function baseMethod() { echo "Base"; }
}
trait ExtendedTrait {
use BaseTrait; // Trait 內部復用另一個 Trait
public function extendedMethod() { echo "Extended"; }
}
class MyClass {
use ExtendedTrait; // 間接獲得 baseMethod
}
trait Validatable {
// 強制使用類實現具體驗證邏輯
abstract public function validate($data);
public function validateAndSave($data) {
if ($this->validate($data)) {
$this->save($data);
}
}
}
class UserModel {
use Validatable;
public function validate($data) {
return !empty($data['email']) && filter_var($data['email'], FILTER_VALIDATE_EMAIL);
}
}
trait A { public function hello() { echo "A"; } }
trait B { public function hello() { echo "B"; } }
class MyClass {
use A, B {
B::hello insteadof A; // 選擇 B 的 hello,排除 A 的
A::hello as helloFromA; // 將 A 的 hello 重命名為 helloFromA 保留
}
}
$obj = new MyClass();
$obj->hello(); // 輸出: B
$obj->helloFromA(); // 輸出: A
| 特性 | 說明 |
|---|---|
| 不可實例化 | Trait 不能 `new Trait()`,只能被類 `use` |
| 優先級 | Trait 方法 > 類自身方法 > 父類方法 |
| 修飾符支援 | 支援 `public`、`protected`、`private`、`static`、`final`、`abstract` |
| 屬性定義 | 可定義屬性,但同名屬性會導致嚴重錯誤(PHP 7.0+ 已修復部分情況) |
Trait 不是「多繼承的替代品」,而是「精準的功能注入工具」。善用 Trait 能讓類職責單一、代碼高度復用、維護成本大幅降低。