Проверка, реализует ли класс экземпляра интерфейс?

148

Учитывая экземпляр класса, возможно ли определить, реализует ли он конкретный интерфейс? Насколько я знаю, нет встроенной функции, чтобы сделать это напрямую. Какие варианты у меня есть (если есть)?

Wilco
источник

Ответы:

258
interface IInterface
{
}

class TheClass implements IInterface
{
}

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

Вы можете использовать оператор instanceof. Чтобы использовать его, левый операнд является экземпляром класса, а правый операнд является интерфейсом. Возвращает true, если объект реализует определенный интерфейс.

Томаш Вотруба
источник
102

Как оттуда указывает, вы можете использовать class_implements(). Как и в Reflection, это позволяет вам указывать имя класса в виде строки и не требует экземпляра класса:

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 | Отражение instanceOf |
| ------------------ | ------------ | ------------ |
| 140 мс | 290 мс | 35 мс |
'--------------------------------------------'

Построение объекта внутри цикла (100 000 итераций)
 ____________________________________________
| class_implements | Отражение instanceOf |
| ------------------ | ------------ | ------------ |
| 182 мс | 340 мс | 83 мс | Дешевый конструктор
| 431 мс | 607 мс | 338 мс | Дорогой Конструктор
'--------------------------------------------'

Дано только имя класса

100 000 итераций
 ____________________________________________
| class_implements | Отражение instanceOf |
| ------------------ | ------------ | ------------ |
| 149 мс | 295 мс | N / A |
'--------------------------------------------'

Где дорогой __construct ():

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

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

Эти тесты основаны на этом простом коде .

Джесс Телфорд
источник
56

Nlaq указывает, что instanceofможно использовать для проверки, является ли объект экземпляром класса, который реализует интерфейс.

Но instanceofне различает тип класса и интерфейс. Вы не знаете, является ли объект классом, который вызывается IInterface.

Вы также можете использовать API отражения в PHP, чтобы проверить это более конкретно:

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

Смотрите http://php.net/manual/en/book.reflection.php

Билл Карвин
источник
2
Это может быть использовано на «статических» классах
Znarkus
6
Смотрите такжеclass_implements()
Джон Картер
@therefromhere: Спасибо, хороший совет. Это часть расширения SPL. Мой ответ использовал расширение Reflection.
Билл Карвин
3
Если вы используете пространства имен, то между интерфейсами и классами с одинаковыми именами не будет неоднозначности, и вы сможете безопасно использовать instanceofснова.
грипп
+1, class_implements()потому что, очевидно, быстрее вызывать class_implements, а затем in_array, вместо полного отражения
Nickolaus
19

Просто для помощи в будущих поисках is_subclass_of также является хорошим вариантом (для PHP 5.3.7+):

if (is_subclass_of($my_class_instance, 'ISomeInterfaceName')){
    echo 'I can do it!';
}
d.raev
источник
5

Вы также можете сделать следующее

public function yourMethod(YourInterface $objectSupposedToBeImplementing) {
   //.....
}

Он выдаст исправимую ошибку, если $objectSupposedToBeImplementingне реализует YourInterfaceинтерфейс.

Starx
источник
3

Обновить

is_a Функция здесь отсутствует как альтернатива.

Я провел несколько тестов производительности, чтобы проверить, какой из указанных способов наиболее эффективен.

Результаты более 100 000 итераций

      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

Вывод

Если у вас есть объект для проверки, используйте instance ofкак указано в принятом ответе.

Если у вас есть класс для проверки, используйте is_a.

бонус

Учитывая тот случай, когда вы хотите создать экземпляр класса, основанного на интерфейсе, который вам необходим, он более предпочтителен для использования is_a. Есть только одно исключение - когда конструктор пуст.

Пример: is_a(<className>, <interfaceName>, true);

Это вернется bool. Третий параметр " allow_string " позволяет ему проверять имена классов без создания экземпляра класса.

Pilan
источник