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


148

З огляду на екземпляр класу, чи можна визначити, чи реалізує він певний інтерфейс? Наскільки я знаю, немає вбудованої функції, яка б це робила безпосередньо. Які варіанти у мене є (якщо такі є)?

Відповіді:


258
interface IInterface
{
}

class TheClass implements IInterface
{
}

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

Можна скористатися оператором "instanceof". Для його використання лівий операнд є екземпляром класу, а правий операнд - інтерфейсом. Він повертає true, якщо об'єкт реалізує певний інтерфейс.


102

Як звідти вказується, ви можете використовувати 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 | Відбиття | instanceOf |
| ------------------ | ------------ | ------------ |
| 140 мс | 290 мс | 35 мс |
'--------------------------------------------'

Побудова об'єкта всередині циклу (100 000 ітерацій)
 ____________________________________________
| class_implements | Відбиття | instanceOf |
| ------------------ | ------------ | ------------ |
| 182 мс | 340 мс | 83 мс | Дешевий конструктор
| 431 мс | 607 мс | 338 мс | Дорогий конструктор
'--------------------------------------------'

Дано лише назву класу

100 000 ітерацій
 ____________________________________________
| class_implements | Відбиття | instanceOf |
| ------------------ | ------------ | ------------ |
| 149 мс | 295 мс | Н / А |
'--------------------------------------------'

Де дорогий __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, а не робити повне роздуми
Ніколаус

19

Просто для подальшого пошуку is_subclass_of також є хорошим варіантом (для PHP 5.3.7+):

if (is_subclass_of($my_class_instance, 'ISomeInterfaceName')){
    echo 'I can do it!';
}

5

Ви також можете зробити наступне

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

Він видасть помилку, яку можна відновити, якщо $objectSupposedToBeImplementingне реалізує YourInterfaceінтерфейс.


3

Оновлення

Тут is_a функція відсутня як альтернатива.

Я зробив кілька тестів на ефективність, щоб перевірити, який із зазначених способів є найбільш ефективним.

Результати понад 100 к ітерацій

      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. Третій параметр " enable_string " дозволяє йому перевіряти назви класів без ініціалізації класу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.