自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。
- Trait 是为相似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减小单继承语言的限制,使开发人员可以自由地在不一样层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减小复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。
- Trait 和 Class 类似,但仅仅旨在用细粒度和一致的方式来组合功能。 没法经过 trait 自身来实例化。它为传统继承增长了水平特性的组合;也就是说,应用的几个 Class 之间不须要继承。
<?php //日志类 class Logger { //读取日志信息 public function log(string $message, int $level) { echo "[message]:{$message}" . PHP_EOL; echo "[level]:{$level}" . PHP_EOL; } } //扩展日志功能 trait Loggable { protected $logger; /** * 记录日志 * @param DemoLogger $logger */ public function setLogger(Logger $logger) { $this->logger = $logger; } /** * 读取日志 * @param string $message * @param int $level */ public function getLog(string $message, int $level) { $this->logger->log($message, $level); } public function test() { echo 'trait test' . PHP_EOL; } } //基类 class Base { public static $className = 'Base'; public function test() { echo static::getClassName() . ' test' . PHP_EOL; } //获取类名称 public static function getClassName(): string { //return self::$className; return static::$className;//static延时静态绑定 } } class Foo extends Base { public static $className = 'Foo'; use Loggable; } $foo = new Foo; $foo->setLogger(new Logger); $foo->getLog('trait works', 1);//打印日志信息 $foo->test(); //trait test
分析这里 $foo->test()
- Foo类中使用use Loggable来扩展Foo类增长日志功能;
- Trait Loggable类中含有test()方法;
- Foo类继承Base类,其中Base类中含有test();
- 那么问题来了:$foo->test()到底调用的是继承自父类test(),仍是Trait类中的test()?
所以,$foo->test()调用的是Trait类中的方法php
Trait Alibaba { public function getCEO(): string { return '阿里巴巴CEO:马云' . PHP_EOL; } public function getAddress(): string { return '阿里巴巴总部位于杭州' . PHP_EOL; } } Trait Tencent { public function getCEO(): string { return '腾讯CEO:马化腾' . PHP_EOL; } public function getAddress(): string { return '腾讯总部位于深圳' . PHP_EOL; } } class TopBoss { use Alibaba, Tencent; } $MaBoss = new TopBoss(); echo $MaBoss->getCEO(); echo $MaBoss->getAddress();
解决方案ide
- 使用 insteadof(取代) 操做符来明确指定使用冲突方法中的哪个
- as 操做符能够 为某个方法引入别名。 注意,as 操做符不会对方法进行重命名,也不会影响其方法。
最终代码:函数
class TopBoss { use Alibaba, Tencent { Tencent::getCEO insteadof Alibaba;//指定冲突时,使用谁 Tencent::getAddress insteadof Alibaba; Alibaba::getAddress as getA;//取别名,能够经过别名调用 Alibaba::getCEO as getC; } } $MaBoss = new TopBoss(); echo $MaBoss->getCEO();//腾讯CEO:马化腾 echo $MaBoss->getAddress();//腾讯总部位于深圳 echo $superBoss->getC();//阿里巴巴CEO:马云 echo $superBoss->getA();//阿里巴巴总部位于杭州
Laravel中的代码示例:this
<?php namespace Illuminate\Support; use ArrayAccess; class Optional implements ArrayAccess { use Traits\Macroable { __call as macroCall; } ... /** * Dynamically pass a method to the underlying object. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { if (static::hasMacro($method)) { return $this->macroCall($method, $parameters); } ... }