自 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类中的方法
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();
解决方案
- 使用 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中的代码示例:
<?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);
}
...
}
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。