详解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 Parse Error: syntax error, unexpected $end 错误的解决办法
Jun 05 PHP
yii中widget的用法
Dec 03 PHP
PHP基于cookie与session统计网站访问量并输出显示的方法
Jan 15 PHP
php删除txt文件指定行及按行读取txt文档数据的方法
Jan 30 PHP
PHP回调函数与匿名函数实例详解
Aug 16 PHP
PHP流Streams、包装器wrapper概念与用法实例详解
Nov 17 PHP
PHP运用foreach神奇的转换数组(实例讲解)
Feb 01 PHP
php+croppic.js实现剪切上传图片功能
Aug 14 PHP
让Laravel API永远返回JSON格式响应的方法示例
Sep 05 PHP
PHP判断访客是否手机端(移动端浏览器)访问的方法总结【4种方法】
Mar 27 PHP
PHP+iframe模拟Ajax上传文件功能示例
Jul 02 PHP
PHP中isset、empty的用法与区别示例详解
Nov 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中替换字符串中的空格为逗号','的方法
2014/06/09 PHP
php根据一个给定范围和步进生成数组的方法
2015/06/19 PHP
php自动更新版权信息显示的方法
2015/06/19 PHP
jQuery调用WebService的实现代码
2011/06/19 Javascript
javascript打印html内容功能的方法示例
2013/11/28 Javascript
jQuery实现动画效果的简单实例
2014/01/27 Javascript
js中精确计算加法和减法示例
2014/03/28 Javascript
推荐6款基于jQuery实现图片效果插件
2014/12/07 Javascript
jQuery实现统计输入文字个数的方法
2015/03/11 Javascript
Javascript中arguments用法实例分析
2015/06/13 Javascript
基于Jquery代码实现支持PC端手机端幻灯片代码
2015/11/17 Javascript
ES6中Generator与异步操作实例分析
2017/03/31 Javascript
ionic2自定义cordova插件开发以及使用(Android)
2017/06/19 Javascript
[js高手之路]图解javascript的原型(prototype)对象,原型链实例
2017/08/28 Javascript
浅谈Angularjs中不同类型的双向数据绑定
2018/07/16 Javascript
微信小程序 slot踩坑的解决
2019/04/01 Javascript
js图片无缝滚动插件使用详解
2020/05/26 Javascript
layui table单元格事件修改值的方法
2019/09/24 Javascript
小程序选项卡以及swiper套用(跨页面)
2020/06/19 Javascript
浅谈Python 多进程默认不能共享全局变量的问题
2019/01/11 Python
python元组的概念知识点
2019/11/19 Python
python接口自动化如何封装获取常量的类
2019/12/24 Python
Django restful framework生成API文档过程详解
2020/11/12 Python
详解使用scrapy进行模拟登陆三种方式
2021/02/21 Python
纯CSS3代码实现文字描边
2016/04/25 HTML / CSS
浅析HTML5 meta viewport参数
2020/10/28 HTML / CSS
受外贸欢迎的美国主机:BlueHost
2017/05/16 全球购物
戴森西班牙官网:Dyson西班牙
2020/02/04 全球购物
探矿工程师自荐信
2014/01/24 职场文书
面试后的感谢信范文
2014/02/01 职场文书
市场营销调查计划书
2014/05/02 职场文书
优秀本科毕业生自荐信
2014/07/04 职场文书
践行三严三实心得体会
2014/10/13 职场文书
CSS中em的正确打开方式详解
2021/04/08 HTML / CSS
Python爬虫基础初探selenium
2021/05/31 Python
Vue鼠标滚轮滚动切换路由效果的实现方法
2021/08/04 Vue.js