自 PHP 5.4.0 起,PHP 实现了代码复用的一个方法,称为 traits。
Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用方法集。Traits 和类组合的语义是定义了一种方式来减少复杂性,避免传统多继承和混入类(Mixin)相关的典型问题。
Trait 和一个类相似,但仅仅旨在用细粒度和一致的方式来组合功能。Trait 不能通过它自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用类的成员不需要继承。
基本使用
trait 申明:
<?php
trait vilay {
public function test1()
{
echo 'my is test1';
}
}
trait 使用:
<?php
trait vilay {
public function test1()
{
echo 'my is test1';
}
}
class Base {
use vilay;
}
$base = new Base;
$base->test1();
结果:
my is test1
优先级
从基类继承的成员被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
从Base基类继承来的myBase方法会被trait Mytrait中的myBase方法覆盖,看示例A。
示例A:
<?php
class Base {
public function mybase()
{
echo 'my is Base Class function';
}
}
trait Mytrait {
public function myBase()
{
echo 'my is trait function';
}
}
class Home extends Base {
use Mytrait;
}
$home = new Home;
$home->myBase();
输出:
my is trait function
还有另一种优先级,当前类的方法会覆盖trait 中的方法,看示例B:
示例B:
<?php
trait Mytrait {
public function myBase()
{
echo 'my is trait function';
}
}
class Home {
use Mytrait;
public function mybase()
{
echo 'my is Home Class function';
}
}
$home = new Home;
$home->myBase();
结果输出:
my is Home Class function
多个trait
使用多个trait用逗号隔开
示例C:
<?php
trait MyTrait {
public function myBase()
{
echo 'my is trait function';
}
}
trait OtherTrait {
public function otherBase()
{
echo 'my is other trait function';
}
}
class Home {
use Mytrait,OtherTrait;
}
$home = new Home;
$home->myBase();
echo '<br>';
$home->otherBase();
结果输出:
my is trait function
my is other trait function
trait冲突
如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。
为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof
操作符来明确__指定使用冲突方法中的哪一个。__
以上方式仅允许排除掉其它方法,as
操作符可以将__其中一个冲突的方法以另一个名称来引入__。
未解决冲突示例
示例D:
<?php
trait FunTrait {
public function myBase()
{
echo 'my is FunTrait mybase';
}
public function testBase()
{
echo 'my is FunTrait testbase';
}
}
trait TestTrait {
public function myBase()
{
echo 'my is TestTrait mybase';
}
public function testBase()
{
echo 'my is TestTrait testbase';
}
}
class Home {
use FunTrait,TestTrait;
}
$home = new Home;
$home->myBase();
echo '<br>';
$home->otherBase();
结果:
Fatal error: Trait method myBase has not been applied, because there are collisions with other trait methods on Home
解决冲突示例
示例E:
<?php
trait FunTrait {
public function myBase()
{
echo 'my is FunTrait mybase';
}
public function testBase()
{
echo 'my is FunTrait testbase';
}
}
trait TestTrait {
public function myBase()
{
echo 'my is TestTrait mybase';
}
public function testBase()
{
echo 'my is TestTrait testbase';
}
}
class Home {
use FunTrait,TestTrait {
FunTrait::myBase insteadof TestTrait;
TestTrait::testBase insteadof FunTrait;
TestTrait::myBase as tBase;
}
}
$home = new Home;
$home->myBase();
echo '<br>';
$home->testBase();
结果输出:
my is FunTrait mybase
my is TestTrait testbase
my is TestTrait mybase
修改方法的访问控制
使用 as
语法还可以用来调整方法的访问控制。
示例F:
<?php
trait FunTrait {
public function test()
{
echo 'my is FunTrait test Function';
}
}
class Home {
use FunTrait;
}
$home = new Home;
$home->test();
结果:
my is FunTrait test Function
修改访问控制
示例G:
<?php
trait FunTrait {
public function test()
{
echo 'my is FunTrait test Function';
}
}
class Home {
use FunTrait {
test as protected;
}
}
$home = new Home;
$home->test();
结果输出:
Fatal error: Uncaught Error: Call to protected method Home::test() from context '
修改访问控制并使用别名
示例H:
<?php
trait FunTrait {
public function test()
{
echo 'my is FunTrait test Function';
}
}
class Home {
use FunTrait {
test as protected myTest;
}
}
$home = new Home;
$home->test();
$home->myTest();
结果输出:
my is FunTrait test Function
Fatal error: Uncaught Error: Call to protected method Home::myTest() from context '‘
由示例H可以看出:
1. 使用别名之后原函数还存在,仍然可以继续使用
2. 修改函数别名的访问控制,对原函数无任何影响(使用别名只是生成了函数副本)
从 trait 来组成 trait
正如类能够使用 trait 一样,其它 trait 也能够使用 trait。在 trait 定义时通过使用一个或多个 trait,它能够组合其它 trait 中的部分或全部成员。
示例J:
<?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 支持抽象方法的使用。
示例K:
<?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;
}
}
$hello = new MyHelloWorld;
$hello->setWorld('vilay');
$hello->sayHelloWorld();
结果输出:
Hello vilay
Trait 的静态成员
Traits 可以被静态成员静态方法定义。
静态变量
示例L:
<?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();
$o->inc();
$p = new C2();
$p->inc();
结果输出:
1 2 1
静态方法
示例M:
<?php
trait StaticExample {
public static function doSomething() {
echo 'Doing something';
}
}
class Example {
use StaticExample;
}
Example::doSomething();
结果输出:
Doing something
属性
Trait 同样可以定义属性。
示例N:
<?php
trait PropertiesTrait {
public $x = 1;
}
class PropertiesExample {
use PropertiesTrait;
}
$example = new PropertiesExample;
$example->x;
结果输出:
<?php
trait PropertiesTrait {
public $x = 'vilay';
}
class PropertiesExample {
use PropertiesTrait;
}
$example = new PropertiesExample;
echo $example->x;
结果输出:
vilay
如果 trait 定义了一个属性,那类将不能定义同样名称的属性,否则会产生一个错误。如果该属性在类中的定义与在 trait 中的定义兼容(同样的可见性和初始值)则错误的级别是 E_STRICT,否则是一个致命错误。
同样的可见性和同样的初始值
示例O:
<?php
trait PropertiesTrait {
public $x = 'vilay';
}
class PropertiesExample {
public $x = 'vilay';
use PropertiesTrait;
}
$example = new PropertiesExample;
echo $example->x;
结果输出:
vilay
不同的可见性或者不同的初始值
示例P:
<?php
trait PropertiesTrait {
public $x = 'vilay';
}
class PropertiesExample {
public $x = 'hello';
use PropertiesTrait;
}
$example = new PropertiesExample;
echo $example->x;
结果输出:
Fatal error: PropertiesExample and PropertiesTrait define the same property ($x) in the composition of PropertiesExample. However, the definition differs and is considered incompatible. Class was composed in
示例Q:
<?php
trait PropertiesTrait {
public $x = 'vilay';
}
class PropertiesExample {
protected $x = 'vilay';
use PropertiesTrait;
}
$example = new PropertiesExample;
echo $example->x;
结果输出:
Fatal error: PropertiesExample and PropertiesTrait define the same property ($x) in the composition of PropertiesExample. However, the definition differs and is considered incompatible. Class was composed in