Trait

自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。

Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。

Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。

Example #1 Trait 示例

<?php
trait ezcReflectionReturnInfo {
function getReturnType() { /**/ }
function getReturnDescription() { /**/ }
} class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
} class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
?>

优先级

从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。

Example #2 优先顺序示例

从基类继承的成员被插入的 SayWorld Trait 中的 MyHelloWorld 方法所覆盖。其行为 MyHelloWorld 类中定义的方法一致。优先顺序是当前类中的方法会覆盖 trait 方法,而 trait 方法又覆盖了基类中的方法。

 <?php
class Base {
public function sayHello() {
echo 'Hello ';
}
} trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
} class MyHelloWorld extends Base {
use SayWorld;
} $o = new MyHelloWorld();
$o->sayHello();
?>

以上例程会输出:

Hello World!

Example #3 另一个优先级顺序的例子

 <?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
} class TheWorldIsNotEnough {
use HelloWorld;
public function sayHello() {
echo 'Hello Universe!';
}
} $o = new TheWorldIsNotEnough();
$o->sayHello();
?>

以上例程会输出:

Hello Universe!

多个 trait

通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。

Example #4 多个 trait 的用法

 <?php
trait Hello {
public function sayHello() {
echo 'Hello ';
}
} trait World {
public function sayWorld() {
echo 'World';
}
} class MyHelloWorld {
use Hello, World;
public function sayExclamationMark() {
echo '!';
}
} $o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>

以上例程会输出:

Hello World!

冲突的解决

如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。

为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。

以上方式仅允许排除掉其它方法,as 操作符可以 为某个方法引入别名。 注意,as 操作符不会对方法进行重命名,也不会影响其方法。

Example #5 冲突的解决

在本例中 Talker 使用了 trait A 和 B。由于 A 和 B 有冲突的方法,其定义了使用 trait B 中的 smallTalk 以及 trait A 中的 bigTalk。

Aliased_Talker 使用了 as 操作符来定义了 talk 来作为 B 的 bigTalk 的别名。

 <?php
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
} trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
} class Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
}
} class Aliased_Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as talk;
}
}
?>

Note:

在 PHP 7.0 之前,在类里定义和 trait 同名的属性,哪怕是完全兼容的也会抛出 E_STRICT(完全兼容的意思:具有相同的访问可见性、初始默认值)。

修改方法的访问控制

使用 as 语法还可以用来调整方法的访问控制。

Example #6 修改方法的访问控制

 <?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
} // 修改 sayHello 的访问控制
class MyClass1 {
use HelloWorld { sayHello as protected; }
} // 给方法一个改变了访问控制的别名
// 原版 sayHello 的访问控制则没有发生变化
class MyClass2 {
use HelloWorld { sayHello as private myPrivateHello; }
}
?>

从 trait 来组成 trait

正如 class 能够使用 trait 一样,其它 trait 也能够使用 trait。在 trait 定义时通过使用一个或多个 trait,能够组合其它 trait 中的部分或全部成员。

Example #7 从 trait 来组成 trait

 <?php
trait Hello {
public function sayHello() {
echo 'Hello ';
}
} trait World {
public function sayWorld() {
echo 'World!';
}
} trait HelloWorld {
use Hello, World;
} class MyHelloWorld {
use HelloWorld;
} $o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
?>

以上例程会输出:

Hello World!

Trait 的抽象成员

为了对使用的类施加强制要求,trait 支持抽象方法的使用。

Example #8 表示通过抽象方法来进行强制要求

 <?php
trait Hello {
public function sayHelloWorld() {
echo 'Hello'.$this->getWorld();
}
abstract public function getWorld();
} class MyHelloWorld {
private $world;
use Hello;
public function getWorld() {
return $this->world;
}
public function setWorld($val) {
$this->world = $val;
}
}
?>

Trait 的静态成员

Traits 可以被静态成员静态方法定义。

Example #9 静态变量

 <?php
