PHP 單一繼承模型限制了程式碼複用的靈活性。自 PHP 5.4 引入 Trait 後,開發者得以在不使用繼承的情況下,將方法群組合入多個類別中。本文詳細說明 Trait 語法、衝突解決、優先順序與實際應用場景。
<?php
trait Loggable
{
protected function log(string $message, string $level = 'info'): void
{
$time = date('Y-m-d H:i:s');
echo "[$time] [$level] $message\n";
}
}
trait Timestampable
{
public function setCreatedAt(\DateTimeInterface $time): void
{
$this->createdAt = $time;
}
public function setUpdatedAt(\DateTimeInterface $time): void
{
$this->updatedAt = $time;
}
}
class Article
{
use Loggable, Timestampable;
private \DateTimeInterface $createdAt;
private \DateTimeInterface $updatedAt;
public function publish(): void
{
$this->setCreatedAt(new \DateTimeImmutable());
$this->log('文章發布', 'notice');
}
}
$article = new Article();
$article->publish();
// 輸出: [2024-01-15 10:30:00] [notice] 文章發布
當多個 Trait 定義相同方法名時,必須顯式解決:
<?php
trait A { public function hello() { echo 'A'; } }
trait B { public function hello() { echo 'B'; } }
class Test
{
use A, B {
B::hello insteadof A; // 選用 B 的 hello
A::hello as helloA; // 將 A 的 hello 別名為 helloA
}
}
$t = new Test();
$t->hello(); // 輸出 B
$t->helloA(); // 輸出 A
<?php
trait InternalTools
{
private function internalHelper(): string { return 'secret'; }
protected function protectedHelper(): string { return 'protected'; }
}
class Service
{
use InternalTools {
internalHelper as public getSecret; // 將 private 改為 public
protectedHelper as public getProtected; // 將 protected 改為 public
}
}
$svc = new Service();
echo $svc->getSecret(); // 可呼叫
echo $svc->getProtected(); // 可呼叫
<?php
trait Configurable
{
// Trait 可定義屬性
private array $config = [];
// 可定義抽象方法,強制使用類別實作
abstract protected function getDefaultConfig(): array;
public function setConfig(array $config): void
{
$this->config = array_merge($this->getDefaultConfig(), $config);
}
public function getConfig(string $key, $default = null)
{
return $this->config[$key] ?? $default;
}
}
class App
{
use Configurable;
protected function getDefaultConfig(): array
{
return ['debug' => false, 'env' => 'production'];
}
}
$app = new App();
$app->setConfig(['debug' => true]);
var_dump($app->getConfig('debug')); // true
ArrayAccess、Countable、Serializable 等介面的通用實作。use。insteadof 與 as。readonly 屬性與回傳型別。| 特性 | Trait | Interface | Abstract Class |
|---|---|---|---|
| 程式碼複用 | ✅ 方法實作 | ❌ 僅契約 | ✅ 方法實作 |
| 多重組合 | ✅ 無限制 | ✅ 多實作 | ❌ 單一繼承 |
| 狀態(屬性) | ✅ 可定義 | ❌ 不可 | ✅ 可定義 |
| 建構子 | ❌ 不支援 | ❌ 不支援 | ✅ 支援 |
| 適用場景 | 橫切功能、混入 | 契約定義 | 核心領域模型基類 |