详解PHP中的Traits


Posted in PHP onJuly 29, 2015

PHP是单继承的语言,在PHP 5.4 Traits出现之前,PHP的类无法同时从两个基类继承属性或方法。php的Traits和Go语言的组合功能类似,通过在类中使用use关键字声明要组合的Trait名称,而具体某个Trait的声明使用trait关键词,Trait不能直接实例化。具体用法请看下面的代码:

<?php
  trait Drive {
    public $carName = 'trait';
    public function driving() {
      echo "driving {$this->carName}\n";
    }
  }
  class Person {
    public function eat() {
      echo "eat\n";
    }
  }
  class Student extends Person {
    use Drive;
    public function study() {
      echo "study\n";
    }
  }
  $student = new Student();
  $student->study();
  $student->eat();
  $student->driving();

输出结果如下:

study
eat
driving trait

上面的例子中,Student类通过继承Person,有了eat方法,通过组合Drive,有了driving方法和属性carName。

如果Trait、基类和本类中都存在某个同名的属性或者方法,最终会保留哪一个呢?通过下面的代码测试一下:

<?php 
  trait Drive {
    public function hello() {
      echo "hello drive\n";
    }
    public function driving() {
      echo "driving from drive\n";
    }
  }
  class Person {
    public function hello() {
      echo "hello person\n";
    }
    public function driving() {
      echo "driving from person\n";
    }
  }
  class Student extends Person {
    use Drive;
    public function hello() {
      echo "hello student\n";
    }
  }
  $student = new Student();
  $student->hello();
  $student->driving();

输出结果如下:

hello student
driving from drive

因此得出结论:当方法或属性同名时,当前类中的方法会覆盖 trait的 方法,而 trait 的方法又覆盖了基类中的方法。

如果要组合多个Trait,通过逗号分隔 Trait名称:

use Trait1, Trait2;

如果多个Trait中包含同名方法或者属性时,会怎样呢?答案是当组合的多个Trait包含同名属性或者方法时,需要明确声明解决冲突,否则会产生一个致命错误。

<?php
trait Trait1 {
  public function hello() {
    echo "Trait1::hello\n";
  }
  public function hi() {
    echo "Trait1::hi\n";
  }
}
trait Trait2 {
  public function hello() {
    echo "Trait2::hello\n";
  }
  public function hi() {
    echo "Trait2::hi\n";
  }
}
class Class1 {
  use Trait1, Trait2;
}

输出结果如下:

PHP Fatal error: Trait method hello has not been applied, because there are collisions with other trait methods on Class1 in ~/php54/trait_3.php on line 20

使用insteadof和as操作符来解决冲突,insteadof是使用某个方法替代另一个,而as是给方法取一个别名,具体用法请看代码:

<?php
trait Trait1 {
  public function hello() {
    echo "Trait1::hello\n";
  }
  public function hi() {
    echo "Trait1::hi\n";
  }
}
trait Trait2 {
  public function hello() {
    echo "Trait2::hello\n";
  }
  public function hi() {
    echo "Trait2::hi\n";
  }
}
class Class1 {
  use Trait1, Trait2 {
    Trait2::hello insteadof Trait1;
    Trait1::hi insteadof Trait2;
  }
}
class Class2 {
  use Trait1, Trait2 {
    Trait2::hello insteadof Trait1;
    Trait1::hi insteadof Trait2;
    Trait2::hi as hei;
    Trait1::hello as hehe;
  }
}
$Obj1 = new Class1();
$Obj1->hello();
$Obj1->hi();
echo "\n";
$Obj2 = new Class2();
$Obj2->hello();
$Obj2->hi();
$Obj2->hei();
$Obj2->hehe();

输出结果如下:

Trait2::hello
Trait1::hi

Trait2::hello
Trait1::hi
Trait2::hi
Trait1::hello

as关键词还有另外一个用途,那就是修改方法的访问控制:

<?php
  trait Hello {
    public function hello() {
      echo "hello,trait\n";
    }
  }
  class Class1 {
    use Hello {
      hello as protected;
    }
  }
  class Class2 {
    use Hello {
      Hello::hello as private hi;
    }
  }
  $Obj1 = new Class1();
  $Obj1->hello(); # 报致命错误,因为hello方法被修改成受保护的
  $Obj2 = new Class2();
  $Obj2->hello(); # 原来的hello方法仍然是公共的
  $Obj2->hi(); # 报致命错误,因为别名hi方法被修改成私有的

Trait 也能组合Trait,Trait中支持抽象方法、静态属性及静态方法,测试代码如下:

