trait PHP 實現(xiàn)了一種代碼復用的方法(PHP5.4.0起)

自 PHP 5.4.0 起,PHP 實現(xiàn)了一種代碼復用的方法,稱為 trait。

Trait 是為類似 PHP 的單繼承語言而準備的一種代碼復用機制球化。Trait 為了減少單繼承語言的限制逮刨,使開發(fā)人員能夠自由地在不同層次結(jié)構(gòu)內(nèi)獨立的類中復用 method呕缭。Trait 和 Class 組合的語義定義了一種減少復雜性的方式,避免傳統(tǒng)多繼承和 Mixin 類相關(guān)典型問題。
Trait 和 Class 相似恢总,但僅僅旨在用細粒度和一致的方式來組合功能迎罗。 無法通過 trait 自身來實例化。它為傳統(tǒng)繼承增加了水平特性的組合片仿;也就是說纹安,應用的幾個 Class 之間不需要繼承。

Example #1 Trait 示例

<?php
trait ezcReflectionReturnInfo {
    function getReturnType() { /*1*/ }
    function getReturnDescription() { /*2*/ }
}

class ezcReflectionMethod extends ReflectionMethod {
    use ezcReflectionReturnInfo;
    /* ... */
}

class ezcReflectionFunction extends ReflectionFunction {
    use ezcReflectionReturnInfo;
    /* ... */
}
?>
優(yōu)先級 
從基類繼承的成員會被 trait 插入的成員所覆蓋滋戳。優(yōu)先順序是來自當前類的成員覆蓋了 trait 的方法钻蔑,而 trait 則覆蓋了被繼承的方法。

Example #2 優(yōu)先順序示例

從基類繼承的成員被插入的 SayWorld Trait 中的 MyHelloWorld 方法所覆蓋奸鸯。其行為 MyHelloWorld 類中定義的方法一致咪笑。優(yōu)先順序是當前類中的方法會覆蓋 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!

Example #3 另一個優(yōu)先級順序的例子

<?php
trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

class TheWorldIsNotEnough {
    use HelloWorld;
    public function sayHello() {
        echo 'Hello Universe!';
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHello();
?>
以上例程會輸出:

Hello Universe!

多個 trait

通過逗號分隔窗怒,在 use 聲明列出多個 trait,可以都插入到一個類中蓄拣。

Example #4 多個 trait 的用法
<?php
trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo 'World';
    }
}

class MyHelloWorld {
    use Hello, World;
    public function sayExclamationMark() {
        echo '!';
    }
}

$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>
以上例程會輸出:

Hello World!
沖突的解決 

如果兩個 trait 都插入了一個同名的方法扬虚,如果沒有明確解決沖突將會產(chǎn)生一個致命錯誤。

為了解決多個 trait 在同一個類中的命名沖突球恤,需要使用 insteadof 操作符來明確指定使用沖突方法中的哪一個辜昵。

以上方式僅允許排除掉其它方法,as 操作符可以將其中一個沖突的方法以另一個名稱來引入咽斧。

Example #5 沖突的解決

在本例中 Talker 使用了 trait A 和 B堪置。由于 A 和 B 有沖突的方法,其定義了使用 trait B 中的 smallTalk 以及 trait A 中的 bigTalk张惹。

Aliased_Talker 使用了 as 操作符來定義了 talk 來作為 B 的 bigTalk 的別名舀锨。
<?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;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}
?>

修改方法的訪問控制

使用 as 語法還可以用來調(diào)整方法的訪問控制。
Example #6 修改方法的訪問控制
<?php
trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

// 修改 sayHello 的訪問控制
class MyClass1 {
    use HelloWorld { sayHello as protected; }
}

// 給方法一個改變了訪問控制的別名
// 原版 sayHello 的訪問控制則沒有發(fā)生變化
class MyClass2 {
    use HelloWorld { sayHello as private myPrivateHello; }
}
?>

從 trait 來組成 trait

正如 class 能夠使用 trait 一樣宛逗,其它 trait 也能夠使用 trait坎匿。在 trait 定義時通過使用一個或多個 trait,能夠組合其它 trait 中的部分或全部成員雷激。
Example #7 從 trait 來組成 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 的抽象成員

為了對使用的類施加強制要求替蔬,trait 支持抽象方法的使用。
Example #8 表示通過抽象方法來進行強制要求
<?php
trait Hello {
    public function sayHelloWorld() {
        echo 'Hello'.$this->getWorld();
    }
    abstract public function getWorld();
}

class MyHelloWorld {
    private $world;
    use Hello;
    public function getWorld() {
        return $this->world;
    }
    public function setWorld($val) {
        $this->world = $val;
    }
}
?>

Trait 的靜態(tài)成員

Traits 可以被靜態(tài)成員靜態(tài)方法定義屎暇。
Example #9 靜態(tài)變量

<?php

trait Counter {
    public function inc() {
        static $c = 0;
        $c = $c + 1;
        echo "$c\n";
    }
}

