PHP Trait 只能定义成员变量和方法,不能定义常量。如下代码会报错:
// test.php
trait UsageTrait
{
const THE_CONST_NAME = 1;
}
错误消息为:
Fatal error: Traits cannot have constants in test.php
想要在不同的类中共用一组常量,可以通过接口来解决:
interface UsageConstantsInterface
{
const THE_CONST_NAME = __CLASS__;
}
trait UsageTrait
{
public function usageForBar()
{
echo UsageConstantsInterface::THE_CONST_NAME, PHP_EOL;
}
public function usageForFoo()
{
// 如果使用 UsageTrait 的类实现了 UsageConstantsInterface 接口,
// 也可以使用 self::THE_CONST_NAME 来访问常量echo self::THE_CONST_NAME, PHP_EOL;
}
}
class Foo implements UsageConstantsInterface
{
use UsageTrait;
}
class Bar
{
use UsageTrait;
}
(new Foo)->usageForFoo();
(new Bar)->usageForBar();
另外,PHP Trait 的 insteadof
和 as
语句只能处理方法名的冲突,而无法处理成员变量的冲突。如果在 Trait 和类中都定义了同名的成员变量,当它们的初始值不一致时,编译器会报错。如下代码可以正常执行:
trait UsageTrait
{
protected $property1;
protected $property2 = 'some value';
protected $property3 = __CLASS__;
}
class Foo
{
use UsageTrait;
protected $property1;
protected $property2 = 'some value';
// 非常有意思的是,$property3 这样初始化不会引发冲突
// 虽然两处的 __CLASS__ 的实际值并不相同protected $property3 = __CLASS__;
}
new Foo;
如下代码则会报错:
// test.php
trait UsageTrait
{
protected $property = 'default value';
}
class Foo
{
use UsageTrait;
protected $property = 'new value';
}
new Foo;
错误消息为:
Fatal error: Foo and UsageTrait define the same property ($property) in the composition of Foo. However, the definition differs and is considered incompatible. Class was composed in test.php on line 8
在某些场景下,我们希望在 Trait 里定义一个缺省的成员变量,而在类中可以覆盖这个成员变量的值。一种方案是在子类中覆盖这个成员变量,代码如下:
trait UsageTrait
{
protected $property = 'default value';
}
abstract class Foo
{
use UsageTrait;
}
class Bar extends Foo
{
protected $property = 'new value';
}
但实际上,我们之所以使用 Trait 这种 Mixin 方式就是为了更加灵活地、低耦合地组合代码。而上面的解决方案要求使用 UsageTrait 的类必须继承自同一个抽象类,显然违背了 Trait 的初衷。在没有更好的解决方案情况下,只能自己多写几行代码了:
/**
* @property string $property
*/
trait UsageTrait
{
private $_property = 'default value';
public function __get($name)
{
if ($name == 'property') {
return $this->_property;
}
}
public function usage()
{
echo $this->property, PHP_EOL;
return $this;
}
}
class Foo
{
use UsageTrait;
protected $property = 'new value';
}
class Bar
{
use UsageTrait;
}
(new Foo)->usage(); // 打印 "new value"
(new Bar)->usage();