我们都知道,PHP是当代化的面向工具措辞。为理解决C++多重继续的混乱问题,大部分措辞都是单继续多接口的形式,但这也会让一些可以复用的代码必须通过组合的办法来实现,如果要用到组合,不可避免的就要实例化类或者利用静态方法,无形中增加了内存的占用。而PHP为理解决这个问题,就正式推出了trait能力。你可以把它看做是组合能力的一种变体。
traitA{public$a='A';publicfunctiontestA(){echo'Thisis'.$this->a;}}classclassA{useA;}classclassB{useA;publicfunction__construct(){$this->a='B';}}$a=newclassA();$b=newclassB();$a->testA();$b->testA();
从上述代码中,我们可以看出,trait可以给运用于任意一个类中,而且可以定义变量,非常方便。trait最须要把稳的是关于同名方法的重载优先级问题。
traitB{functiontest(){echo'ThisistraitB!';}}traitC{functiontest(){echo'ThisistraitC!';}}classtestB{useB,C;functiontest(){echo'ThisisclasstestB!';}}$b=newtestB();$b->test();//ThisisclasstestB!//classtestC{//useB,C;//}//$c=newtestC();//$c->test();//Fatalerror:Traitmethodtesthasnotbeenapplied,becausetherearecollisionswithothertraitmethodsontestC
在这里,我们的类中重载了test()方法,这里输出的便是类中的方法了。如果注释掉testB类中的test()方法,则会报错。由于程序无法区分出你要利用的是哪一个trait中的test()方法。我们可以利用insteadof来指定要利用的方法调用哪一个trait。

classtestE{useB,C{B::testinsteadOfC;C::testastestC;}}$e=newtestE();$e->test();//ThisistraitB!$e->testC();//ThisistraitC!
当然,现实开拓中还是只管即便规范方法名,不要涌现这种重复情形。其余,如果子类引用了trait,而父类又定义了同样的方法呢?当然还是调用父类所继续来的方法。trait的优先级是低于普通的类继续的。
traitD{functiontest(){echo'ThisistraitD!';}}classparentD{functiontest(){echo'ThisisclassparentD';}}classtestDextendsparentD{useD;}$d=newtestD();$d->test();//ThisistraitD!
末了,trait中也是可以定义抽象方法的。这个抽象方法是引用这个trait的类所必须实现的方法,和抽象类中的抽象方法效果同等。
traitF{functiontest(){echo'ThisistraitF!';}abstractfunctionshow();}classtestF{useF;functionshow(){echo'ThisisclasstestF!';}}$f=newtestF();$f->test();$f->show();
trait真的是PHP是非常灵巧的一个功能。当然,越是灵巧的东西越须要我们去弄明白它的一些利用规则,这样才能避免一些不可预见的缺点。
测试代码: https://github.com/zhangyue0503/dev-blog/blob/master/php/201912/source/trait%E8%83%BD%E5%8A%9B%E5%9C%A8PHP%E4%B8%AD%E7%9A%84%E4%BD%BF%E7%94%A8.php
参考文档: https://www.php.net/manual/zh/language.oop5.traits.php