Vérifier si la classe d'une instance implémente une interface?

148

Étant donné une instance de classe, est-il possible de déterminer si elle implémente une interface particulière? Pour autant que je sache, il n'y a pas de fonction intégrée pour le faire directement. Quelles options ai-je (le cas échéant)?

Wilco
la source

Réponses:

258
interface IInterface
{
}

class TheClass implements IInterface
{
}

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

Vous pouvez utiliser l'opérateur "instanceof". Pour l'utiliser, l'opérande gauche est une instance de classe et l'opérande droit est une interface. Il renvoie true si l'objet implémente une interface particulière.

Tomáš Votruba
la source
102

Comme therefromhere souligne, vous pouvez utiliser class_implements(). Tout comme avec Reflection, cela vous permet de spécifier le nom de la classe sous forme de chaîne et ne nécessite pas d'instance de la classe:

interface IInterface
{
}

class TheClass implements IInterface
{
}

$interfaces = class_implements('TheClass');

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

class_implements() fait partie de l'extension SPL.

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

Des tests de performance

Quelques tests de performance simples montrent les coûts de chaque approche:

Étant donné une instance d'un objet

Construction d'objets en dehors de la boucle (100 000 itérations)
 ____________________________________________
| class_implements | Réflexion | instanceOf |
| ------------------ | ------------ | ------------ |
| 140 ms | 290 ms | 35 ms |
«--------------------------------------------»

Construction d'objets à l'intérieur de la boucle (100 000 itérations)
 ____________________________________________
| class_implements | Réflexion | instanceOf |
| ------------------ | ------------ | ------------ |
| 182 ms | 340 ms | 83 ms | Constructeur pas cher
| 431 ms | 607 ms | 338 ms | Constructeur cher
«--------------------------------------------»

Donné seulement un nom de classe

100 000 itérations
 ____________________________________________
| class_implements | Réflexion | instanceOf |
| ------------------ | ------------ | ------------ |
| 149 ms | 295 ms | N / A |
«--------------------------------------------»

Où le __construct () coûteux est:

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

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

Ces tests sont basés sur ce code simple .

Jess Telford
la source
56

nlaq souligne qu'il instanceofpeut être utilisé pour tester si l'objet est une instance d'une classe qui implémente une interface.

Mais instanceofne fait pas la distinction entre un type de classe et une interface. Vous ne savez pas si l'objet est une classe qui est appelée IInterface.

Vous pouvez également utiliser l'API de réflexion en PHP pour tester cela plus spécifiquement:

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

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

Bill Karwin
la source
2
Cela peut être utilisé sur les classes "statiques"
Znarkus
6
Voir aussiclass_implements()
John Carter
@therefromhere: Merci, bon conseil. Cela fait partie de l'extension SPL. Ma réponse a utilisé l'extension Reflection.
Bill Karwin
3
Si vous utilisez des espaces de noms, il n'y aura pas d'ambiguïté entre les interfaces et les classes du même nom et vous pourrez les réutiliser en toute sécurité instanceof.
grippe
+1 class_implements()car il est évidemment plus rapide d'appeler class_implements puis in_array, au lieu de faire une réflexion complète
Nickolaus
19

Juste pour aider les recherches futures, is_subclass_of est également une bonne variante (pour PHP 5.3.7+):

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

Vous pouvez également faire ce qui suit

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

Il lancera une erreur récupérable si le $objectSupposedToBeImplementingn'implémente pas l' YourInterfaceinterface.

Starx
la source
3

Mettre à jour

La is_a fonction manque ici comme alternative.

J'ai fait quelques tests de performance pour vérifier laquelle des méthodes indiquées est la plus performante.

Résultats sur 100 000 itérations

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

Ajout de quelques points pour réellement "sentir" voir la différence.

Généré par ceci: https://3v4l.org/8Cog7

Conclusion

Dans le cas où vous avez un objet à vérifier, utilisez instance ofcomme mentionné dans la réponse acceptée.

Si vous avez une classe à vérifier, utilisez is_a.

Prime

Étant donné le cas où vous souhaitez instancier une classe basée sur une interface dont vous avez besoin, il est plus préformant à utiliser is_a. Il n'y a qu'une seule exception - lorsque le constructeur est vide.

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

Il reviendra bool. Le troisième paramètre " allow_string " lui permet de vérifier les noms de classe sans instancier la classe.

Pilan
la source