详解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 相关文章推荐
web方式ftp
Oct 09 PHP
无限级别菜单的实现
Oct 09 PHP
PHP5.2下chunk_split()函数整数溢出漏洞 分析
Jun 06 PHP
并发下常见的加锁及锁的PHP具体实现代码
Oct 12 PHP
PHP 使用header函数设置HTTP头的示例解析 表头
Jun 17 PHP
Thinkphp实现MySQL读写分离操作示例
Jun 25 PHP
php按字符无乱码截取中文的方法
Mar 27 PHP
PHP+shell脚本操作Memcached和Apache Status的实例分享
Mar 11 PHP
PHP实现财务审核通过后返现金额到客户的功能
Jul 04 PHP
php的命名空间与自动加载实现方法
Aug 25 PHP
PHP生成随机字符串实例代码(字母+数字)
Sep 11 PHP
one.php 多项目、函数库、类库 统一为一个版本的方法
Aug 24 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/03/14 PHP
thinkphp实现163、QQ邮箱收发邮件的方法
2015/12/18 PHP
PHP接口继承及接口多继承原理与实现方法详解
2017/10/18 PHP
PHP7新特性之抽象语法树(AST)带来的变化详解
2018/07/17 PHP
发现的以前不知道的函数
2006/09/19 Javascript
调用js时ie6和ie7,ff的区别
2009/08/19 Javascript
关于用Jquery的height()、width()计算动态插入的IMG标签的宽高的问题
2010/12/08 Javascript
文本框只能输入数字的js代码(含小数点)
2016/07/10 Javascript
JS关闭窗口时产生的事件及用法示例
2016/08/20 Javascript
vue cli使用绝对路径引用图片问题的解决
2017/12/06 Javascript
Vue+Jwt+SpringBoot+Ldap完成登录认证的示例代码
2018/05/21 Javascript
Vue.js 利用v-for中的index值实现隔行变色
2018/08/01 Javascript
Angular8 Http拦截器简单使用教程
2019/08/20 Javascript
vue双向绑定数据限制长度的方法
2019/11/04 Javascript
Js数组扁平化实现方法代码总汇
2020/11/11 Javascript
基于Vue2实现移动端图片上传、压缩、拖拽排序、拖拽删除功能
2021/01/05 Vue.js
python实现关键词提取的示例讲解
2018/04/28 Python
Python读取csv文件分隔符设置方法
2019/01/14 Python
python 矢量数据转栅格数据代码实例
2019/09/30 Python
python GUI库图形界面开发之pyinstaller打包python程序为exe安装文件
2020/02/26 Python
python爬虫实例之获取动漫截图
2020/05/31 Python
python+requests实现接口测试的完整步骤
2020/10/27 Python
HTML5新标签兼容——&gt; 的两种方法
2018/09/12 HTML / CSS
英国泽西岛植物:Jersey Plants Direct
2019/08/07 全球购物
The North Face北面荷兰官网:美国著名户外品牌
2019/10/16 全球购物
意大利奢侈品牌在线精品店:Jole.it
2020/11/23 全球购物
C/C++有关内存的思考题
2015/12/04 面试题
你经历的项目中的SCM配置项主要有哪些?什么是配置项?
2013/11/04 面试题
终端业务员岗位职责
2013/11/27 职场文书
大学同学聚会邀请函
2014/01/19 职场文书
2014党委书记四风对照检查材料思想汇报
2014/09/21 职场文书
授权委托书范本(单位)
2014/09/28 职场文书
公务员政审个人总结
2015/02/12 职场文书
承兑汇票延期证明
2015/06/23 职场文书
《比尾巴》教学反思
2016/02/24 职场文书
InterProcessMutex实现zookeeper分布式锁原理
2022/03/21 Java/Android