<?php
trait Hello {
  public function sayHello() {
    echo "Hello\n";
  }
}
trait World {
  use Hello;
  public function sayWorld() {
    echo "World\n";
  }
  abstract public function getWorld();
  public function inc() {
    static $c = 0;
    $c = $c + 1;
    echo "$c\n";
  }
  public static function doSomething() {
    echo "Doing something\n";
  }
}
class HelloWorld {
  use World;
  public function getWorld() {
    return 'get World';
  }
}
$Obj = new HelloWorld();
$Obj->sayHello();
$Obj->sayWorld();
echo $Obj->getWorld() . "\n";
HelloWorld::doSomething();
$Obj->inc();
$Obj->inc();

输出结果如下:

Hello
World
get World
Doing something
1
2

以上就是本文的全部内容,希望对大家的学习有所帮助。

PHP 相关文章推荐
php修改时间格式的代码
May 29 PHP
PHP setTime 设置当前时间的代码
Aug 27 PHP
codeigniter数据库操作函数汇总
Jun 12 PHP
PHP实现多图片上传类实例
Jul 26 PHP
php查找字符串出现次数的方法
Dec 01 PHP
PHP模块memcached使用指南
Dec 08 PHP
PHP遍历XML文档所有节点的方法
Mar 12 PHP
PHP图像处理类库及演示分享
May 17 PHP
PHP生成条形码大揭秘
Sep 24 PHP
PHP简单操作MongoDB的方法(安装及增删改查)
May 26 PHP
php微信开发之带参数二维码的使用
Aug 03 PHP
Laravel 微信小程序后端搭建步骤详解
Nov 26 PHP
php实现在多维数组中查找特定value的方法
Jul 29 #PHP
1亿条数据如何分表100张到Mysql数据库中(PHP)
Jul 29 #PHP
php实现仿写CodeIgniter的购物车类
Jul 29 #PHP
PHP使用逆波兰式计算工资的方法
Jul 29 #PHP
ThinkPHP实现递归无级分类――代码少
Jul 29 #PHP
PHP之正则表达式捕获组与非捕获组(详解)
Jul 29 #PHP
PHP实现的简单缓存类
Jul 29 #PHP
You might like
php强制更新图片缓存的方法
2015/02/11 PHP
PHP实现的猴王算法(猴子选大王)示例
2018/04/30 PHP
JScript内置对象Array中元素的删除方法
2007/03/08 Javascript
javascript tips提示框组件实现代码
2010/11/19 Javascript
jQuery 绑定事件到动态创建的元素上的方法实例
2013/08/18 Javascript
JavaScript对象数组排序实例方法浅析
2016/06/15 Javascript
微信小程序 wxapp导航 navigator详解
2016/10/31 Javascript
微信小程序(六):列表上拉加载下拉刷新示例
2017/01/13 Javascript
利用jQuery实现滑动开关按钮效果(附demo源码下载)
2017/02/07 Javascript
jQuery阻止移动端遮罩层后页面滚动
2017/03/15 Javascript
微信小程序获取微信运动步数的实例代码
2017/07/20 Javascript
小程序云开发如何实现图片上传及发表文字
2019/05/17 Javascript
VuePress 中如何增加用户登录功能
2019/11/29 Javascript
使用pkg打包ThinkJS项目的方法步骤
2019/12/30 Javascript
node.js使用 http-proxy 创建代理服务器操作示例
2020/02/10 Javascript
vue项目配置同一局域网可使用ip访问的操作
2020/10/23 Javascript
uni-app使用countdown插件实现倒计时
2020/11/01 Javascript
[01:02:47]EG vs Secret 2019国际邀请赛淘汰赛 胜者组 BO3 第一场 8.21.mp4
2020/07/19 DOTA
[01:52]PWL S2开团时刻第四期——DOTA2成语故事
2020/12/03 DOTA
Python文件操作基本流程代码实例
2017/12/11 Python
基于python实现简单日历
2018/07/28 Python
python3+requests接口自动化session操作方法
2018/10/13 Python
Python基础之条件控制操作示例【if语句】
2019/03/23 Python
Python字符串处理的8招秘籍(小结)
2019/08/13 Python
解决Pytorch训练过程中loss不下降的问题
2020/01/02 Python
Win10下安装并使用tensorflow-gpu1.8.0+python3.6全过程分析(显卡MX250+CUDA9.0+cudnn)
2020/02/17 Python
解决Python数据可视化中文部分显示方块问题
2020/05/16 Python
CAT鞋加拿大官网:CAT Footwear加拿大
2020/08/05 全球购物
秋季校运动会广播稿
2014/02/23 职场文书
电子商务专业应届毕业生求职信
2014/06/21 职场文书
大学第二课堂活动总结
2014/07/08 职场文书
化学专业毕业生求职信
2014/07/28 职场文书
2014法院干警廉洁警示教育思想汇报
2014/09/13 职场文书
学校计划生育责任书
2015/05/09 职场文书
2015年社区党建工作汇报材料
2015/06/25 职场文书
教你怎么用Python监控愉客行车程
2021/04/29 Python