详解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 相关文章推荐
BBS(php &amp; mysql)完整版(六)
Oct 09 PHP
FCKeditor添加自定义按钮
Mar 27 PHP
php array_map array_multisort 高效处理多维数组排序
Jun 11 PHP
部署PHP项目应该注意的几点事项分享
Dec 20 PHP
php ci框架中加载css和js文件失败的原因及解决方法
Jul 29 PHP
PHP中子类重载父类的方法【parent::方法名】
May 06 PHP
浅析php如何实现App常用的秒发功能
Aug 03 PHP
PHP仿微信多图片预览上传实例代码
Sep 13 PHP
PHP二进制与字符串之间的相互转换教程
Oct 14 PHP
PHP编程获取各个时间段具体时间的方法
May 26 PHP
Yii2.0使用阿里云OSS的SDK上传图片、下载、删除图片示例
Sep 20 PHP
PHP7匿名类的用法示例
Apr 05 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学习资料汇总与网址
2007/03/16 PHP
分享PHP header函数使用教程
2013/09/05 PHP
QQ互联一键登录审核不通过的解决方案
2014/09/10 PHP
PHP 实现页面静态化的几种方法
2017/07/23 PHP
JS面向对象编程 for Cookie
2010/09/19 Javascript
工作需要写的一个js拖拽组件
2011/07/28 Javascript
如何使用jQuery来处理图片坏链具体实现步骤
2013/05/02 Javascript
jquery 提示信息显示后自动消失的具体实现
2013/12/18 Javascript
常见的jQuery选择器汇总
2014/11/24 Javascript
JavaScript中的标签语句用法分析
2015/02/10 Javascript
新手快速学习JavaScript免费教程资源汇总
2015/06/25 Javascript
基于zepto的移动端轻量级日期插件--date_picker
2016/03/04 Javascript
Nodejs全局安装和本地安装的不同之处
2016/07/04 NodeJs
jQuery Validate插件实现表单验证
2016/08/19 Javascript
jQuery+ThinkPHP+Ajax实现即时消息提醒功能实例代码
2017/03/21 jQuery
Js实现中国公民身份证号码有效性验证实例代码
2017/05/03 Javascript
简单实现vue验证码60秒倒计时功能
2017/10/11 Javascript
JavaScript程序设计高级算法之动态规划实例分析
2017/11/24 Javascript
jQuery实现仿京东防抖动菜单效果示例
2018/07/06 jQuery
js实现头像上传并且可预览提交
2020/12/25 Javascript
[00:43]FTP典藏礼包 DOTA2三大英雄霸气新套装
2014/03/21 DOTA
[45:52]2018DOTA2亚洲邀请赛 4.1小组赛 A组加赛 LGD vs Liquid
2018/04/02 DOTA
[53:13]DOTA2-DPC中国联赛 正赛 DLG vs PHOENIX BO3 第三场 1月18日
2021/03/11 DOTA
Python MD5文件生成码
2009/01/12 Python
Python实现发送QQ邮件的封装
2017/07/14 Python
pip matplotlib报错equired packages can not be built解决
2018/01/06 Python
Django实现网页分页功能
2019/10/31 Python
python 实现百度网盘非会员上传超过500个文件的方法
2021/01/07 Python
关于css中margin的值和垂直外边距重叠问题
2020/10/27 HTML / CSS
手工制作的意大利太阳镜和光学元件:Illesteva
2019/01/19 全球购物
vue实现倒计时功能
2021/03/24 Vue.js
高中校园广播稿
2014/01/11 职场文书
初中同学聚会邀请函
2014/02/03 职场文书
调查研究项目计划书
2014/04/29 职场文书
辩论赛开场白大全(主持人+辩手)
2015/05/29 职场文书
浅谈Mysql多表连接查询的执行细节
2021/04/24 MySQL