在看Yii的文档,Yii2用到了一个叫Traits的东西,查询文档可以知道自PHP5.4起,PHP实现了一个代码复用的方法,就是traits。官方的解释一大堆,翻译过来的晦涩难懂,我理解的意思就是——PHP是单继承语言,为了在不同结构的类里面复用一些方法,且不必像多继承类那样复杂,就使用了Traits来解决这类问题。简而言之,traits就是实现代码复用的方法。
1.使用简介
使用起来很简单,这个方法的官方名字是Traits,使用的时候是trait:
trait zz{
function showMe(){
echo "show me something in trails";
}
}
class ZzTest{
use zz;
}
$test = new ZzTest();
$test->showMe();
这里最终会打印出:
show me something in trails
从基类继承的子类里使用traits的,会有一个覆盖的问题,我们看下面的代码。
trait zz{
function showMe(){
parent::showMe();
echo "show me something in trails";
}
}
class Father{
function showMe(){
echo "show me in Father<br/>";
}
}
class ZzTest extends Father{
use zz;
}
$test = new ZzTest();
$test->showMe();
最终会打印出:
show me in Father
show me something in trails
trait中的方法覆盖了父类的方法,所以执行的是trait中的showMe(),这里可以理解成子类中得到了trait作为自己的方法,覆盖了父类的方法。然后再看下面的代码:
trait zz{
function showMe(){
parent::showMe();
echo "show me something in trails";
}
}
class Father{
function showMe(){
echo "show me in Father<br/>";
}
}
class ZzTest extends Father{
use zz;
public function showMe(){
echo "show me MySelf";
}
}
$test = new ZzTest();
$test->showMe();
结果会打印出:
show me MySelf
子类中会覆盖Trait中的方法,优先级关系为子类 > trait> 基类。
2.多个trait
use声明时,列出多个trait,可以都插入到一个类中。
trait zz{
function sayMe(){
echo "Say me.";
}
}
trait nn{
function sayYou(){
echo "Say you,";
}
}
class ZzTest{
use zz,nn;
public function sayIt(){
echo "Say it for always.";
}
}
$test = new ZzTest();
$test->sayYou();
$test->sayMe();
$test->sayIt();
结果打印出:
Say you,Say me.Say it for always.
如果trait中有重名的方法,会造成冲突:
trait zz{
function sayMe(){
echo "Say me.";
}
function sayYou(){
echo "sAY YOU,";
}
}
trait nn{
function sayYou(){
echo "Say you,";
}
function sayMe(){
echo "sAY ME";
}
}
class ZzTest{
use zz,nn;
public function sayIt(){
echo "Say it for always.";
}
}
最终会报错:
Fatal error
: Trait method sayYou has not been applied, because there are collisions with other trait methods on ZzTest
解决这个问题可以将同名的方法指定给另一个trait。
trait zz{
function sayMe(){
echo "Say me.";
}
function sayYou(){
echo "sAY YOU,";
}
}
trait nn{
function sayYou(){
echo "Say you,";
}
function sayMe(){
echo "sAY ME.";
}
}
class ZzTest{
use zz,nn{
nn::sayYou insteadof zz;
zz::sayMe insteadof nn;
}
public function sayIt(){
echo "Say it for always.";
}
}
$test = new ZzTest();
$test->sayYou();
$test->sayMe();
$test->sayIt();
指定使用trait nn中的sayYou(),和zz中的sayMe();最终打印出来也是:
Say you,Say me.Say it for always.这里是指定使用哪一个,如果也想用另一个trait中的同名方法,就可以用as操作符将重名方法起一个别名,在之后的调用中使用别名。
use zz,nn{
nn::sayYou insteadof zz;
zz::sayMe insteadof nn;
zz::sayYou as sayYounn;
nn::sayMe as asMenn;
}
起了别名之后在之后就可以调用他们
$test->sayYouzz();
$test->sayMenn();
//打印结果:sAY YOU,sAY ME.
3.方法的访问控制
trait默认的访问控制是public,也可以修改。这里需要知道一点,trait里的代码可以看作是孤立的代码段,然后拿这些放到类里面去用。类里面是如何定义的,这里就可以怎么写。
trait zz{
public function sayYou(){
$this->sayMe();
}
protected function sayMe(){
echo "Say me.";
}
}
class ZzTest{
use zz;
public function sayIt(){
echo "Say it for always.";
}
}
$test = new ZzTest();
$test->sayYou();
像这样是可行的,打印出:Say me.
用as就可以修改访问控制:
trait zz{
protected function sayMe(){
echo "Say me.";
}
}
class ZzTest{
use zz{
sayMe as public;
}
public function sayIt(){
echo "Say it for always.";
}
}
$test = new ZzTest();
$test->sayMe();
4.trait组成trait
正如类能够使用 trait 一样,其它 trait 也能够使用 trait。在 trait 定义时通过使用一个或多个 trait,它能够组合其它 trait 中的部分或全部成员。
trait zz{
function sayMe(){
echo "Say me.";
}
}
trait nn{
function sayYou(){
echo "Say you,";
}
}
trait wj{
use zz,nn;
}
class ZzTest{
use wj;
}
$test = new ZzTest();
$test->sayYou();
$test->sayMe();
最后也是一样打印出:Say you,Say me.
5.trait的抽象成员
为了对使用trait的类施加强制要求,trait 支持抽象方法的使用。trait zz{
function sayYou(){
echo "Say you,".$this->sayMe();
}
abstract public function sayMe();
}
class ZzTest{
private $song = "Say me.";
use zz;
public function sayMe(){
return $this->song;
}
}
$test = new ZzTest();
$test->sayYou();
//结果依然是打印出:Say you,Say me.
6.其他特性
Traits 可以被静态成员静态方法定义,也可以定义属性。
trait zz{
public $name = 'Say it for always.<br/>';
function sayYou(){
static $song = 'Say you,';
echo $song.'Say me.';
}
public static function sing(){
echo 'That\'s the way it should be.';
}
}
class ZzTest{
use zz;
}
$test = new ZzTest();
$test->sayYou();
echo $test->name;
$test->sing();
最后打印结果送给你:
Say you,Say me.Say it for always.
That's the way it should be.