Verifica se la classe di un'istanza implementa un'interfaccia?


148

Data un'istanza di classe, è possibile determinare se implementa una particolare interfaccia? Per quanto ne so, non esiste una funzione integrata per farlo direttamente. Quali opzioni ho (se ce ne sono)?

Risposte:


258
interface IInterface
{
}

class TheClass implements IInterface
{
}

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

È possibile utilizzare l'operatore "instanceof". Per usarlo, l'operando di sinistra è un'istanza di classe e l'operando di destra è un'interfaccia. Restituisce vero se l'oggetto implementa una particolare interfaccia.


102

Come therefromhere sottolinea, è possibile utilizzare class_implements(). Proprio come con Reflection, questo ti consente di specificare il nome della classe come stringa e non richiede un'istanza della classe:

interface IInterface
{
}

class TheClass implements IInterface
{
}

$interfaces = class_implements('TheClass');

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

class_implements() fa parte dell'estensione SPL.

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

Test delle prestazioni

Alcuni semplici test delle prestazioni mostrano i costi di ciascun approccio:

Data un'istanza di un oggetto

Costruzione di oggetti al di fuori del ciclo (100.000 iterazioni)
 ____________________________________________
| class_implements | Riflessione | instanceOf |
| ------------------ | ------------ | ------------ |
| 140 ms | 290 ms | 35 ms |
'--------------------------------------------'

Costruzione di oggetti all'interno del ciclo (100.000 iterazioni)
 ____________________________________________
| class_implements | Riflessione | instanceOf |
| ------------------ | ------------ | ------------ |
| 182 ms | 340 ms | 83 ms | Costruttore economico
| 431 ms | 607 ms | 338 ms | Costruttore costoso
'--------------------------------------------'

Dato solo un nome di classe

100.000 iterazioni
 ____________________________________________
| class_implements | Riflessione | instanceOf |
| ------------------ | ------------ | ------------ |
| 149 ms | 295 ms | N / A |
'--------------------------------------------'

Dove il costoso __construct () è:

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

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

Questi test si basano su questo semplice codice .


56

nlaq sottolinea che instanceofpuò essere utilizzato per verificare se l'oggetto è un'istanza di una classe che implementa un'interfaccia.

Ma instanceofnon distingue tra un tipo di classe e un'interfaccia. Non sai se l'oggetto è una classe che sembra essere chiamata IInterface.

Puoi anche utilizzare l'API di riflessione in PHP per testarlo in modo più specifico:

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

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


2
Questo può essere usato su classi "statiche"
Znarkus,


@therefromhere: Grazie, buon consiglio. Fa parte dell'estensione SPL. La mia risposta ha usato l'estensione Reflection.
Bill Karwin,

3
Se usi gli spazi dei nomi, non ci sarà alcuna ambiguità tra interfacce e classi con lo stesso nome e puoi tranquillamente instanceofriutilizzarli.
influenza

+1 per class_implements()poiché è ovviamente più veloce chiamare class_implements e poi in_array, invece di fare una riflessione completa
Nickolaus

19

Anche per aiutare le ricerche future is_subclass_of è anche una buona variante (per PHP 5.3.7+):

if (is_subclass_of($my_class_instance, 'ISomeInterfaceName')){
    echo 'I can do it!';
}

5

Puoi anche fare quanto segue

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

Emetterà un errore recuperabile se $objectSupposedToBeImplementingnon implementa l' YourInterfaceinterfaccia.


3

Aggiornare

La is_a funzione manca qui come alternativa.

Ho fatto alcuni test delle prestazioni per verificare quale dei modi indicati è il più performante.

Risultati oltre 100.000 iterazioni

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

Aggiunti alcuni punti per "sentire" realmente vedere la differenza.

Generato da questo: https://3v4l.org/8Cog7

Conclusione

Nel caso in cui tu abbia un oggetto da controllare, usa instance ofcome indicato nella risposta accettata.

Nel caso tu abbia una classe da controllare, usa is_a.

indennità

Dato il caso in cui si desidera creare un'istanza di una classe in base a un'interfaccia richiesta, è più preformante da utilizzare is_a. C'è solo un'eccezione: quando il costruttore è vuoto.

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

Tornerà bool. Il terzo parametro " allow_string " consente di controllare i nomi delle classi senza creare un'istanza della classe.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.