ตรวจสอบว่าคลาสของอินสแตนซ์ใช้อินเตอร์เฟสหรือไม่?


148

รับอินสแตนซ์ของคลาสเป็นไปได้ไหมที่จะพิจารณาว่าใช้อินเทอร์เฟซเฉพาะหรือไม่ เท่าที่ฉันรู้ไม่มีฟังก์ชั่นในตัวที่จะทำสิ่งนี้โดยตรง ฉันมีตัวเลือกอะไรบ้าง (ถ้ามี)

คำตอบ:


258
interface IInterface
{
}

class TheClass implements IInterface
{
}

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

คุณสามารถใช้โอเปอเรเตอร์ "instanceof" ในการใช้งานตัวถูกดำเนินการด้านซ้ายเป็นอินสแตนซ์ของคลาสและตัวถูกดำเนินการด้านขวาเป็นอินเทอร์เฟซ มันจะส่งกลับจริงถ้าวัตถุใช้อินเตอร์เฟซที่เฉพาะเจาะจง


102

ในฐานะที่เป็นtherefromhereclass_implements()ชี้ให้คุณสามารถใช้ เช่นเดียวกับ Reflection สิ่งนี้อนุญาตให้คุณระบุชื่อคลาสเป็นสตริงและไม่ต้องการอินสแตนซ์ของคลาส:

interface IInterface
{
}

class TheClass implements IInterface
{
}

$interfaces = class_implements('TheClass');

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

class_implements() เป็นส่วนหนึ่งของส่วนขยาย SPL

ดู: http://php.net/manual/th/function.class-implements.php

การทดสอบประสิทธิภาพ

การทดสอบประสิทธิภาพอย่างง่าย ๆ แสดงค่าใช้จ่ายของแต่ละวิธี:

รับตัวอย่างของวัตถุ

การสร้างวัตถุนอกลูป (100,000 ซ้ำ)
 ____________________________________________
| class_implements | ภาพสะท้อน instanceOf |
| ------------------ | ------------ | ------------ |
| 140 ms | 290 ms | 35 ms |
'--------------------------------------------'

การสร้างวัตถุภายในลูป (100,000 ซ้ำ)
 ____________________________________________
| class_implements | ภาพสะท้อน instanceOf |
| ------------------ | ------------ | ------------ |
| 182 ms | 340 ms 83 ms | ตัวสร้างราคาถูก
| 431 ms | 607 ms | 338 ms | ตัวสร้างแพง
'--------------------------------------------'

ให้ชื่อคลาสเท่านั้น

100,000 ซ้ำ
 ____________________________________________
| class_implements | ภาพสะท้อน instanceOf |
| ------------------ | ------------ | ------------ |
| 149 ms | 295 ms | N / A |
'--------------------------------------------'

ที่ __ โครงสร้าง () ราคาแพงคือ:

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

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

การทดสอบเหล่านี้ใช้รหัสง่าย ๆนี้


56

nlaq ชี้ให้เห็นว่าinstanceofสามารถใช้เพื่อทดสอบว่าวัตถุนั้นเป็นอินสแตนซ์ของคลาสที่ใช้อินเทอร์เฟซ

แต่instanceofไม่แยกความแตกต่างระหว่างประเภทคลาสและส่วนต่อประสาน คุณไม่รู้ว่าวัตถุนั้นเป็นคลาสที่เกิดขึ้นถูกเรียกIInterfaceหรือไม่

คุณยังสามารถใช้การสะท้อน API ใน PHP เพื่อทดสอบสิ่งนี้โดยเฉพาะ:

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

ดูhttp://php.net/manual/th/book.reflection.php


2
นี้สามารถนำไปใช้กับ "คงที่" เรียน
Znarkus

6
ดูเพิ่มเติมclass_implements()
John Carter

@therromromhere: ขอบคุณเคล็ดลับดี นั่นเป็นส่วนหนึ่งของส่วนขยาย SPL คำตอบของฉันใช้ส่วนขยายการสะท้อน
Bill Karwin

3
หากคุณใช้เนมสเปซที่มากกว่าจะไม่มีความกำกวมระหว่างอินเทอร์เฟซและคลาสที่มีชื่อเดียวกันและคุณสามารถใช้instanceofอีกครั้งได้อย่างปลอดภัย
ไข้หวัดใหญ่

+1 สำหรับclass_implements()เนื่องจากเห็นได้ชัดว่าเร็วกว่าที่จะเรียก class_implements จากนั้น in_array แทนที่จะทำการสะท้อนที่สมบูรณ์
Nickolaus

19

เพียงเพื่อช่วยให้การค้นหาในอนาคตis_subclass_ofเป็นตัวแปรที่ดี (สำหรับ PHP 5.3.7+):

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

5

คุณยังสามารถทำสิ่งต่อไปนี้

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

มันจะโยนข้อผิดพลาดที่กู้คืนได้หาก$objectSupposedToBeImplementingไม่ได้ใช้YourInterfaceอินเทอร์เฟซ


3

ปรับปรุง

is_a ฟังก์ชั่นที่ขาดหายไปที่นี่เป็นทางเลือก

ฉันทำการทดสอบประสิทธิภาพเพื่อตรวจสอบว่าวิธีใดที่มีประสิทธิภาพมากที่สุด

ผลลัพธ์มากกว่า 100k การวนซ้ำ

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

เพิ่มจุดบางจุดเพื่อ "รู้สึก" จริง ๆ เห็นความแตกต่าง

สร้างโดยสิ่งนี้: https://3v4l.org/8Cog7

ข้อสรุป

ในกรณีที่คุณมีวัตถุที่จะตรวจสอบการใช้งานinstance ofดังกล่าวในคำตอบที่ได้รับการยอมรับ

ในกรณีที่คุณมีระดับis_aการตรวจสอบการใช้งาน

โบนัส

ได้รับกรณีที่คุณต้องการที่จะยกตัวอย่างชั้นเรียนขึ้นอยู่กับอินเตอร์เฟซที่คุณต้องการที่จะมีก็เป็น preformant is_aมากขึ้นในการใช้งาน มีข้อยกเว้นเพียงข้อเดียวเท่านั้น - เมื่อตัวสร้างว่างเปล่า

ตัวอย่าง: is_a(<className>, <interfaceName>, true);

boolมันจะกลับมา พารามิเตอร์ที่สาม " allow_string " อนุญาตให้ตรวจสอบชื่อคลาสโดยไม่สร้างอินสแตนซ์ของคลาส

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.