自 PHP 5.4.0 起,Traits(特徵) 作為一種代碼復用機制被引入,專為解決單繼承語言無法多重繼承的限制而設計。本文系統梳理 Traits 的語法、優先級、衝突解決、靜態成員、屬性定義等核心特性,並配合豐富示例助你掌握實戰用法。
Trait 類似類,但不能實例化。它定義了一組方法,可通過 use 關鍵字在類中「混入」。
<?php
trait EzcReflectionReturnInfo {
function getReturnType() { /* ... */ }
function getReturnDescription() { /* ... */ }
}
class EzcReflectionMethod extends ReflectionMethod {
use EzcReflectionReturnInfo;
}
class EzcReflectionFunction extends ReflectionFunction {
use EzcReflectionReturnInfo;
}
?>
以上兩個類無需繼承共同父類,即可復用 Trait 中的方法。
當類繼承父類、使用 Trait、並自行定義同名方法時,優先級從高到低:
<?php
class Base { public function sayHello() { echo 'Hello '; } }
trait SayWorld { public function sayHello() { parent::sayHello(); echo 'World!'; } }
class MyHelloWorld extends Base { use SayWorld; }
$o = new MyHelloWorld(); $o->sayHello(); // 輸出: Hello World!
?>
此處 SayWorld::sayHello 覆蓋了 Base::sayHello,並通過 parent::sayHello() 調用父類方法。若 MyHelloWorld 再定義 sayHello,則會覆蓋 Trait 方法。
可用逗號分隔引入多個 Trait:use Hello, World;。若多個 Trait 存在同名方法,必須顯式解決衝突,否則報致命錯誤。
<?php
trait A { public function smallTalk() { echo 'a'; } public function bigTalk() { echo 'A'; } }
trait B { public function smallTalk() { echo 'b'; } public function bigTalk() { echo 'B'; } }
class Talker {
use A, B {
B::smallTalk insteadof A; // 選用 B 的 smallTalk
A::bigTalk insteadof B; // 選用 A 的 bigTalk
}
}
class Aliased_Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as talk; // 給 B::bigTalk 起別名 talk
}
}
?>
insteadof:指定保留哪個 Trait 的方法。as:為被排除的方法起別名,保留其功能。使用 as 可調整引入方法的可見性:
<?php
trait HelloWorld { public function sayHello() { echo 'Hello World!'; } }
class MyClass1 { use HelloWorld { sayHello as protected; } }
class MyClass2 { use HelloWorld { sayHello as private myPrivateHello; } }
// MyClass1 中 sayHello 變為 protected
// MyClass2 中原 sayHello 仍為 public,新增私有別名 myPrivateHello
?>
Trait 可在定義時 use 其他 Trait,實現功能組合:
<?php
trait Hello { public function sayHello() { echo 'Hello '; } }
trait World { public function sayWorld() { echo 'World!'; } }
trait HelloWorld { use Hello, World; }
class MyHelloWorld { use HelloWorld; }
$o = new MyHelloWorld(); $o->sayHello(); $o->sayWorld(); // Hello World!
?>
trait Hello { abstract public function getWorld(); public function sayHelloWorld() { echo 'Hello'.$this->getWorld(); } }
trait Counter { public static $c = 0; public static function inc() { echo ++self::$c; } }
class C1 { use Counter; } class C2 { use Counter; }
C1::inc(); // 1 C2::inc(); // 1(獨立計數)
Trait 可定義屬性。若類中定義同名屬性:
E_STRICT 警告__CLASS__ 返回使用 Trait 的類名,__TRAIT__ 返回 Trait 名。Foo::bar()(若方法未使用 $this)。Traits 是 PHP 代碼復用的強大補充,特別適合「橫向」功能模塊(如日誌、序列化、驗證等)在多個無關類中的共享。合理使用 insteadof/as 解決衝突,注意靜態成員隔離性,即可編寫靈活、可維護的代碼。