PHP Trait
Trait 是一種程式碼重用機制,可以在多個類別之間共享方法。Trait 解決了 PHP 單一繼承的限制。
基本用法
<?php
trait Timestampable {
public ?DateTime $createdAt = null;
public ?DateTime $updatedAt = null;
public function touch(): void {
$now = new DateTime();
if ($this->createdAt === null) {
$this->createdAt = $now;
}
$this->updatedAt = $now;
}
}
class Post {
use Timestampable;
public function __construct(
public string $title
) {
$this->touch();
}
}
class Comment {
use Timestampable;
public function __construct(
public string $content
) {
$this->touch();
}
}
$post = new Post("Hello World");
echo $post->createdAt->format('Y-m-d');
?>
使用多個 Trait
<?php
trait Loggable {
public function log(string $message): void {
echo "[LOG] $message\n";
}
}
trait Cacheable {
private array $cache = [];
public function cache(string $key, mixed $value): void {
$this->cache[$key] = $value;
}
public function getFromCache(string $key): mixed {
return $this->cache[$key] ?? null;
}
}
class Service {
use Loggable, Cacheable;
public function process(): void {
$this->log("Processing...");
$this->cache('result', 'done');
}
}
?>
衝突解決
當多個 Trait 有相同的方法時,需要解決衝突:
<?php
trait A {
public function hello(): void {
echo "Hello from A\n";
}
}
trait B {
public function hello(): void {
echo "Hello from B\n";
}
}
class MyClass {
use A, B {
A::hello insteadof B; // 使用 A 的 hello
B::hello as helloB; // B 的 hello 改名為 helloB
}
}
$obj = new MyClass();
$obj->hello(); // Hello from A
$obj->helloB(); // Hello from B
?>
改變存取修飾符
<?php
trait Example {
private function secret(): void {
echo "Secret\n";
}
}
class MyClass {
use Example {
secret as public; // 改為 public
}
}
$obj = new MyClass();
$obj->secret(); // Secret
?>
Trait 中的抽象方法
<?php
trait Validator {
abstract public function validate(): bool;
public function isValid(): bool {
return $this->validate();
}
}
class User {
use Validator;
public function __construct(
public string $email
) {}
public function validate(): bool {
return filter_var($this->email, FILTER_VALIDATE_EMAIL) !== false;
}
}
?>
Trait 中的靜態成員
<?php
trait Counter {
private static int $count = 0;
public static function increment(): void {
self::$count++;
}
public static function getCount(): int {
return self::$count;
}
}
class A {
use Counter;
}
class B {
use Counter;
}
A::increment();
A::increment();
B::increment();
echo A::getCount(); // 2
echo B::getCount(); // 1(各自獨立計數)
?>
實際範例
<?php
trait SoftDelete {
public ?DateTime $deletedAt = null;
public function delete(): void {
$this->deletedAt = new DateTime();
}
public function restore(): void {
$this->deletedAt = null;
}
public function isDeleted(): bool {
return $this->deletedAt !== null;
}
}
trait HasUuid {
public readonly string $uuid;
protected function generateUuid(): void {
$this->uuid = sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000,
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
}
class User {
use SoftDelete, HasUuid;
public function __construct(
public string $name
) {
$this->generateUuid();
}
}
$user = new User("Alice");
echo $user->uuid;
$user->delete();
var_dump($user->isDeleted()); // true
?>