详解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 db类库进行数据库操作
Mar 19 PHP
探讨:如何使用PHP实现计算两个日期间隔的年、月、周、日数
Jun 13 PHP
php获取四位字母和数字的随机数的实现方法
Jan 09 PHP
php实现paypal 授权登录
May 28 PHP
PHP实现通过get方式识别用户发送邮件的方法
Jul 16 PHP
详解WordPress中调用评论模板和循环输出评论的PHP函数
Jan 05 PHP
PHP设置images目录不充许http访问的方法
Nov 01 PHP
php 运算符与表达式详细介绍
Nov 30 PHP
thinkPHP简单实现多个子查询语句的方法
Dec 05 PHP
php中bind_param()函数用法分析
Mar 28 PHP
PDO::errorInfo讲解
Jan 28 PHP
PHP实现通过二维数组键值获取一维键名操作示例
Oct 11 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
简单示例AJAX结合PHP代码实现登录效果代码
2008/07/25 PHP
判断Keep-Alive模式的HTTP请求的结束的实现代码
2011/08/06 PHP
php操作SVN版本服务器类代码
2011/11/27 PHP
php stripslashes和addslashes的区别
2014/02/03 PHP
php将日期格式转换成xx天前的格式
2015/04/16 PHP
Javascript 的addEventListener()及attachEvent()区别分析
2009/05/21 Javascript
javascript 伪数组实现方法
2010/10/11 Javascript
js仿百度有啊通栏展示效果实现代码
2013/05/28 Javascript
ExtJS4如何给同一个formpanel不同的url
2014/05/02 Javascript
jquery中val()方法是从最后一个选项往前读取的
2015/09/06 Javascript
《JavaScript高级编程》学习笔记之object和array引用类型
2015/11/01 Javascript
jquery实现简单的遮罩层
2016/01/08 Javascript
JavaScript的ExtJS框架中表格的编写教程
2016/05/21 Javascript
基于react组件之间的参数传递(详解)
2017/09/05 Javascript
微信小程序实现左右联动的实战记录
2018/07/05 Javascript
vue.js父子组件通信动态绑定的实例
2018/09/28 Javascript
React父子组件间的传值的方法
2018/11/13 Javascript
layui复选框限制选择个数的方法
2019/09/18 Javascript
JS实现网站楼层导航效果代码实例
2020/06/16 Javascript
[07:52]2014DOTA2 TI逗比武士游V社解说背后的故事
2014/07/10 DOTA
Python中内置数据类型list,tuple,dict,set的区别和用法
2015/12/14 Python
Python脚本实时处理log文件的方法
2016/11/21 Python
tensorflow: 查看 tensor详细数值方法
2018/06/13 Python
Python常见数字运算操作实例小结
2019/03/22 Python
python暴力解压rar加密文件过程详解
2019/07/05 Python
Python定时任务APScheduler的实例实例详解
2019/07/22 Python
18个Python脚本可加速你的编码速度(提示和技巧)
2019/10/17 Python
python框架flask表单实现详解
2019/11/04 Python
python实现梯度法 python最速下降法
2020/03/24 Python
Python爬虫之Selenium实现键盘事件
2020/12/04 Python
哈利波特商店:Harry Potter Shop
2018/11/30 全球购物
2014年百日安全生产活动总结
2014/05/04 职场文书
新闻工作者先进事迹
2014/05/26 职场文书
资深HR教你写好简历中的自我评价
2019/05/07 职场文书
JS前端canvas交互实现拖拽旋转及缩放示例
2022/08/05 Javascript
Java中的Kafka为什么性能这么快及4大核心详析
2022/09/23 Java/Android