Traits是在5.4中新增的一個用來實現程式碼重用的方法。
php是一種單一繼承的語言,我們無法像java一樣在一個class中extends多個基底類別來實現程式碼重複使用,現在Traits能解決這個程式碼重用的問題,它能讓開發者在多個不同的class中實作程式碼重用。
Traits和class在語意的定義上都是為了減少程式碼的複雜性,避免多重繼承的問題。
Traits 和class相似,但是僅用於以統一和較細粒度的方式來提供一組功能,在Traits內部無法進行實例化,即不存在類似class的構造函數__construct()。 Traits作為一個php傳統繼承的擴展並實現水平整合;因此,在應用程式的class中可以不再需要繼承。
1)如何使用
在類別中用關鍵字'use' 來引用 Traits。多個Traits 用','隔開。
實例代碼如下:
<?php trait ezcReflectionReturnInfo { function getReturnType() { } function getReturnDescription() { } }class ezcReflectionMethod extends ReflectionMethod { use ezcReflectionReturnInfo; /* ... */ } class ezcReflectionFunction extends ReflectionFunction { use ezcReflectionReturnInfo; /* ... */ } ?>
2)優先權
基類中的成員函數將被Traits中的函數覆蓋,當前類別中的成員將覆蓋Traits中的函數。
<?php class Base { public function sayHello() { echo 'Hello '; } } trait SayWorld { public function sayHello() { parent::sayHello(); echo "World!\n"; } } class MyHelloWorld extends Base { use SayWorld; } class MyHelloWorldExt extends Base { use SayWorld; public function sayHello() { /** * 这里是5.4中的新的改动,5.4之前的版本会提示: * PHP Fatal error: Cannot use string offset as an array * 5.4中改进为返回字符串中的该索引号的字符 */ $str = "Arvin"; echo $str[0][0];// echo 'A'; } public function shortArray() { $array = ['first', 2, 3, 4];//5.4中的数组简单语法 echo $array[0];//5.4中的数组解引用取数组元素方法 } } $o = new MyHelloWorld(); $o->sayHello(); $oe = new MyHelloWorldExt(); $oe->sayHello(); echo "\n"; $oe->shortArray();
輸出:
Hello World! A first
3)多Traits
多個Traits可以添加到一個class的聲明中,多個Traits之間用","隔開。
<?php trait Hello { public function sayHello() { echo 'Hello '; } } trait World { public function sayWorld() { echo 'World'; } } class MyHelloWorld { use Hello, World; } $o = new MyHelloWorld(); $o->sayHello(); $o->sayWorld(); ?>
輸出結果:
Hello World
4)多Traits衝突
如果添加到同一個class的兩個Traits中有相同的函數名稱,且沒有明確的進行處理,將產生一個錯誤。
為了解決同一個類別中兩個Tratis中的同名函數衝突,需要用insteadof操作符來選擇正確的函數。
因為方法的唯一性和排他性,'as'運算子允許用在衝突函數之後以解決內部衝突的問題。
<?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; } } ?>
上面的範例中,Talker使用Traits A 和B,因此兩者中相同的函數名稱有衝突。
alker中定義了smallTalk取自Traits B,bigTalk取自Traits A。
Aliased_Talker中透過使用as操作符來確保Traits B中的bigTalk透過別名talk來實現。
5)改變函數存取權
我們可以使用as語法來改變Traits中函數的存取權屬性。
<?php trait HelloWorld { public function sayHello() { echo 'Hello World!'; } } // Change visibility of sayHello,改变sayHello的访问权限。 class MyClass1 { use HelloWorld { sayHello as protected; } } // Alias method with changed visibility // sayHello visibility not changed,设置别名myPrivateHello。 class MyClass2 { use HelloWorld { sayHello as private myPrivateHello; } } ?>
6)Traits組成新Traits
就像許多類別一樣可以在類別中使用Traits,Traits中一樣可以使用Traits。可以在一個Traits中定義一個或多個Traits,這些Traits 可以作為部分或全部成員被定義在其他Traits。
<?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!
7)抽象Trait成員
為了在類別中強制實作某些方法,可以在Traits中使用抽象方法。
例如:
<?php trait Hello { public function sayHelloWorld() { echo 'Hello '.$this->getWorld(); } abstract public function getWorld(); } class MyHelloWorld { private $world; use Hello; public function __construct($world) { $this->world = $world; } public function getWorld() { return $this->world; } } /** * 这里用到了5.4新功能 类实例化解引用操作 * (new class())->method(); */ (new MyHelloWorld('Arvin'))->sayHelloWorld(); ?>
這個實例輸出:
Hello Arvin
8)靜態Trait成員
在Traits中無法定義static 靜態變數,但是可以定義在Tratis的函數中。 Tratis中同樣可以定義靜態函數。
<?php trait Counter { public function inc() { static $c = 0;//静态变量 $c += 1; echo "$c\n"; } /** * 静态方法 */ public static function doSomething() { echo 'Doing something'; } } class C1 { use Counter; } (new C1())->inc(); // echo 1 C1::doSomething(); ?> 输出为: 1 Doing something
9) Traits 定義屬性
如果在一個trait中定義了一個屬性,則在引用該trait的類別中不能定義同名的屬性,如果該類別中定義有和trait中已定義屬性具有相同的名字和存取可見性,則是一個E_STRICT 提示,否則拋出語法錯誤。
<?php trait PropertiesTrait { public $x = 1; public $y = 2; } class PropertiesExample { use PropertiesTrait; public $x = 1; //public $y = 3; } $example = new PropertiesExample; echo $example->x, $example->y; ?>
輸出:
12
在最後貼上php5.4.0部分新功能changelog:
Added short array syntax support ([1,2,3]), see UPGRADING guide for full details. Added binary numbers format (0b001010). Added support for Class::{expr}() syntax. Added support for Traits.//本文的主要内容 Added closure $this support back. Added array dereferencing support.//数组解引用支持,上文中有实例 Added callable typehint. Added indirect method call through array. #47160. Added DTrace support.//传说DTrace是一个性能分析工具,可以跟踪出函数调用点,返回点等数据 Added class member access on instantiation (e.g. (new foo)->bar()) support.//类新实例解引用操作,上文中有实例
本文旨在拋磚引玉,希望大家一起繼續探究php5.4的新功能。 ^_^