一、
- Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。
- Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。
- 无法通过 trait 自身来实例化。
- 它为传统继承增加了水平特性的组合;应用的几个 Class 之间不需要继承。
trait phoneInfo {
function getPhoneName(){}
function getPhoneDesc(){}
}
class Meizu extends Phone{
//用use 调用 trait
use phoneInfo;
}
class Xiaomi extends Phone{
use phoneInfo;
}
二、优先级:类本身成员方法-》triat方法-》基类方法
从基类继承的成员会被 trait 插入的成员所覆盖。
优先顺序是来自当前类的成员方法覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
class Base{
public function sayHello(){
echo 'Hello';
}
}
trait SayWorld{
public function sayHello(){
parent::sayHello();
echo 'World!';
}
}
class MyHelloWorld extends Base{
use SayWorld;//trait的sayHello方法覆盖基类的sayHello方法
}
$o = new MyHelloWorld();
$o->sayHello();
输出
Hello World!
trait HelloWord{
public function sayHello(){
echo 'Hello World!';
}
}
class TheWorldIsNotEnough(){
use HelloWord;
//类本身定义的sayHello方法覆盖了基类的sayHello方法
public function sayHello(){
echo 'Hello Universe!';
}
}
$o = new TheWorldIsNotEnough();
$o->sayHello();
输出
Hello Universe!
三、多个triat使用
通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。
trait Hello {
public function sayHello(){
echo 'Hello';
}
}
trait World{
public function sayWorld(){
echo 'World';
}
}
class MyHelloWorld{
use Hello,World;
public function ayExclamationMark() {
echo '!';
}
}
$o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
输出
Hello World!
四、冲突的解决:就是当两个trait有同名方法时,会产生一个致命错误的冲突
解决方法:需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。
as 操作符可以 为某个方法引入别名。 注意,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;//B中的smallTalk方法替代了A中的smallTalk方法,解决了同名方法冲突
A::bigTalk insteadof B;//A中的bigTalk方法替代了B中的bigTalk方法,解决了冲突
}
}
class Aliased_Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as talk;//将B的bigTalk方法重命名为talk
}
}
?>
五、使用as可以修改方法的访问控制
trait HelloWorld{
public function sayHello(){
echo 'Hello World!';
}
}
class MyClass1{
//修改sayHello的访问控制
use HelloWorld {sayHello as protected;}
}
class MyClass2{
//修改访问控制和方法名
use HelloWorld{sayHello as private myPrivateHello;}
}
六、从trait来组成trait
正如 class 能够使用 trait 一样,其它 trait 也能够使用 trait。在 trait 定义时通过使用一个或多个 trait,能够组合其它 trait 中的部分或全部成员。
trait Hello{
public function Hello(){
echo "Hello";
}
}
trait World{
public function World(){
echo "World!";
}
}
//trait里面允许再使用trait
trait HelloWorld{
use Hello,World;
}
class MyHelloWorld{
use HelloWorld;
}
$o = new MyHelloWorld();
$o->Hello();
$o->World();
输出
Hello World!
七、Trait的抽象成员
为了对使用的类施加强制要求,trait 支持抽象方法的使用。
<?php
trait Hello {
public function sayHelloWorld() {
echo 'Hello'.$this->getWorld();
}
//使用抽象方法,强制引用它的类使用
abstract public function getWorld();
}
class MyHelloWorld {
private $world;
use Hello;
public function getWorld() {
return $this->world;
}
public function setWorld($val) {
$this->world = $val;
}
}
?>
八、Trait的静态成员
Traits 可以被静态成员静态方法定义。
//静态变量
<?php
trait Counter {
public function inc() {
//定义静态变量
static $c = 0;
$c = $c + 1;
echo "$c\n";
}
}
class C1 {
use Counter;
}
class C2 {
use Counter;
}
$o = new C1(); $o->inc(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>
<?php
trait StaticExample {
//定义静态方法
public static function doSomething() {
return 'Doing something';
}
}
class Example {
use StaticExample;
}
Example::doSomething();
?>
九、trait定义属性
Trait 定义了一个属性后,类就不能定义同样名称的属性,否则会产生 fatal error。
有种情况例外:属性是兼容的(同样的访问可见度、初始默认值)。
在 PHP 7.0 之前,属性是兼容的,则会有 E_STRICT 的提醒。
<?php
trait PropertiesTrait {
public $x = 1;
}
class PropertiesExample {
use PropertiesTrait;
}
$example = new PropertiesExample;
$example->x;
?>