Überprüfen, ob die Klasse einer Instanz eine Schnittstelle implementiert?

148

Kann anhand einer Klasseninstanz festgestellt werden, ob eine bestimmte Schnittstelle implementiert ist? Soweit ich weiß, gibt es keine eingebaute Funktion, um dies direkt zu tun. Welche Optionen habe ich (falls vorhanden)?

Wilco
quelle

Antworten:

258
interface IInterface
{
}

class TheClass implements IInterface
{
}

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

Sie können den Operator "instanceof" verwenden. Um es zu verwenden, ist der linke Operand eine Klasseninstanz und der rechte Operand eine Schnittstelle. Es gibt true zurück, wenn das Objekt eine bestimmte Schnittstelle implementiert.

Tomáš Votruba
quelle
102

Wie therefromhere weist darauf hin, können Sie verwenden class_implements(). Genau wie bei Reflection können Sie hiermit den Klassennamen als Zeichenfolge angeben und benötigen keine Instanz der Klasse:

interface IInterface
{
}

class TheClass implements IInterface
{
}

$interfaces = class_implements('TheClass');

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

class_implements() ist Teil der SPL-Erweiterung.

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

Leistungstests

Einige einfache Leistungstests zeigen die Kosten jedes Ansatzes:

Gegeben eine Instanz eines Objekts

Objektkonstruktion außerhalb der Schleife (100.000 Iterationen)
 ____________________________________________
| class_implements | Reflexion | instanceOf |
| ------------------ | ------------ | ------------ |
| 140 ms | 290 ms | 35 ms |
'----------------------------------------'

Objektkonstruktion innerhalb der Schleife (100.000 Iterationen)
 ____________________________________________
| class_implements | Reflexion | instanceOf |
| ------------------ | ------------ | ------------ |
| 182 ms | 340 ms | 83 ms | Billiger Konstruktor
| 431 ms | 607 ms | 338 ms | Teurer Konstruktor
'----------------------------------------'

Nur einen Klassennamen angegeben

100.000 Iterationen
 ____________________________________________
| class_implements | Reflexion | instanceOf |
| ------------------ | ------------ | ------------ |
| 149 ms | 295 ms | N / A |
'----------------------------------------'

Wo das teure __construct () ist:

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

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

Diese Tests basieren auf diesem einfachen Code .

Jess Telford
quelle
56

nlaq weist darauf hin, dass damit getestet werden instanceofkann, ob das Objekt eine Instanz einer Klasse ist, die eine Schnittstelle implementiert.

Aber instanceofunterscheidet nicht zwischen einem Klasse - Typ und einer Schnittstelle. Sie wissen nicht, ob das Objekt eine Klasse ist , die zufällig aufgerufen wird IInterface.

Sie können auch die Reflection-API in PHP verwenden, um dies genauer zu testen:

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

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

Bill Karwin
quelle
2
Dies kann für "statische" Klassen verwendet werden
Znarkus
6
Siehe auchclass_implements()
John Carter
@therefromhere: Danke, guter Tipp. Das ist Teil der SPL-Erweiterung. Meine Antwort verwendete die Reflection-Erweiterung.
Bill Karwin
3
Wenn Sie Namespaces verwenden, besteht keine Mehrdeutigkeit zwischen Schnittstellen und Klassen mit demselben Namen, und Sie können sie sicher instanceofwieder verwenden.
Grippe
+1 für class_implements()da es offensichtlich schneller ist, class_implements und dann in_array aufzurufen, anstatt eine vollständige Reflexion zu machen
Nickolaus
19

Nur um zukünftige Suchen zu erleichtern, ist is_subclass_of auch eine gute Variante (für PHP 5.3.7+):

if (is_subclass_of($my_class_instance, 'ISomeInterfaceName')){
    echo 'I can do it!';
}
d.raev
quelle
5

Sie können auch Folgendes tun

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

Es wird ein behebbarer Fehler ausgegeben, wenn die Schnittstelle $objectSupposedToBeImplementingnicht implementiert wird YourInterface.

Starx
quelle
3

Aktualisieren

Die is_a Funktion fehlt hier alternativ.

Ich habe einige Leistungstests durchgeführt, um zu überprüfen, welche der angegebenen Methoden am leistungsfähigsten ist.

Ergebnisse über 100.000 Iterationen

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

Es wurden einige Punkte hinzugefügt, um den Unterschied tatsächlich zu "fühlen".

Generiert von diesem: https://3v4l.org/8Cog7

Fazit

Wenn Sie ein zu überprüfendes Objekt haben , verwenden Sie es instance ofwie in der akzeptierten Antwort angegeben.

Wenn Sie eine Klasse überprüfen müssen, verwenden Sie is_a.

Bonus

In dem Fall, dass Sie eine Klasse basierend auf einer Schnittstelle instanziieren möchten, die Sie benötigen, ist ihre Verwendung vorformender is_a. Es gibt nur eine Ausnahme - wenn der Konstruktor leer ist.

Beispiel: is_a(<className>, <interfaceName>, true);

Es wird zurückkehren bool. Mit dem dritten Parameter " allow_string " können Klassennamen überprüft werden, ohne die Klasse zu instanziieren.

Pilan
quelle