详解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 相关文章推荐
Base64在线编码解码实现代码 演示与下载
Jan 08 PHP
PHP中根据IP地址判断城市实现城市切换或跳转代码
Sep 04 PHP
PHP zip扩展Linux下安装过程分享
May 05 PHP
一个比较不错的PHP日历类分享
Nov 18 PHP
php用户密码加密算法分析【Discuz加密算法】
Oct 12 PHP
php PDO实现的事务回滚示例
Mar 23 PHP
PHP快速推送微信模板消息
Apr 14 PHP
php实现微信公众平台发红包功能
Jun 14 PHP
PHP swoole和redis异步任务实现方法分析
Aug 12 PHP
解决在Laravel 中处理OPTIONS请求的问题
Oct 11 PHP
在laravel中实现将查询的对象转换为多维数组的函数
Oct 21 PHP
PHP 自动加载类原理与用法实例分析
Apr 14 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
《猛禽小队》:DC宇宙的又一超级大烂片
2020/04/09 欧美动漫
php代码优化及php相关问题总结
2006/10/09 PHP
cakephp常见知识点汇总
2017/02/24 PHP
php高性能日志系统 seaslog 的安装与使用方法分析
2020/02/29 PHP
jquery easyui的tabs使用时的问题
2010/03/23 Javascript
基于jquery的监控数据是否发生改变
2011/04/11 Javascript
JavaScript EasyPager 分页函数
2011/05/25 Javascript
jquery实现清新实用的网页菜单效果
2015/08/28 Javascript
JS实现横向与竖向两个选项卡Tab联动的方法
2015/09/27 Javascript
jquery遍历json对象集合详解
2016/05/18 Javascript
jQuery操作cookie
2016/08/08 Javascript
jQuery中DOM节点删除之empty与remove
2017/01/20 Javascript
Nodejs 发送Post请求功能(发短信验证码例子)
2017/02/09 NodeJs
vue组件化中slot的基本使用方法
2019/05/01 Javascript
vue 插件的方法代码详解
2019/06/06 Javascript
微信小程序-可移动菜单的实现过程详解
2019/06/24 Javascript
vue 解决IOS10低版本白屏的问题
2020/11/17 Javascript
js异步接口并发数量控制的方法示例
2020/11/22 Javascript
Python中字典和JSON互转操作实例
2015/01/19 Python
Linux下python与C++使用dlib实现人脸检测
2018/06/29 Python
django框架防止XSS注入的方法分析
2019/06/21 Python
python中字符串数组逆序排列方法总结
2019/06/23 Python
Python打包模块wheel的使用方法与将python包发布到PyPI的方法详解
2020/02/12 Python
python实现银行实战系统
2020/02/26 Python
如何使用 Flask 做一个评论系统
2020/11/27 Python
css3 border-image使用说明
2010/06/23 HTML / CSS
印度尼西亚电子产品购物网站:Kliknklik
2018/06/05 全球购物
Notino法国:购买香水和化妆品
2019/04/15 全球购物
土木工程专业个人求职信
2013/12/30 职场文书
打架检讨书400字
2014/01/17 职场文书
访谈节目策划方案
2014/05/15 职场文书
竞聘报告优秀范文
2014/11/06 职场文书
学生个人评语大全
2015/01/04 职场文书
火烧圆明园观后感
2015/06/03 职场文书
格列夫游记读书笔记
2015/07/01 职场文书
SQL Server中T-SQL标识符介绍与无排序生成序号的方法
2022/05/25 SQL Server