Sprawdzanie, czy klasa instancji implementuje interfejs?

148

Czy przy danej instancji klasy można określić, czy implementuje ona określony interfejs? O ile wiem, nie ma wbudowanej funkcji, która mogłaby to zrobić bezpośrednio. Jakie mam opcje (jeśli w ogóle)?

Wilco
źródło

Odpowiedzi:

258
interface IInterface
{
}

class TheClass implements IInterface
{
}

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

Możesz użyć operatora „instanceof”. Aby go użyć, lewy operand jest instancją klasy, a prawy operand jest interfejsem. Zwraca prawdę, jeśli obiekt implementuje określony interfejs.

Tomáš Votruba
źródło
102

Jak stąd wynika , możesz użyć class_implements(). Podobnie jak w przypadku Reflection, pozwala to określić nazwę klasy jako ciąg i nie wymaga instancji klasy:

interface IInterface
{
}

class TheClass implements IInterface
{
}

$interfaces = class_implements('TheClass');

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

class_implements() jest częścią rozszerzenia SPL.

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

Testy wydajności

Niektóre proste testy wydajności pokazują koszty każdego podejścia:

Biorąc pod uwagę wystąpienie obiektu

Konstrukcja obiektu poza pętlą (100 000 iteracji)
 ____________________________________________
| narzędzia_klasy | Odbicie | instanceOf |
| ------------------ | ------------ | ------------ |
| 140 ms | 290 ms | 35 ms |
„--------------------------------------------”

Konstrukcja obiektu wewnątrz pętli (100 000 iteracji)
 ____________________________________________
| narzędzia_klasy | Odbicie | instanceOf |
| ------------------ | ------------ | ------------ |
| 182 ms | 340 ms | 83 ms | Tani konstruktor
| 431 ms | 607 ms | 338 ms | Drogi konstruktor
„--------------------------------------------”

Podano tylko nazwę klasy

100 000 iteracji
 ____________________________________________
| narzędzia_klasy | Odbicie | instanceOf |
| ------------------ | ------------ | ------------ |
| 149 ms | 295 ms | Nie dotyczy |
„--------------------------------------------”

Gdzie kosztowna __construct () to:

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

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

Te testy są oparte na tym prostym kodzie .

Jess Telford
źródło
56

nlaq wskazuje, że instanceofmożna go użyć do sprawdzenia, czy obiekt jest instancją klasy implementującej interfejs.

Ale instanceofnie rozróżnia między typem klasy a interfejsem. Nie wiesz, czy obiekt jest klasą, która została wywołana IInterface.

Możesz również użyć interfejsu API odbicia w PHP, aby przetestować to bardziej szczegółowo:

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

Zobacz http://php.net/manual/en/book.reflection.php

Bill Karwin
źródło
2
Może to być używane w klasach „statycznych”
Znarkus
6
Zobacz takżeclass_implements()
John Carter,
@therefromhere: Dzięki, dobra wskazówka. To część rozszerzenia SPL. W mojej odpowiedzi wykorzystałem rozszerzenie Reflection.
Bill Karwin,
3
Jeśli używasz przestrzeni nazw, nie będzie niejednoznaczności między interfejsami i klasami o tej samej nazwie i możesz bezpiecznie użyć instanceofponownie.
grypa
+1 za, class_implements()ponieważ jest oczywiście szybsze wywołanie class_implements, a następnie in_array, zamiast dokonywać pełnej refleksji
Nickolaus
19

Aby ułatwić przyszłe wyszukiwania, is_subclass_of jest również dobrym wariantem (dla PHP 5.3.7+):

if (is_subclass_of($my_class_instance, 'ISomeInterfaceName')){
    echo 'I can do it!';
}
d.raev
źródło
5

Możesz także wykonać następujące czynności

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

Jeśli interfejs $objectSupposedToBeImplementingnie zaimplementuje YourInterfaceinterfejsu, zostanie wyświetlony możliwy do naprawienia błąd .

Starx
źródło
3

Aktualizacja

is_a Funkcja brakuje tutaj jako alternatywę.

Zrobiłem kilka testów wydajności, aby sprawdzić, który z podanych sposobów jest najbardziej wydajny.

Wyniki ponad 100 tys. Iteracji

      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% | ............................................

Dodano kilka kropek, aby faktycznie „poczuć” różnicę.

Wygenerowane przez to: https://3v4l.org/8Cog7

Wniosek

Jeśli masz obiekt do sprawdzenia, użyj tego, co instance ofpodano w zaakceptowanej odpowiedzi.

Jeśli masz klasę do sprawdzenia, użyj is_a.

Premia

Biorąc pod uwagę przypadek, w którym chcesz utworzyć instancję klasy w oparciu o interfejs, którego potrzebujesz, jest to bardziej wydajne w użyciu is_a. Jest tylko jeden wyjątek - gdy konstruktor jest pusty.

Przykład: is_a(<className>, <interfaceName>, true);

Wróci bool. Trzeci parametr „ allow_string ” umożliwia sprawdzanie nazw klas bez tworzenia instancji klasy.

Pilan
źródło