trait Counter {
public function inc() {
static $c = 0;
$c = $c + 1;
echo "$c\n";
}
} class C1 {
use Counter;
} class C2 {
use Counter;
} $o = new C1(); $o->inc(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>

Example #10 静态方法

 <?php
trait StaticExample {
public static function doSomething() {
return 'Doing something';
}
} class Example {
use StaticExample;
} Example::doSomething();
?>

属性

Trait 同样可以定义属性。

Example #11 定义属性

 <?php
trait PropertiesTrait {
public $x = 1;
} class PropertiesExample {
use PropertiesTrait;
} $example = new PropertiesExample;
$example->x;
?>

Trait 定义了一个属性后,类就不能定义同样名称的属性,否则会产生 fatal error。 有种情况例外:属性是兼容的(同样的访问可见度、初始默认值)。 在 PHP 7.0 之前,属性是兼容的,则会有 E_STRICT 的提醒。

Example #12 解决冲突

 <?php
trait PropertiesTrait {
public $same = true;
public $different = false;
} class PropertiesExample {
use PropertiesTrait;
public $same = true; // PHP 7.0.0 后没问题,之前版本是 E_STRICT 提醒
public $different = true; // 致命错误
}
?>

原文地址:https://www.php.net/traits

PHP trait介绍的更多相关文章

  1. PHP的Trait机制

    PHP的Trait机制 Trait介绍: 1.自PHP5.4起,PHP实现了一种代码复用的方法,称为trait.2.Trait是为类似PHP的单继承语言二准备的一种代码复用机制.3.Trait为了减少 ...

  2. ThinkPHP5.1入门

    ThinkPHP5.1入门 ===================================Composer的官方网站:https://www.phpcomposer.com/========= ...

  3. PHP namespace、abstract、interface、trait使用介绍

    小菜鸟一枚,一直搞不懂 namespace.abstract.interface.trait 这些关系,就抽出几天时间研究,做个总结,不足之处希望大家指正交流. namespace 命名空间 介绍:顾 ...

  4. php中trait(性状)与generator(生成器)

    PHP中trait(性状)与generator(生成器) 一.trait (性状) 最近在看Josh Lockhat的<Modern PHP>,这本书很薄.但是其中给出了一个很重要的学习方 ...

  5. 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait

    [源码下载] 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait 作者:webabcd 介绍速战速决 之 PHP 类基础 抽象类 接口 trait 示例1.类的相关知识点 1(基础 ...

  6. Scala的trait

    一:说明 1.介绍 2.功能 二:具体解释功能 1.定义接口 2.定义方法 3.定义字段 4.定义抽象字段 5.混合trait

  7. 利用GCTA工具计算复杂性状/特征(Complex Trait)的遗传相关性(genetic correlation)

    如文章"Genome-wide Complex Trait Analysis(GCTA)-全基因组复杂性状分析"中介绍的GCTA,是一款基于全基因组关联分析发展的分析工具,除了计算 ...

  8. Scalaz(55)- scalaz-stream: fs2-基础介绍,fs2 stream transformation

    fs2是scalaz-stream的最新版本,沿用了scalaz-stream被动式(pull model)数据流原理但采用了全新的实现方法.fs2比较scalaz-stream而言具备了:更精简的基 ...

  9. Scalaz(46)- scalaz-stream 基础介绍

    scalaz-stream是一个泛函数据流配件库(functional stream combinator library),特别适用于函数式编程.scalar-stream是由一个以上各种状态的Pr ...

随机推荐

  1. Redis Desktop Manager的下载及安装

    一.下载Redis Desktop Manager 1. Redis Desktop Manager 的下载路径 (1)https://pan.baidu.com/s/1Jvr9MbgFn4UJh4M ...

  2. STM32 + RT Thread OS 串口通讯

    1.   创建项目 a)   禁用Finsh和console b)   默认情况下,项目文件包含了finsh,它使用COM1来通讯,另外,console输出(rt_kprintf)也使用了COM1.因 ...

  3. MindManager2018试用期过后 修改过期时间 破解使用

    MindManager2018试用期过后 修改过期时间 破解使用 2019年06月13日 15:58:11 一生中所爱 阅读数 1991更多 分类专栏: 工具软件使用   1.找到路径:C:\User ...

  4. 19 Flutter 自定义AppBar 定义顶部Tab切换 底部Tab结合顶部Tab实现类似头条页面布局(27分36秒)

    Flutter AppBar自定义顶部导航按钮图标.颜色以及TabBar定义顶部Tab切换. leading:在标题前面显示的一个控件,在首页通常显示应用的logo:在其他界面通常显示为付汇按钮. t ...

  5. python数据分析数据标准化及离散化详解

    python数据分析数据标准化及离散化详解 本文为大家分享了python数据分析数据标准化及离散化的具体内容,供大家参考,具体内容如下 标准化 1.离差标准化 是对原始数据的线性变换,使结果映射到[0 ...

  6. Jsoup-基础练习

    认识Jsoup 一个解析网页的工具 无论你用什么语言爬虫,都要解析网页,今天,我们用一款常用的网页解析Jsoup,来开启爬虫的第一课 认识网页,认识爬虫,认识你自己 *** 快速上手 了解一个新东西最 ...

  7. java cpu 使用率100%

    --宝典开始 top :查看 进程 ,选CPU使用率高的 获取进程ID,pid top -Hp pid:查看线程,选CPU使用率高的 获取线程ID,threadid printf "%X\n ...

  8. 小程序 实现fadeIn 渐变的效果

    const winHeight = wx.getSystemInfoSync().windowHeight Page({ data: { logs: [] }, onLoad: function () ...

  9. java 工程编码格式由GBK转化成utf-8 (转载)

    import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import ja ...

  10. vue等单页面应用及其优缺点

    优点: Vue 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件,核心是一个响应的数据绑定系统.MVVM.数据驱动.组件化.轻量.简洁.高效.快速.模块友好. 缺点: 不支持低版本 ...