【发布时间】:2016-07-30 15:59:21
【问题描述】:
检查一个类是否使用某种特征的正确方法是什么?
【问题讨论】:
-
是吗?据我所知,它不是(
$x instanceof SomeTrait永远是假的)。 -
instanceof总是带有 trait 的错误。特征不是某事的任何实例。
标签: php class instanceof traits
检查一个类是否使用某种特征的正确方法是什么?
【问题讨论】:
$x instanceof SomeTrait 永远是假的)。
instanceof 总是带有 trait 的错误。特征不是某事的任何实例。
标签: php class instanceof traits
虽然没有什么能阻止您使用方法来确定类是否使用特征,但推荐的方法是将特征与接口配对。所以你有:
class Foo implements MyInterface
{
use MyTrait;
}
其中MyTrait 是MyInterface 的实现。
然后你检查接口而不是像这样的特征:
if ($foo instanceof MyInterface) {
...
}
你也可以输入提示,这是你不能用特征做的:
function bar(MyInterface $foo) {
...
}
如果您绝对需要知道一个类是否使用了某个特性或实现,您可以向接口添加另一个方法,该方法根据实现返回不同的值。
【讨论】:
use BehaviorATrait; use BehaviorBTrait; 产生不同的行为变体时,它应该基于一些受保护的方法接口。当然,这不是一个好的设计,而是一种工具灵活性的讨论。
class_uses 成功了。但是,说为 trait 创建冗余接口是“推荐的方法”,这是错误的。实际上它会增加错误倾向,因为当您忘记定义接口和特征时,可能会发生意外行为。另一方面,您不需要单独定义接口和特征的灵活性。所以保持简单!
$this。接口确保您(和其他人)理解您的代码并使您的代码更加可靠......嗯......健壮。
您可以使用class_uses 函数来获取一个类使用的所有特征的数组。
然后你检查这个数组是否有一个与你正在测试的特征同名的键。
如果是这样,那么您的班级正在使用您的特质。 如果没有,那么它没有使用它。
【讨论】:
instanceof 之类的语言结构进行检查要昂贵得多。我会推荐任何遵循上面建议的instanceof FooInterface 方法的人,即使它对非公共方法有不利之处。
它不是很干净,可能不是适合您的情况的解决方案。但另一种方法是检查对象或类是否实现了 Trait 的方法(因为通常你不会用 Trait 覆盖现有方法)
if (method_exists($my_object, 'MyTraitSpecificMethod')){
...
}
【讨论】:
我刚刚发现 Laravel 是如何解决这个问题的,我想在这里分享一下。它在下面使用class_uses,但会遍历所有父母以递归方式找到所有特征。
它定义了一个名为class_uses_recursive的辅助函数:
function class_uses_recursive($class)
{
if (is_object($class)) {
$class = get_class($class);
}
$results = [];
foreach (array_reverse(class_parents($class)) + [$class => $class] as $class) {
$results += trait_uses_recursive($class);
}
return array_unique($results);
}
function trait_uses_recursive($trait)
{
$traits = class_uses($trait);
foreach ($traits as $trait) {
$traits += trait_uses_recursive($trait);
}
return $traits;
}
你可以这样使用它:
in_array(MyTrait::class, class_uses_recursive($class));
您可以看到他们如何使用它来检查模型是否实现了 SoftDeletes trait here:
public function throughParentSoftDeletes()
{
return in_array(SoftDeletes::class, class_uses_recursive($this->throughParent));
}
【讨论】:
您可以使用反射作为附加选项。
$reflection = new ReflectionClass($this)
$usedTraits = $reflection->getTraitNames();
if (in_array(MyTrait::class, $usedTraits)) {
// do Something
}
【讨论】: