trait
是PHP為類只能單繼承而實現的代碼復用機制。
trait
不能繼承,也不能實現接口。
trait
不能有常量。
trait
不能實例化。
trait
中可以使用 trait
。
trait
中可以使用抽象方法。
trait
中通過 insteadof
來解決命名沖突,也可以使用 as
來設置別名和訪問級別。
trait
中的優先級為 自己 > trait
> 父類。
trait
和類中如果有同名的屬性,那么需要保證他們的訪問級別和值相同才不會報錯。
一個簡單的 trait
例子
// trait
trait One{
// 普通屬性
public $name = 'One';
// 不能有常量
// 靜態屬性
static $desc = 'Hello One';
// 普通方法
public function say(){
echo $this->name . ': <br />';
echo static::$desc . '<br />';
}
}
// 類
class Foobar{
// 引入 trait,可以引入多個,用逗號分開
use One;
// 構造函數
public function __construct(){
// 調用trait 中的函數
$this->say();
}
}
// 實例化
new Foobar();
當子類繼承了父類,同時引入了 trait
,三者中如果有同名的成員(屬性、方法),那么優先級從大到小是 自己 > trait
> 父類。
// trait
trait One{
public function fn(){
echo "One::fn";
}
}
// parent
class Two{
public function fn(){
echo "Two::fn";
}
}
// class
class Foobar extends Two{
use One;
public function fn(){
echo "Foobar::fn";
}
}
// 實例化 輸出:Foobar::fn
(new Foobar())->fn();
當一個類使用了多個trait
時,難免會發生 trait
之間會有命名沖突的問題,如果不解決,會報錯。
// trait
trait One{
public function foo(){
echo "One::foo <br />";
}
public function bar(){
echo "One::bar <br />";
}
}
trait Two{
public function foo(){
echo "Two::foo <br />";
}
public function bar(){
echo "Two::bar <br />";
}
}
trait Three{
public function foo(){
echo "Three::foo <br />";
}
public function bar(){
echo "Three::bar <br />";
}
}
// class
class Four{
// 引入 trait
use One, Two, Three {
// 當 One 中的 foo方法 存在沖突時,排除掉 Two和Three 中的方法
One::foo insteadof Two, Three;
// 當 Two 中的 bar方法 存在沖突時,排除掉 One和Three 中的方法
Two::bar insteadof One, Three;
// 還一種方式是給方法起個別名
Three::foo as tf;
}
public function call(){
$this->foo();
$this->bar();
$this->tf();
}
}
// 實例化
(new Four())->call();
/*
最終輸出
One::foo
Two::bar
Three::foo
*/
當一個 trait
中的方法訪問級別較低時,可以通過 as
給它重新設置訪問級別
// trait
trait One{
// 我是私有的,只能在類中使用,無法通過實例訪問
private function say(){
echo "private <br />";
}
}
// 類
class Foobar{
use One {
// 調整方法的訪問控制級別
say as public;
}
}
// 實例化
(new Foobar())->say();
可以給 trait
添加抽象方法來強制要求使用者必須做一些事。
// trait
trait One{
public function fn1(){
return $this;
}
// 我是抽象方法
public abstract function fn2();
}
// 類
class Foobar{
use One;
// 必須要實現抽象方法
public function fn2(){
echo "hello world";
}
}
// 實例化
(new Foobar())->fn1()->fn2();