详解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 相关文章推荐
用Flash图形化数据(一)
Oct 09 PHP
解析PHP中如何将数组变量写入文件
Jun 06 PHP
处理单名多值表单的详解
Jun 08 PHP
PHP中的多行字符串传递给JavaScript的两种方法
Jun 19 PHP
php socket实现的聊天室代码分享
Aug 16 PHP
php实现网页缓存的工具类分享
Jul 14 PHP
在WordPress中实现发送http请求的相关函数解析
Dec 29 PHP
CodeIgniter配置之routes.php用法实例分析
Jan 19 PHP
PHP实现适用于自定义的验证码类
Jun 15 PHP
PHP MySql增删改查的简单实例
Jun 21 PHP
PHP 应用容器化以及部署方法
Feb 12 PHP
phpstudy2020搭建站点的实现示例
Oct 30 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类中的魔术方法(Magic Method)简明总结
2014/07/08 PHP
php读取本地json文件的实例
2018/03/07 PHP
Javascript中暂停功能的实现代码
2007/03/04 Javascript
jQuery live
2009/05/15 Javascript
JavaScript 异步调用框架 (Part 1 - 问题 &amp; 场景)
2009/08/03 Javascript
Google的跟踪代码 动态加载js代码方法应用
2012/11/12 Javascript
Jquery跳到页面指定位置的方法
2014/05/12 Javascript
javascript比较两个日期相差天数的方法
2015/07/24 Javascript
老生常谈 js中this的指向
2016/06/30 Javascript
JavaScript学习笔记整理_简单实现枚举类型,扑克牌应用
2016/09/19 Javascript
JS中SetTimeout和SetInterval使用初探
2017/03/23 Javascript
Vue AST源码解析第一篇
2017/07/19 Javascript
jQuery滚动条美化插件nicescroll简单用法示例
2018/04/18 jQuery
React中如何引入Angular组件详解
2018/08/09 Javascript
NodeJs入门教程之定时器和队列
2019/03/08 NodeJs
JQuery实现简单的复选框树形结构图示例【附源码下载】
2019/07/16 jQuery
vue-loader中引入模板预处理器的实现
2019/09/04 Javascript
[01:07:19]2018DOTA2亚洲邀请赛 4.5 淘汰赛 Mineski vs VG 第一场
2018/04/06 DOTA
pyv8学习python和javascript变量进行交互
2013/12/04 Python
老生常谈Python之装饰器、迭代器和生成器
2017/07/26 Python
python学习教程之Numpy和Pandas的使用
2017/09/11 Python
浅谈Pandas 排序之后索引的问题
2018/06/07 Python
python 获取utc时间转化为本地时间的方法
2018/12/31 Python
TensorFlow车牌识别完整版代码(含车牌数据集)
2019/08/05 Python
关于Pytorch MaxUnpool2d中size操作方式
2020/01/03 Python
python global和nonlocal用法解析
2020/02/03 Python
css3实现圆锥渐变conic-gradient效果
2020/02/12 HTML / CSS
DAWGS鞋官方网站:鞋,凉鞋,靴子
2016/10/04 全球购物
澳大利亚领先的在线葡萄酒零售商:Get Wines Direct
2018/03/27 全球购物
Shopping happy life西班牙:以最优惠的价格提供最好的时尚配饰
2020/03/13 全球购物
外联部演讲稿
2014/05/24 职场文书
旅游活动总结
2014/08/27 职场文书
个人对照检查材料思想汇报
2014/09/26 职场文书
2014年技术工作总结范文
2014/11/20 职场文书
巧用 -webkit-box-reflect 倒影实现各类动效(小结)
2021/04/22 HTML / CSS
mysql中如何用命令创建联合唯一索引
2022/04/20 MySQL