¿Verifica si la clase de una instancia implementa una interfaz?

148

Dada una instancia de clase, ¿es posible determinar si implementa una interfaz particular? Hasta donde sé, no hay una función integrada para hacer esto directamente. ¿Qué opciones tengo (si las hay)?

Wilco
fuente

Respuestas:

258
interface IInterface
{
}

class TheClass implements IInterface
{
}

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

Puede usar el operador "instanceof". Para usarlo, el operando izquierdo es una instancia de clase y el operando derecho es una interfaz. Devuelve verdadero si el objeto implementa una interfaz particular.

Tomáš Votruba
fuente
102

Como se señala a continuación, puede usar class_implements(). Al igual que con Reflection, esto le permite especificar el nombre de la clase como una cadena y no requiere una instancia de la clase:

interface IInterface
{
}

class TheClass implements IInterface
{
}

$interfaces = class_implements('TheClass');

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

class_implements() es parte de la extensión SPL.

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

Pruebas de rendimiento

Algunas pruebas de rendimiento simples muestran los costos de cada enfoque:

Dada una instancia de un objeto

Construcción de objetos fuera del ciclo (100,000 iteraciones)
 ____________________________________________
El | implementaciones de clase | Reflexión | instanceOf |
| ------------------ | ------------ | ------------ |
El | 140 ms | 290 ms | 35 ms |
'--------------------------------------------'

Construcción de objetos dentro del bucle (100,000 iteraciones)
 ____________________________________________
El | implementaciones de clase | Reflexión | instanceOf |
| ------------------ | ------------ | ------------ |
El | 182 ms | 340 ms | 83 ms | Constructor barato
El | 431 ms | 607 ms | 338 ms | Constructor caro
'--------------------------------------------'

Dado solo un nombre de clase

100,000 iteraciones
 ____________________________________________
El | implementaciones de clase | Reflexión | instanceOf |
| ------------------ | ------------ | ------------ |
El | 149 ms | 295 ms | N / A
'--------------------------------------------'

Donde el costoso __construct () es:

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

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

Estas pruebas se basan en este código simple .

Jess Telford
fuente
56

nlaq señala que instanceofse puede usar para probar si el objeto es una instancia de una clase que implementa una interfaz.

Pero instanceofno distingue entre un tipo de clase y una interfaz. No sabe si el objeto es una clase a la que se llama IInterface.

También puede usar la API de reflexión en PHP para probar esto más específicamente:

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

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

Bill Karwin
fuente
2
Esto se puede usar en clases "estáticas"
Znarkus
66
Ver tambiénclass_implements()
John Carter
@therefromhere: Gracias, buen consejo. Eso es parte de la extensión SPL. Mi respuesta usó la extensión Reflection.
Bill Karwin
3
Si usa espacios de nombres, entonces no habrá una ambigüedad entre las interfaces y las clases con el mismo nombre y puede instanceofvolver a usar de forma segura .
gripe
+1 class_implements()porque es obviamente más rápido llamar a class_implements y luego in_array, en lugar de hacer una reflexión completa
Nickolaus
19

Solo para ayudar a futuras búsquedas is_subclass_of también es una buena variante (para PHP 5.3.7+):

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

También puedes hacer lo siguiente

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

Lanzará un error recuperable si $objectSupposedToBeImplementingno implementa la YourInterfaceinterfaz.

Starx
fuente
3

Actualizar

La is_a función falta aquí como alternativa.

Hice algunas pruebas de rendimiento para verificar cuál de las formas indicadas es la más eficiente.

Resultados de más de 100k iteraciones

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

Se agregaron algunos puntos para realmente "sentir" ver la diferencia.

Generado por esto: https://3v4l.org/8Cog7

Conclusión

En caso de que tenga un objeto para verificar, use instance ofcomo se menciona en la respuesta aceptada.

En caso de que tenga una clase para verificar, use is_a.

Prima

Dado el caso en el que desea crear una instancia de una clase basada en una interfaz que necesita, es más preformante de usar is_a. Solo hay una excepción: cuando el constructor está vacío.

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

Se volverá bool. El tercer parámetro " allow_string " le permite verificar los nombres de clase sin crear instancias de la clase.

Pilan
fuente