详解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 相关文章推荐
支持oicq头像的留言簿(二)
Oct 09 PHP
php生成WAP页面
Oct 09 PHP
PHP curl 获取响应的状态码的方法
Jan 13 PHP
php图片缩放实现方法
Feb 20 PHP
Drupal7中常用的数据库操作实例
Mar 02 PHP
PHP中使用array函数新建一个数组
Nov 19 PHP
PHP结合Mysql数据库实现留言板功能
Mar 04 PHP
redis查看连接数及php模拟并发创建redis连接的方法
Dec 15 PHP
PHP中让json_encode不自动转义斜杠“/”的方法
Feb 28 PHP
php实现批量上传数据到数据库(.csv格式)的案例
Jun 18 PHP
支持汉转拼和拼音分词的PHP中文工具类ChineseUtil
Feb 23 PHP
tp5框架基于Ajax实现列表无刷新排序功能示例
Feb 10 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中获取远程客户端的真实ip地址的方法
2011/08/03 PHP
php中使用base HTTP验证的方法
2015/04/20 PHP
php 在字符串指定位置插入新字符的简单实现
2016/06/28 PHP
javascript读取xml
2006/11/04 Javascript
Jquery 选中表格一列并对表格排序实现原理
2012/12/15 Javascript
7款风格新颖的jQuery/CSS3菜单导航分享
2013/04/23 Javascript
JS+CSS实现带小三角指引的滑动门效果
2015/09/22 Javascript
总结JavaScript中布尔操作符||与&amp;&amp;的使用技巧
2015/11/17 Javascript
window.open不被拦截的简单实现代码(推荐)
2016/08/04 Javascript
VUE中使用Vue-resource完成交互
2017/07/21 Javascript
详解关于react-redux中的connect用法介绍及原理解析
2017/09/11 Javascript
浅谈vue的iview列表table render函数设置DOM属性值的方法
2017/09/30 Javascript
解决Angular.js中使用Swiper插件不能滑动的问题
2018/02/26 Javascript
小程序显示弹窗时禁止下层的内容滚动实现方法
2019/03/20 Javascript
vue+vant实现商品列表批量倒计时功能
2020/01/13 Javascript
Python 25行代码实现的RSA算法详解
2018/04/10 Python
python读文件保存到字典,修改字典并写入新文件的实例
2018/04/23 Python
Python实现合并同一个文件夹下所有txt文件的方法示例
2018/04/26 Python
详解python如何在django中为用户模型添加自定义权限
2018/10/15 Python
python框架flask表单实现详解
2019/11/04 Python
Python实现把类当做字典来访问
2019/12/16 Python
HTML5 LocalStorage 本地存储详细概括(多图)
2017/08/18 HTML / CSS
原生canvas制作画图小工具的踩坑和爬坑
2020/06/09 HTML / CSS
伦敦最有品味的百货:Liberty London
2016/11/12 全球购物
下列程序在32位linux或unix中的结果是什么
2015/01/26 面试题
远程研修随笔感言
2014/02/10 职场文书
连锁超市项目计划书
2014/09/15 职场文书
党的群众路线教育实践活动个人对照检查材料
2014/09/22 职场文书
党支部2014年度工作总结
2014/12/04 职场文书
2014年语文教研组工作总结
2014/12/06 职场文书
2015年社区党务工作总结
2015/04/21 职场文书
最感人的道歉情书
2015/05/12 职场文书
2015年幼儿园学期工作总结
2015/05/22 职场文书
2019思想汇报范文
2019/05/21 职场文书
Vue过滤器(filter)实现及应用场景详解
2021/06/15 Vue.js
TV动画《史上最强大魔王转生为村民A》番宣CM公布
2022/04/01 日漫