[关闭]
@a5635268 2017-04-03T20:24:17.000000Z 字数 3144 阅读 1240

PHP类多继承的替代方案Traits

PHP


概述

traits是PHP5.4新进入的特性,其目的就是解决PHP的类不能多继承的问题。Traits不是类!不能被实例化。可以理解为一组能被不同的类都能调用到的方法集合。只需要在类中使用关键词use引入即可,可引入多个Traits,用','隔开。

简单使用

  1. trait myTrait{
  2. public $traitPublic = 'public';
  3. protected $traitProtected = 'protected';
  4. function traitMethod1()
  5. {
  6. echo __METHOD__,PHP_EOL;
  7. }
  8. function traitMethod2()
  9. {
  10. echo __METHOD__,PHP_EOL;
  11. }
  12. }
  13. class myClass{
  14. use myTrait;
  15. }
  16. $obj = new myClass();
  17. $obj->traitMethod1();
  18. $obj->traitMethod2();
  19. // ↓↓ 只能调用public的属性和方法; protected以及private只供在traits内部自己调用;
  20. echo $obj->traitPublic;

优先级问题

Trait会覆盖继承的方法,当前类会覆盖Trait方法。即 继承的方法 < Traits方法 < 当前类方法,

  1. trait A{
  2. public $var1 = 'test';
  3. public function test()
  4. {
  5. echo 'A::test()';
  6. }
  7. public function test1()
  8. {
  9. echo 'A::test1()';
  10. }
  11. }
  12. class B{
  13. public function test()
  14. {
  15. echo 'B::test()';
  16. }
  17. public function test1()
  18. {
  19. echo 'B::test1()';
  20. }
  21. }
  22. class C extends B{
  23. use A;
  24. public function test()
  25. {
  26. echo 'c::test()';
  27. }
  28. }
  29. $c = new C();
  30. $c->test(); //c::test() Traits方法 < 当前类方法
  31. $c->test1(); //A::test1() 继承的方法 < Traits方法

多个Trait冲突问题

  1. 如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。
  2. 为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。
  3. 可用as操作符将其中一个冲突方法另起名;
  1. trait A{
  2. public function test()
  3. {
  4. echo 'A::test()';
  5. }
  6. }
  7. trait B{
  8. public function test()
  9. {
  10. echo 'B::test()';
  11. }
  12. }
  13. class C{
  14. use A , B {
  15. B::test insteadof A; //明确B替代A
  16. B::test as t; //或者另起一个名字
  17. }
  18. }
  19. $c = new C();
  20. $c->test(); //B::test()
  21. $c->t(); //B::test() 可以用as另起名

as可用来修改方法访问控制

  1. trait HelloWorld{
  2. public function sayHello()
  3. {
  4. echo 'Hello World!';
  5. }
  6. }
  7. // 修改 sayHello 的访问控制
  8. class A{
  9. use HelloWorld {
  10. sayHello as protected;
  11. }
  12. }
  13. // 给方法一个改变了访问控制的别名
  14. // 原版 sayHello 的访问控制则没有发生变化
  15. class B{
  16. use HelloWorld {
  17. sayHello as private myPrivateHello;
  18. }
  19. }
  20. $a = new A();
  21. $a->sayHello(); //Fatal error: Call to protected method A::sayHello() from context ''; 改变了sayHello的访问规则;
  22. $b = new B();
  23. $b->sayHello(); //Hello World!

Trait中使用Trait

  1. trait Hello{
  2. public function sayHello()
  3. {
  4. echo 'Hello ';
  5. }
  6. }
  7. trait World{
  8. public function sayWorld()
  9. {
  10. echo 'World!';
  11. }
  12. }
  13. trait HelloWorld{
  14. use Hello , World;
  15. }
  16. class MyHelloWorld{
  17. use HelloWorld;
  18. }
  19. $o = new MyHelloWorld();
  20. $o->sayHello();
  21. $o->sayWorld(); // Hello World!

Trait中抽象成员

为了对使用的类施加强制要求,trait 支持抽象方法的使用。

  1. trait Hello{
  2. public function sayHelloWorld()
  3. {
  4. echo 'Hello' . $this->getWorld();
  5. }
  6. abstract public function getWorld();
  7. }
  8. class MyHelloWorld{
  9. private $world;
  10. use Hello;
  11. // 必须要实现trait里面的抽象方法,否则Fatal error: Class MyHelloWorld contains 1 abstract method and must therefore be declared abstract or implement the remaining methods
  12. public function getWorld()
  13. {
  14. return $this->world;
  15. }
  16. public function setWorld($val)
  17. {
  18. $this->world = $val;
  19. }
  20. }
  21. $obj = new MyHelloWorld();
  22. echo $obj->setWorld();

Trait中静态成员

Traits 可以被静态成员静态方法定义,不可以直接定义静态变量,但静态变量可被trait方法引用.

  1. # 静态属性;
  2. trait Counter {
  3. public function inc() {
  4. static $c = 0;
  5. $c = $c + 1;
  6. echo "$c\n";
  7. }
  8. }
  9. class C1 {
  10. use Counter;
  11. }
  12. class C2 {
  13. use Counter;
  14. }
  15. $o = new C1();
  16. $o->inc(); // echo 1
  17. $o->inc(); // echo 2;
  18. $p = new C2();
  19. $p->inc(); // echo 1
  20. # 静态方法
  21. trait StaticExample {
  22. public static function doSomething() {
  23. echo 'Doing something';
  24. }
  25. }
  26. class Example {
  27. use StaticExample;
  28. }
  29. Example::doSomething(); // Doing something

Trait中属性

  1. trait PropertiesTrait{
  2. public $x = 1;
  3. }
  4. class PropertiesExample{
  5. use PropertiesTrait;
  6. }
  7. $example = new PropertiesExample;
  8. echo $example->x; // 1

如果 trait 定义了一个属性,那类将不能定义同样名称的属性,否则会产生一个错误。如果该属性在类中的定义与在 trait 中的定义兼容(同样的可见性和初始值)则错误的级别是 E_STRICT,否则是一个致命错误。

  1. trait PropertiesTrait {
  2. public $same = true;
  3. public $different = false;
  4. }
  5. class PropertiesExample {
  6. use PropertiesTrait;
  7. public $same = true; // Strict Standards
  8. public $different = true; // 致命错误
  9. }

参考链接:

http://www.php.net/manual/zh/language.oop5.traits.php

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注