详解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执行速度全攻略(上)
Oct 09 PHP
PHP 面向对象 final类与final方法
May 05 PHP
PHP Zip压缩 在线对文件进行压缩的函数
May 26 PHP
PHPMailer使用教程(PHPMailer发送邮件实例分析)
Dec 06 PHP
codeigniter教程之上传视频并使用ffmpeg转flv示例
Feb 13 PHP
php查看网页源代码的方法
Mar 13 PHP
学习php设计模式 php实现合成模式(composite)
Dec 08 PHP
Yii基于数组和对象的Model查询技巧实例详解
Dec 28 PHP
Thinkphp3.2.3整合phpqrcode生成带logo的二维码
Jul 21 PHP
php 实现简单的登录功能示例【基于thinkPHP框架】
Dec 02 PHP
用Laravel轻松处理千万级数据的方法实现
Dec 25 PHP
php中Swoole的热更新实现代码实例
Mar 04 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学习之字符串比较和查找
2011/04/17 PHP
PHP中开启gzip压缩的2种方法
2015/01/31 PHP
php实现的验证码文件类实例
2015/06/18 PHP
PHP+ajax分页实例简析
2015/12/07 PHP
初学prototype,发个JS接受URL参数的代码
2006/09/25 Javascript
javascript之锁定表格栏位
2007/06/29 Javascript
event对象的方法 兼容多浏览器
2009/06/27 Javascript
基于jQuery的message插件实现右下角弹出消息框
2011/01/11 Javascript
Js 获取Gridview选中行的内容操作步骤
2013/02/05 Javascript
js实现按钮加背景图片常用方法
2014/11/01 Javascript
微信浏览器内置JavaScript对象WeixinJSBridge使用实例
2015/05/25 Javascript
Google Maps基础及实例解析
2016/08/06 Javascript
vue全局自定义指令-元素拖拽的实现代码
2019/04/14 Javascript
Python统计文件中去重后uuid个数的方法
2015/07/30 Python
举例讲解Python面向对象编程中类的继承
2016/06/17 Python
详解如何将python3.6软件的py文件打包成exe程序
2018/10/09 Python
Python Django模板之模板过滤器与自定义模板过滤器示例
2019/10/18 Python
浅谈Django2.0 加xadmin踩的坑
2019/11/15 Python
Django Channel实时推送与聊天的示例代码
2020/04/30 Python
如何用Matplotlib 画三维图的示例代码
2020/07/28 Python
探讨HTML5移动开发的几大特性(必看)
2015/12/30 HTML / CSS
TIME时代杂志台湾总代理:台时亚洲
2018/10/22 全球购物
W Hamond官网:始于1979年的钻石专家
2020/07/20 全球购物
武汉世纪畅想数字传播有限公司 .NET笔试题
2015/06/13 面试题
英文自荐信
2013/12/15 职场文书
初三学习决心书
2014/03/11 职场文书
2014年三万活动总结
2014/04/26 职场文书
中文专业毕业生自荐信
2014/05/24 职场文书
企业文化宣传标语
2014/06/09 职场文书
干部对照检查材料范文
2014/08/26 职场文书
党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
2014年党的群众路线学习心得体会
2014/11/05 职场文书
安全知识竞赛主持词
2015/06/30 职场文书
2016元旦主持人开场白
2015/12/03 职场文书
2019年教师入党申请书
2019/06/27 职场文书
vue el-table实现递归嵌套的示例代码
2022/08/14 Vue.js