检查一个实例的类是否实现了一个接口?

192

给定一个类实例,是否可以确定它是否实现了特定的接口?据我所知,没有直接执行此操作的内置函数。我有哪些选项(如果有)?

给定一个类的实例,如何判断其是否实现了某个特定的接口呢?据我所知,没有直接的内置函数可以完成这个任务。那么我有哪些选项呢?
6个回答

318
interface IInterface
{
}

class TheClass implements IInterface
{
}

$cls = new TheClass();
if ($cls instanceof IInterface) {
    echo "yes";
}

你可以使用 "instanceof" 运算符。使用它时,左操作数是一个类实例,右操作数是一个接口。如果对象实现了特定的接口,则返回 true。
参考:https://www.php.net/manual/en/language.operators.type.php#example-124

请注意,如果您试图确定类的构造函数,则此内容将无用。 - AaA

132

正如therefromhere所指出的那样,您可以使用class_implements()。就像反射一样,这允许您将类名指定为字符串,而不需要类的实例:

interface IInterface
{
}

class TheClass implements IInterface
{
}

$interfaces = class_implements('TheClass');

if (isset($interfaces['IInterface'])) {
    echo "Yes!";
}

class_implements()是SPL扩展的一部分。

参见:http://php.net/manual/en/function.class-implements.php

性能测试

一些简单的性能测试显示了每种方法的成本:

给定一个对象实例

在循环外构建对象(100,000次迭代)
 ____________________________________________
| class_implements | Reflection | instanceOf |
|------------------|------------|------------|
| 140毫秒         | 290毫秒     | 35毫秒      |
'--------------------------------------------'
在循环内构建对象(100,000次迭代) ____________________________________________ | class_implements | Reflection | instanceOf | |------------------|------------|------------| | 182毫秒 | 340毫秒 | 83毫秒 | Cheap Constructor | 431毫秒 | 607毫秒 | 338毫秒 | Expensive Constructor '--------------------------------------------'

仅给定类名

100,000次迭代
 ____________________________________________
| class_implements | Reflection | instanceOf |
|------------------|------------|------------|
| 149毫秒         | 295毫秒     | N/A        |
'--------------------------------------------'

其中贵重的__construct()位于:

public function __construct() {
    $tmp = array(
        'foo' => 'bar',
        'this' => 'that'
    );  

    $in = in_array('those', $tmp);
}

这些测试基于这个简单代码


62

nlaq指出,instanceof可用于测试对象是否是实现接口的类的实例。

但是instanceof不能区分类类型和接口。您不知道对象是否是被称为IInterface

您还可以在PHP中使用反射API进行更具体的测试:

$class = new ReflectionClass('TheClass');
if ($class->implementsInterface('IInterface'))
{
  print "Yep!\n";
}

请参见http://php.net/manual/zh/book.reflection.php


2
这可以用于“静态”类。 - Markus Hedlund
7
另请参阅class_implements() - John Carter
@therefromhere:谢谢,好的提示。那是SPL扩展的一部分。我的答案使用了Reflection扩展。 - Bill Karwin
3
如果您使用命名空间,那么接口和类具有相同名称时就不会产生歧义,您可以安全地再次使用 instanceof - flu
由于调用class_implements然后调用in_array显然比进行完整的反射更快,因此对class_implements()加1。 - Nickolaus

28

更新

这里缺少is_a 函数作为替代方案。

我进行了一些性能测试,以确定哪种方法是最高效的。

100k次迭代的结果

      instanceof [object] took   7.67 ms | +  0% | ..........
            is_a [object] took  12.30 ms | + 60% | ................
             is_a [class] took  17.43 ms | +127% | ......................
class_implements [object] took  28.37 ms | +270% | ....................................
       reflection [class] took  34.17 ms | +346% | ............................................

添加了一些点以实际"感受"并看到区别。

由此生成:https://3v4l.org/8Cog7

结论

如果你有一个要检查的对象,请像接受的答案中提到的那样使用instanceof

如果你有一个要检查的,请使用is_a

奖励

假设你想根据所需接口实例化一个类,使用is_a更加高效。只有一个例外-当构造函数为空时。

示例: is_a(<className>, <interfaceName>, true);

它将返回bool。第三个参数“allow_string”允许其在不实例化类的情况下检查类名。


1
这很好,将 is_subclass_of 添加到脚本中。https://3v4l.org/FY4re - Dan Mason
谢谢...这个方法有效而且比被接受的答案更简洁。 - undefined

27
只是为了帮助未来的搜索,is_subclass_of也是一个不错的选择(适用于PHP 5.3.7+)。
if (is_subclass_of($my_class_instance, 'ISomeInterfaceName')){
    echo 'I can do it!';
}

6
您还可以进行以下操作:
public function yourMethod(YourInterface $objectSupposedToBeImplementing) {
   //.....
}

如果$objectSupposedToBeImplementing没有实现YourInterface 接口,它会抛出可恢复的错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接