详解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 相关文章推荐
第十二节--类的自动加载
Nov 16 PHP
延长phpmyadmin登录时间的方法
Feb 06 PHP
PHP中call_user_func_array()函数的用法演示
Feb 05 PHP
PHP操作Memcache实例介绍
Jun 14 PHP
php实现httpclient类示例
Apr 08 PHP
PHPUnit安装及使用示例
Oct 29 PHP
codeigniter中实现一次性加载多个view的方法
Mar 20 PHP
php数字每三位加逗号的功能函数
Oct 22 PHP
详解使用php调用微信接口上传永久素材
Apr 11 PHP
基于PHP-FPM进程池探秘
Oct 17 PHP
php如何比较两个浮点数是否相等详解
Feb 12 PHP
php生成静态页面并实现预览功能
Jun 27 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
超外差式晶体管收音机的组装与统调
2021/03/01 无线电
PHP 函数学习简单小结
2010/07/08 PHP
通过PHP修改Linux或Unix口令的方法分享
2012/01/30 PHP
PHP网络操作函数汇总
2015/05/18 PHP
PHP中COOKIES使用示例
2015/07/26 PHP
javascript奇异的arguments分析
2010/10/20 Javascript
kmock javascript 单元测试代码
2011/02/06 Javascript
JS左右无缝滚动(一般方法+面向对象方法)
2012/08/17 Javascript
javascript编码的几个方法详细介绍
2013/01/06 Javascript
Jquery右下角抖动、浮动 实例代码(兼容ie6、FF)
2013/08/15 Javascript
JS使用for循环遍历Table的所有单元格内容
2014/08/21 Javascript
Jquery日期选择datepicker插件用法实例分析
2015/06/08 Javascript
JavaScript代码判断点击第几个按钮
2015/12/13 Javascript
使用jQuery实现简单的tab框实例
2017/08/22 jQuery
webpack3之loader全解析
2017/10/26 Javascript
VueJs里利用CryptoJs实现加密及解密的方法示例
2019/04/29 Javascript
Electron + vue 打包桌面操作流程详解
2019/06/24 Javascript
layui加载数据显示loading加载完成loading消失的实例代码
2019/09/23 Javascript
Django集成百度富文本编辑器uEditor攻略
2014/07/04 Python
理解生产者消费者模型及在Python编程中的运用实例
2016/06/26 Python
python实现杨辉三角思路
2017/07/14 Python
Python实现基于C/S架构的聊天室功能详解
2018/07/07 Python
Django unittest 设置跳过某些case的方法
2018/12/26 Python
Pytorch 高效使用GPU的操作
2020/06/27 Python
css3中background新增的4个新的相关属性用法介绍
2013/09/26 HTML / CSS
美国中小型企业领先的办公家具供应商:Office Designs
2016/11/26 全球购物
英国门销售网站:Green Tree Doors
2020/01/07 全球购物
英语专业毕业生自荐信
2013/10/28 职场文书
行政管理专业推荐信
2013/11/02 职场文书
小区物业门卫岗位职责
2014/04/10 职场文书
环境卫生倡议书
2014/08/29 职场文书
湖南省党的群众路线教育实践活动总结会议新闻稿
2014/10/21 职场文书
2014年部门工作总结
2014/11/12 职场文书
中学团支部工作总结
2015/08/13 职场文书
Nginx使用Lua模块实现WAF的原理解析
2021/09/04 Servers
解决使用了nginx获取IP地址都是127.0.0.1 的问题
2021/09/25 Servers