Sprawdzanie, czy klasa instancji implementuje interfejs?

Biorąc pod uwagę instancję klasy, czy jest możliwe określenie, czy implementuje ona konkretny interfejs? Z tego, co wiem, nie ma wbudowanej funkcji, aby to zrobić bezpośrednio. Jakie opcje mam (jeśli istnieją)?

Author: Wilco, 2008-11-08

6 answers

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 true, jeśli obiekt implementuje określony interfejs.

 274
Author: Tomáš Votruba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-14 11:33:51

Jak wskazuje stąd , możesz użyć class_implements(). Podobnie jak w przypadku Reflection, pozwala to określić nazwę klasy jako łańcuch znaków i nie wymaga wystąpienia 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ściowe

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

Dana instancja obiekt

Object construction outside the loop (100,000 iterations)
 ____________________________________________
| class_implements | Reflection | instanceOf |
|------------------|------------|------------|
| 140 ms           | 290 ms     | 35 ms      |
'--------------------------------------------'

Object construction inside the loop (100,000 iterations)
 ____________________________________________
| class_implements | Reflection | instanceOf |
|------------------|------------|------------|
| 182 ms           | 340 ms     | 83 ms      | Cheap Constructor
| 431 ms           | 607 ms     | 338 ms     | Expensive Constructor
'--------------------------------------------'

Podano tylko nazwę klasy

100,000 iterations
 ____________________________________________
| class_implements | Reflection | instanceOf |
|------------------|------------|------------|
| 149 ms           | 295 ms     | N/A        |
'--------------------------------------------'

Gdzie drogie _ _ construct () jest:

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

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

Te testy są oparte na tym prostym kodzie .

 110
Author: Jess Telford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:23

Nlaq wskazuje, że instanceof może być użyty do sprawdzenia, czy obiekt jest instancją klasy implementującej interfejs.

Ale instanceof nie odróżnia typu klasy od interfejsu. Nie wiesz, czy obiekt jest klasą , która nazywa się IInterface.

Możesz również użyć API reflection w PHP, aby przetestować to dokładniej:

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

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

 57
Author: Bill Karwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-17 02:14:09

Aby pomóc przyszłym wyszukiwaniom 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!';
}
 21
Author: d.raev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-12 12:45:48

Update

The is_a funkcja nie jest tutaj dostępna jako alternatywa.

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

Wyniki ponad 100k 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ć" zobaczyć różnicę.

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

Podsumowanie

Jeśli masz obiekt do sprawdzenia, użyj instance of Jak wspomniano w akceptowanym odpowiedz.

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

Bonus

Biorąc pod uwagę przypadek, w którym chcesz utworzyć instancję klasy na podstawie wymaganego interfejsu, bardziej preformantowe jest użycie is_a. Jest tylko jeden wyjątek - gdy konstruktor jest pusty.

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

Zwróci bool. Trzeci parametr " allow_string" pozwala na sprawdzenie nazw klas bez tworzenia instancji klasy.

 6
Author: SirPilan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Możesz również wykonać następujące czynności]}

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

Spowoduje błąd, który można odzyskać, jeśli $objectSupposedToBeImplementing nie zaimplementuje interfejsu YourInterface.

 5
Author: Starx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-31 09:22:01