介绍
自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。
Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php trait Hello{ public function echo_hello(){ echo 'hello' ; } } trait World{ public function echo_world(){ echo 'world' ; } } class A{ use Hello,World; } $a = new A(); $a ->echo_hello(); $a ->echo_world(); |
输出:
![仙士可博客](https://i-blog.csdnimg.cn/blog_migrate/a3eaccd287efa10bee63a95b014c4aaa.png)
trait的应用场景
PHP语言使用一种典型的单继承模型,在这种模型中,我们先编写一个通用的根类,实现基本的功能,然后扩展这个根类,创建更具体的子类,直接从父类继承实现。这叫做继承层次结构,很多编程语言都使用这个模式。大多数时候这种典型的继承模型能够良好运作,但是如果想让两个无关的PHP类具有类似的行为,应该怎么做呢?
Trait就是为了解决这种问题而诞生的。Trait能够把模块化的实现方式注入多个无关的类中,从而提高代码复用,符合DRY(Don’t Repeat Yourself) 原则和面对对象单一职责。
使用场景一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php trait Login{ public function login(){ } } trait Register{ public function register(){ } } class Auth{ use Login,Register; public function auth(){ } } $a = new Auth(); |
当系统底层的auth验证时,auth类通过use login,use register,使底层验证类有了登录注册功能,实现了代码复用
使用场景二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | trait Singleton{ protected static $_instance ; public static function getInstance() { if (! (self:: $_instance instanceof self) ) { self:: $_instance = new static (); //new static和new self区别可查看http://www.jb51.net/article/54167.htm } return self:: $_instance ; } } class A{ use Singleton; } class B{ use Singleton; } $a = A::getInstance(); $b = B::getInstance(); var_dump( $a ); var_dump( $b ); |
用trait实现单例模式
trait优先级
一:方法优先级
从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 | trait Test{ public function echo_a(){ echo 'test_a' ; } } class A{ use Test; public function echo_a(){ echo 'a' ; } } $a = new A(); $a ->echo_a(); |
输出 'a';
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | trait Test{ public function echo_a(){ echo 'test_a' ; } } class BaseA{ public function echo_a(){ echo 'base_a' ; } } class A extends BaseA { use Test; } $a = new A(); $a ->echo_a(); |
输出 'test_a';
二:属性优先级
在 PHP 7.0 之前,在类里定义和 trait 同名的属性,哪怕是完全兼容的也会抛出 E_STRICT(完全兼容的意思:具有相同的访问可见性、初始默认值)。
7.0之后,完全相同的属性,不会抛出错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php trait Test{ public $a = 2; static $b =1; } class A { public $a = 2; use Test; } class B { static $b = 1; use Test; } $a = new A(); echo $a ->a; echo "<br>" ; echo B:: $b ; |
输出:
![仙士可博客](https://i-blog.csdnimg.cn/blog_migrate/1284132164872e1768fe59e795e809b0.png)
多个trait冲突
使用逗号,可以use 多个trait;
如果use多个方法名相同的trait并且没做任何处理的话,将会产生一个致命错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php trait Test1{ public function echo_a(){ echo 'test1_a' ; } } trait Test2{ public function echo_a(){ echo 'test2_a' ; } } class A { use Test1,Test2; } $a = new A(); $a ->echo_a(); |
![仙士可博客](https://i-blog.csdnimg.cn/blog_migrate/90f5b35a8b61d867a919c73b728eb631.png)
解决方法:需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <?php trait Test1{ public function echo_a(){ echo 'test1_a' ; } public function echo_b(){ echo 'test1_b' ; } } trait Test2{ public function echo_a(){ echo 'test2_a' ; } public function echo_b(){ echo 'test2_b' ; } } class A { use Test1,Test2{ Test2::echo_a insteadof Test1; Test1::echo_b insteadof Test2; } } $a = new A(); $a ->echo_a(); echo "\n" ; $a ->echo_b(); |
输出:![仙士可博客](https://i-blog.csdnimg.cn/blog_migrate/c531da66c8a04f46055d345cd917fae1.png)
as操作符
一:当多个trait冲突,使用insteadof排除其中一个时,可使用as操作符赋予别名进行调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?php trait Test1{ public function echo_a(){ echo 'test1_a' ; } public function echo_b(){ echo 'test1_b' ; } } trait Test2{ public function echo_a(){ echo 'test2_a' ; } public function echo_b(){ echo 'test2_b' ; } } class A { use Test1,Test2{ Test2::echo_a insteadof Test1; Test1::echo_b insteadof Test2; Test2::echo_b as echo_b_2; //赋予别名 } } $a = new A(); $a ->echo_a(); echo "\n" ; $a ->echo_b(); echo "\n" ; $a ->echo_b_2(); |
输出:![仙士可博客](https://i-blog.csdnimg.cn/blog_migrate/e24ace7da8588053fcd37551f8b24dd4.png)
二:使用as操作符修改访问权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class A { public $a = 1; use Test{ Test::echo_a as private ; } public function want_echo_a(){ $this ->echo_a(); } } $a = new A(); //$a->echo_a();//直接调用将会报错 $a ->want_echo_a(); |
输出结果:
![仙士可博客](https://i-blog.csdnimg.cn/blog_migrate/c051c9a80e7bc13119cbe651d2383938.png)
Trait 的抽象成员,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php trait Test{ public function __construct() { $this ->echo_test(); } abstract function echo_test(); // } class A { public $a = 2; use Test; public function echo_test() { echo '1' ; } } $a = new A(); |
输出:![仙士可博客](https://i-blog.csdnimg.cn/blog_migrate/ff7d09387c39da5c8d24690976e8dff2.png)
Trait的静态成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?php trait Test{ public function echo_test() { static $a =1; $a ++; echo $a ; } } class A { use Test; } class B { use Test; } $a = new A(); $b = new B(); $a ->echo_test(); echo "\n" ; $a ->echo_test(); echo "\n" ; $b ->echo_test(); echo "\n" ; $b ->echo_test(); |
输出![仙士可博客](https://i-blog.csdnimg.cn/blog_migrate/e63e0cca9d6c582a8f8d37548d138e0c.png)
静态成员基本和http://www.php20.cn/article/sw/%E9%9D%99%E6%80%81/55 说明一致
以上就是关于trait的说明以及用法了
转载来自仙士可博客www.php20.cn