class C1 {
    use Counter;
}

class C2 {
    use Counter;
}

$o = new C1(); $o->inc(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>

Example #10 靜態(tài)方法

<?php
trait StaticExample {
    public static function doSomething() {
        return 'Doing something';
    }
}

class Example {
    use StaticExample;
}

Example::doSomething();
?>

屬性

Trait 同樣可以定義屬性进栽。
Example #11 定義屬性
<?php
trait PropertiesTrait {
    public $x = 1;
}

class PropertiesExample {
    use PropertiesTrait;
}

$example = new PropertiesExample;
$example->x;
?>
如果 trait 定義了一個屬性,那類將不能定義同樣名稱的屬性恭垦,否則會產(chǎn)生一個錯誤。如果該屬性在類中的定義與在 trait 中的定義兼容(同樣的可見性和初始值)則錯誤的級別是 E_STRICT,否則是一個致命錯誤番挺。

Example #12 解決沖突

<?php
trait PropertiesTrait {
    public $same = true;
    public $different = false;
}
class PropertiesExample {
    use PropertiesTrait;
    public $same = true; // Strict Standards
    public $different = true; // 致命錯誤
}
?>

單例

trait singleton {
    /**
     * private construct, generally defined by using class
     */
    //private function __construct() {}

    public static function getInstance() {
        static $_instance = NULL;
        $class = __CLASS__;
        return $_instance ? : $_instance = new $class;
    }

    public function __clone() {
        trigger_error('Cloning '.__CLASS__.' is not allowed.',E_USER_ERROR);
    }

    public function __wakeup() {
        trigger_error('Unserializing '.__CLASS__.' is not allowed.',E_USER_ERROR);
    }
}

class foo {
    use singleton;
    private function __construct() {
        $this->name = 'foo';
    }
}

class bar {
    use singleton;
    private function __construct() {
        $this->name = 'bar';
    }
}

$foo = foo::getInstance();
echo $foo->name;

$bar = bar::getInstance();
echo $bar->name;

來源:http://php.net/manual/zh/language.oop5.traits.php#language.oop5.traits.multiple

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唠帝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子玄柏,更是在濱河造成了極大的恐慌襟衰,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粪摘,死亡現(xiàn)場離奇詭異瀑晒,居然都是意外死亡,警方通過查閱死者的電腦和手機徘意,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門苔悦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人椎咧,你說我怎么就攤上這事玖详。” “怎么了勤讽?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵蟋座,是天一觀的道長。 經(jīng)常有香客問我脚牍,道長向臀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任诸狭,我火速辦了婚禮券膀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘作谚。我一直安慰自己三娩,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布妹懒。 她就那樣靜靜地躺著雀监,像睡著了一般。 火紅的嫁衣襯著肌膚如雪眨唬。 梳的紋絲不亂的頭發(fā)上会前,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音匾竿,去河邊找鬼瓦宜。 笑死,一個胖子當著我的面吹牛岭妖,可吹牛的內(nèi)容都是我干的临庇。 我是一名探鬼主播反璃,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼假夺!你這毒婦竟也來了淮蜈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤已卷,失蹤者是張志新(化名)和其女友劉穎梧田,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侧蘸,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡裁眯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了讳癌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片穿稳。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖析桥,靈堂內(nèi)的尸體忽然破棺而出司草,到底是詐尸還是另有隱情,我是刑警寧澤泡仗,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布埋虹,位于F島的核電站,受9級特大地震影響娩怎,放射性物質(zhì)發(fā)生泄漏搔课。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一截亦、第九天 我趴在偏房一處隱蔽的房頂上張望爬泥。 院中可真熱鬧,春花似錦崩瓤、人聲如沸袍啡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽境输。三九已至,卻和暖如春颖系,著一層夾襖步出監(jiān)牢的瞬間嗅剖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工嘁扼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留信粮,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓趁啸,卻偏偏與公主長得像强缘,于是被迫代替她去往敵國和親督惰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理欺旧,服務發(fā)現(xiàn)姑丑,斷路器,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 1.PHP 對待對象的方式與引用和句柄相同辞友,即每個變量都持有對象的引用,而不是整個對象的拷貝震肮。 2.class 中...
    我在太行山下閱讀 540評論 0 0
  • 今天下午爸爸帶著我和妹妹一起去海里游泳称龙,我們起床穿上游泳衣,我們起床穿好游泳衣就出發(fā)了戳晌,我們走到了公交站臺鲫尊,爸爸說...
    室是陋室閱讀 469評論 0 0
  • 事件:今天算十月份的收入,還是虧損的狀態(tài)沦偎。 思維空性: 我曾種過我很不尊重他人財物的事情疫向。比如偷拿學校的小用品啊豪嚎!...
    黛兒微笑閱讀 354評論 0 0
  • 用 我的刻骨銘心 去愛你 再用 你的若無其事 去忘記 我的今天應該灑脫很多 只是可惜 像你 我學不會…… ...
    尋找米子閱讀 144評論 0 0