Memeriksa apakah kelas instance mengimplementasikan antarmuka?

148

Diberikan instance kelas, apakah mungkin untuk menentukan apakah mengimplementasikan antarmuka tertentu? Sejauh yang saya tahu, tidak ada fungsi bawaan untuk melakukan ini secara langsung. Opsi apa yang saya miliki (jika ada)?

Wilco
sumber

Jawaban:

258
interface IInterface
{
}

class TheClass implements IInterface
{
}

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

Anda dapat menggunakan operator "instanceof". Untuk menggunakannya, operan kiri adalah instance kelas dan operan kanan adalah antarmuka. Ini mengembalikan true jika objek mengimplementasikan antarmuka tertentu.

Tomáš Votruba
sumber
102

Sebagai therefromhere menunjukkan, Anda dapat menggunakan class_implements(). Seperti halnya Refleksi, ini memungkinkan Anda untuk menentukan nama kelas sebagai string dan tidak memerlukan turunan dari kelas:

interface IInterface
{
}

class TheClass implements IInterface
{
}

$interfaces = class_implements('TheClass');

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

class_implements() adalah bagian dari ekstensi SPL.

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

Tes Kinerja

Beberapa tes kinerja sederhana menunjukkan biaya dari setiap pendekatan:

Diberi contoh objek

Konstruksi objek di luar loop (100.000 iterasi)
 ____________________________________________
| class_implements | Refleksi | instanceOf |
| ------------------ | ------------ | ------------ |
| 140 ms | 290 ms | 35 ms |
'--------------------------------------------'

Konstruksi objek di dalam loop (100.000 iterasi)
 ____________________________________________
| class_implements | Refleksi | instanceOf |
| ------------------ | ------------ | ------------ |
| 182 ms | 340 ms | 83 ms | Pembuat murah
| 431 ms | 607 ms | 338 ms | Konstruktor mahal
'--------------------------------------------'

Hanya diberi nama kelas

100.000 iterasi
 ____________________________________________
| class_implements | Refleksi | instanceOf |
| ------------------ | ------------ | ------------ |
| 149 ms | 295 ms | T / A |
'--------------------------------------------'

Di mana __construct () yang mahal adalah:

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

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

Tes-tes ini didasarkan pada kode sederhana ini .

Jess Telford
sumber
56

nlaq menunjukkan bahwa instanceofdapat digunakan untuk menguji apakah objek adalah turunan dari kelas yang mengimplementasikan antarmuka.

Tetapi instanceoftidak membedakan antara tipe kelas dan antarmuka. Anda tidak tahu apakah objek adalah kelas yang kebetulan dipanggil IInterface.

Anda juga dapat menggunakan API refleksi dalam PHP untuk menguji ini lebih spesifik:

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

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

Bill Karwin
sumber
2
Ini dapat digunakan pada kelas "statis"
Znarkus
6
Lihat jugaclass_implements()
John Carter
@therefromhere: Terima kasih, tip yang bagus. Itu bagian dari ekstensi SPL. Jawaban saya menggunakan ekstensi Refleksi.
Bill Karwin
3
Jika Anda menggunakan ruang nama daripada tidak akan ada ambiguitas antara antarmuka dan kelas dengan nama yang sama dan Anda dapat menggunakan instanceoflagi dengan aman .
Flu
1 untuk class_implements()karena jelas lebih cepat untuk memanggil class_implements dan kemudian in_array, daripada membuat refleksi lengkap
Nickolaus
19

Hanya untuk membantu pencarian di masa mendatang is_subclass_of juga merupakan varian yang baik (untuk PHP 5.3.7+):

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

Anda juga dapat melakukan hal berikut

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

Ini akan membuang kesalahan yang dapat dipulihkan jika $objectSupposedToBeImplementingtidak mengimplementasikan YourInterfaceAntarmuka.

Starx
sumber
3

Memperbarui

The is_a Fungsi yang hilang di sini sebagai alternatif.

Saya melakukan beberapa tes kinerja untuk memeriksa mana dari cara yang dinyatakan adalah yang paling performan.

Hasil iterasi lebih dari 100rb

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

Menambahkan beberapa titik untuk benar-benar "merasakan" melihat perbedaannya.

Dihasilkan oleh ini: https://3v4l.org/8Cog7

Kesimpulan

Jika Anda memiliki objek untuk diperiksa, gunakan instance ofseperti yang disebutkan dalam jawaban yang diterima.

Jika Anda memiliki kelas untuk diperiksa, gunakan is_a.

Bonus

Mengingat kasus yang Anda inginkan untuk instantiate kelas berdasarkan pada antarmuka yang Anda inginkan, itu lebih preformant untuk digunakan is_a. Hanya ada satu pengecualian - ketika konstruktor kosong.

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

Itu akan kembali bool. Parameter ketiga " allow_string " memungkinkannya untuk memeriksa nama kelas tanpa membuat instance kelas.

Pilan
sumber