Trait 本质上是无状态的mixin,是PHP解释器自动完成的『粘贴&拷贝』,区别于继承,它提供的重用是水平性的,很好地提高了代码的可重用。然而错误使用会造成冲突,BUG难以调试。先来看看 trait 的其中一种正确使用方式。
interface Logger
{
public function log($message, $level);
}
class DemoLogger implements Logger
{
public function log($message, $level)
{
echo "Logged message: $message with level $level", PHP_EOL;
}
}
trait Loggable // implements Logger
{
protected $logger;
public function setLogger(Logger $logger)
{
$this->logger = $logger;
}
public function log($message, $level)
{
$this->logger->log($message, $level);
}
}
class Foo implements Logger
{
use Loggable;
}
然后我们可以像下面这样使用:
$foo = new Foo;
$foo->setLogger(new DemoLogger);
$foo->log('It works', 1);
下面是一些错误使用 trait 的范例。
trait T {
protected function foo() {}
}
class A {
public function foo() {}
}
class B extends A
{
use T;
}
这段代码会因为修改 foo函数的可见性而引起报错。
trait T {
public function foo() {
return 1;
}
}
class A {
use T;
public function foo() {
return 2;
}
}
$a = new A;
echo $a->foo();
上面代码会输出 2,trait 中的定义的方法永远不会得到执行。另外,trait 中不应该调用concrete class的任何方法或者属性,看下面例子:
class A
{
use T;
protected $prop = 1;
protected function getProp() {
return $this->prop;
}
}
trait T
{
public function foo()
{
return $this->getProp();
}
}
$a = new A;
echo $a->foo();
上面代码,trait中调用了特定类实例的方法,从而造成 trait 对具体实现类的依赖,丧失了trait代码 的可重用性,这种使用方式是错误的。