Verificando se a classe de uma instância implementa uma interface?

148

Dada uma instância de classe, é possível determinar se ela implementa uma interface específica? Até onde eu sei, não há uma função interna para fazer isso diretamente. Que opções eu tenho (se houver)?

Wilco
fonte

Respostas:

258
interface IInterface
{
}

class TheClass implements IInterface
{
}

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

Você pode usar o operador "instanceof". Para usá-lo, o operando esquerdo é uma instância de classe e o operando direito é uma interface. Retorna true se o objeto implementa uma interface específica.

Tomáš Votruba
fonte
102

Como a partir daí , você pode usar class_implements(). Assim como no Reflection, isso permite que você especifique o nome da classe como uma string e não requer uma instância da classe:

interface IInterface
{
}

class TheClass implements IInterface
{
}

$interfaces = class_implements('TheClass');

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

class_implements() faz parte da extensão SPL.

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

Testes de performance

Alguns testes simples de desempenho mostram os custos de cada abordagem:

Dada uma instância de um objeto

Construção de objetos fora do loop (100.000 iterações)
 ____________________________________________
| class_implements | Reflexão instanceOf |
| ------------------ | ------------ | ------------ |
| 140 ms | 290 ms | 35 ms |
'--------------------------------------------'

Construção de objetos dentro do loop (100.000 iterações)
 ____________________________________________
| class_implements | Reflexão instanceOf |
| ------------------ | ------------ | ------------ |
| 182 ms | 340 ms | 83 ms | Construtor Barato
| 431 ms | 607 ms | 338 ms | Construtor caro
'--------------------------------------------'

Dado apenas um nome de classe

100.000 iterações
 ____________________________________________
| class_implements | Reflexão instanceOf |
| ------------------ | ------------ | ------------ |
| 149 ms | 295 ms | N / a
'--------------------------------------------'

Onde o __construct () caro é:

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

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

Esses testes são baseados neste código simples .

Jess Telford
fonte
56

O nlaq aponta que instanceofpode ser usado para testar se o objeto é uma instância de uma classe que implementa uma interface.

Mas instanceofnão faz distinção entre um tipo de classe e uma interface. Você não sabe se o objeto é uma classe que é chamada IInterface.

Você também pode usar a API de reflexão no PHP para testar isso mais especificamente:

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

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

Bill Karwin
fonte
2
Isso pode ser usado em classes "estáticos"
Znarkus
6
Veja tambémclass_implements()
John Carter
@therefromhere: Obrigado, boa dica. Isso faz parte da extensão SPL. Minha resposta usou a extensão Reflexão.
Bill Karwin
3
Se você usar namespaces, não haverá ambiguidade entre interfaces e classes com o mesmo nome e poderá usá-lo com segurança instanceofnovamente.
gripe
+1 para class_implements()desde a sua obviamente mais rápido para chamar class_implements e depois in_array, em vez de fazer uma reflexão completa
Nickolaus
19

Apenas para ajudar futuras pesquisas, o is_subclass_of também é uma boa variante (para PHP 5.3.7+):

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

Você também pode fazer o seguinte

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

Irá gerar um erro recuperável se o $objectSupposedToBeImplementingnão implementar a YourInterfaceinterface.

Starx
fonte
3

Atualizar

A is_a função está faltando aqui como alternativa.

Fiz alguns testes de desempenho para verificar qual das maneiras declaradas é a mais eficiente.

Resultados com mais de 100 mil iterações

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

Foram adicionados alguns pontos para realmente "sentir" ver a diferença.

Gerado por isso: https://3v4l.org/8Cog7

Conclusão

Caso você tenha um objeto para verificar, use instance ofcomo mencionado na resposta aceita.

Caso você tenha uma turma para verificar, use is_a.

Bônus

Dado o caso em que você deseja instanciar uma classe com base em uma interface que você precisa, é mais recomendável usar is_a. Há apenas uma exceção - quando o construtor está vazio.

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

Voltará bool. O terceiro parâmetro " allow_string " permite verificar os nomes das classes sem instanciar a classe.

Pilan
fonte