【问题标题】:PHP instanceof for traits用于特征的 PHP instanceof
【发布时间】:2016-07-30 15:59:21
【问题描述】:

检查一个类是否使用某种特征的正确方法是什么?

【问题讨论】:

  • 是吗?据我所知,它不是($x instanceof SomeTrait 永远是假的)。
  • instanceof 总是带有 trait 的错误。特征不是某事的任何实例。

标签: php class instanceof traits


【解决方案1】:

虽然没有什么能阻止您使用方法来确定类是否使用特征,但推荐的方法是将特征与接口配对。所以你有:

class Foo implements MyInterface
{
    use MyTrait;
}

其中MyTraitMyInterface 的实现。

然后你检查接口而不是像这样的特征:

if ($foo instanceof MyInterface) {
    ...
}

你也可以输入提示,这是你不能用特征做的:

function bar(MyInterface $foo) {
    ...
}

如果您绝对需要知道一个类是否使用了某个特性或实现,您可以向接口添加另一个方法,该方法根据实现返回不同的值。

【讨论】:

  • 这种方法在非公共逻辑实现的情况下没有用处。例如,当您需要使用简单的use BehaviorATrait; use BehaviorBTrait; 产生不同的行为变体时,它应该基于一些受保护的方法接口。当然,这不是一个好的设计,而是一种工具灵活性的讨论。
  • 问题是关于traits,它不能实现接口。上面的 sn-p 只适用于类。
  • 这个答案在两个方面是错误的。首先:它没有回答最初的问题,即如何检查对象中使用的特征。 class_uses 成功了。但是,说为 trait 创建冗余接口是“推荐的方法”,这是错误的。实际上它会增加错误倾向,因为当您忘记定义接口和特征时,可能会发生意外行为。另一方面,您不需要单独定义接口和特征的灵活性。所以保持简单!
  • 恕我直言,@Armin 的评论也有问题。按照设计,接口总是“冗余”。然而,如果应用得当,它们不会增加错误倾向,但会减轻错误倾向,因为开发人员可以将关于预期什么样的对象的信息保存在一个地方,同时对各种实现细节保持开放。
  • 我同意@J.D.并补充说 (imho) Traits 可以(几乎)在没有接口的情况下安全地用于简单情况。如果您习惯于使用 Traits 并推动它们向前发展,那么总有一天您会发现自己需要接口,以保证隐式合同并防止错误的易碎代码。是的:这是多余的,而且大多数 PHP 括号都是,或者必须输入$this。接口确保您(和其他人)理解您的代码并使您的代码更加可靠......嗯......健壮。
【解决方案2】:

您可以使用class_uses 函数来获取一个类使用的所有特征的数组。

然后你检查这个数组是否有一个与你正在测试的特征同名的键。

如果是这样,那么您的班级正在使用您的特质。 如果没有,那么它没有使用它。

【讨论】:

  • 注意:class_uses 只告诉你类是否直接实现了 trait,而不是通过继承。检查 php.net 上的 cmets 以了解有关滚动您自己的方法的一些想法,该方法也遵循班级的祖先php.net/manual/en/function.class-uses.php
  • 调用方法然后在字符串数组中查找以了解类是否使用特征在运行时需要做太多工作。它比通过诸如instanceof 之类的语言结构进行检查要昂贵得多。我会推荐任何遵循上面建议的instanceof FooInterface 方法的人,即使它对非公共方法有不利之处。
【解决方案3】:

它不是很干净,可能不是适合您的情况的解决方案。但另一种方法是检查对象或类是否实现了 Trait 的方法(因为通常你不会用 Trait 覆盖现有方法)

if (method_exists($my_object, 'MyTraitSpecificMethod')){
    ...
}

【讨论】:

    【解决方案4】:

    我刚刚发现 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));
    }
    

    【讨论】:

      【解决方案5】:

      您可以使用反射作为附加选项。

      $reflection = new ReflectionClass($this)
      
      $usedTraits = $reflection->getTraitNames();
      
      if (in_array(MyTrait::class, $usedTraits)) {
          // do Something
      }
      

      【讨论】:

        猜你喜欢
        • 2014-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-20
        • 2018-07-31
        • 2013-07-23
        • 2019-08-09
        • 1970-01-01
        相关资源
        最近更新 更多