PHP 7 อินเทอร์เฟซการบอกใบ้ประเภทส่งคืนและตัวเอง


89

อัปเดต : ขณะนี้ PHP 7.4 รองรับความแปรปรวนร่วมและความแตกต่างซึ่งแก้ไขปัญหาสำคัญที่เกิดขึ้นในคำถามนี้


ฉันพบปัญหาบางอย่างกับการใช้คำใบ้ประเภทการส่งคืนใน PHP 7 ความเข้าใจของฉันคือการบอกใบ้: selfหมายความว่าคุณตั้งใจให้คลาสการใช้งานกลับมาเอง ดังนั้นฉันจึงใช้: selfอินเทอร์เฟซเพื่อระบุสิ่งนั้น แต่เมื่อฉันพยายามใช้อินเทอร์เฟซจริงฉันพบข้อผิดพลาดเกี่ยวกับความเข้ากันได้

ต่อไปนี้เป็นการสาธิตง่ายๆเกี่ยวกับปัญหาที่ฉันพบ:

interface iFoo
{
    public function bar (string $baz) : self;
}

class Foo implements iFoo
{

    public function bar (string $baz) : self
    {
        echo $baz . PHP_EOL;
        return $this;
    }
}

(new Foo ()) -> bar ("Fred") 
    -> bar ("Wilma") 
    -> bar ("Barney") 
    -> bar ("Betty");

ผลลัพธ์ที่คาดหวังคือ:

เฟรดวิลมาบาร์นีย์เบ็ตตี้

สิ่งที่ฉันได้รับคือ:

PHP Fatal error: Declaration of Foo :: bar (int $ baz): Foo ต้องเข้ากันได้กับ iFoo :: bar (int $ baz): iFoo ใน test.php ในบรรทัดที่ 7

สิ่งนี้คือ Foo คือการใช้งาน iFoo ดังนั้นเท่าที่ฉันสามารถบอกได้ว่าการใช้งานควรเข้ากันได้ดีกับอินเทอร์เฟซที่กำหนด ฉันสามารถแก้ไขปัญหานี้ได้โดยการเปลี่ยนอินเทอร์เฟซหรือคลาสการใช้งาน (หรือทั้งสองอย่าง) เพื่อส่งคืนคำใบ้อินเทอร์เฟซตามชื่อแทนที่จะใช้selfแต่ความเข้าใจของฉันคือความหมายหมายselfถึง "ส่งคืนอินสแตนซ์ของคลาสที่คุณเพิ่งเรียกเมธอดบน ". ดังนั้นการเปลี่ยนเป็นอินเทอร์เฟซจะหมายถึงในทางทฤษฎีว่าฉันสามารถส่งคืนอินสแตนซ์ของบางสิ่งที่ใช้อินเทอร์เฟซได้เมื่อเจตนาของฉันคืออินสแตนซ์ที่เรียกใช้คือสิ่งที่จะถูกส่งกลับ

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


ฉันคิดว่ามันเป็นข้อผิดพลาดใน PHP ผลตอบแทนประเภทเค้าบางทีคุณควรจะยกมันเป็นข้อผิดพลาด ; แต่การแก้ไขใด ๆ ไม่น่าจะเข้าสู่ PHP 7.1 ในช่วงปลายนี้
Mark Baker

เนื่องจากเวอร์ชันเบต้าล่าสุดของ 7.1 ได้ออนไลน์เมื่อไม่กี่วันที่ผ่านมาจึงไม่น่าเป็นไปได้มากที่การแก้ไขใด ๆ จะเข้าสู่ 7.1
Charlotte Dunois

ไม่สนใจคุณกำลังอ่านการตีความของคุณว่าselfประเภทผลตอบแทนควรจะทำงานอย่างไร
Adam Cameron

@Adam: ดูเหมือนว่าจะมีเหตุผลที่selfจะหมายถึง "ส่งคืนอินสแตนซ์ที่คุณเรียกใช้สิ่งนี้ไม่ใช่อินสแตนซ์อื่น ๆ ที่ใช้อินเทอร์เฟซเดียวกัน" ฉันดูเหมือนจะจำได้ว่า Java มีประเภทการส่งคืนที่คล้ายกัน (แม้ว่าฉันจะเขียนโปรแกรม Java มาสักพักแล้วก็ตาม)
GordonM

1
สวัสดี Gordon เว้นแต่จะได้รับการบันทึกไว้ว่าจะทำงานที่ไหนสักแห่งฉันจะไม่นับสิ่งที่อาจเป็นตรรกะที่เป็นจริง TBH กับสถานการณ์ที่ฉันอธิบายฉันแค่เปิดเผยให้มากที่สุดและใช้ iFoo เป็นประเภทการส่งคืน มีสถานการณ์ที่ใช้ไม่ได้จริงหรือไม่? (ฉันตระหนักดีว่านี่เป็น "คำแนะนำ" / ความคิดเห็นมากกว่า "คำตอบ" ที่กล่าว
Adam Cameron

คำตอบ:


94

selfไม่ได้อ้างถึงอินสแตนซ์ แต่หมายถึงคลาสปัจจุบัน ไม่มีวิธีใดที่อินเทอร์เฟซจะระบุว่าต้องส่งคืนอินสแตนซ์เดียวกัน- การใช้selfในลักษณะที่คุณกำลังพยายามจะบังคับให้อินสแตนซ์ที่ส่งคืนเป็นคลาสเดียวกันเท่านั้น

ที่กล่าวว่าการประกาศประเภทการส่งคืนใน PHP จะต้องไม่แปรผันในขณะที่สิ่งที่คุณพยายามทำนั้นเป็นความแปรปรวน

การใช้งานของคุณselfเทียบเท่ากับ:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : Foo  {...}
}

ซึ่งไม่ได้รับอนุญาต


ประเภทกลับประกาศ RFCมีนี้จะพูดถึง :

การบังคับใช้ประเภทการส่งคืนที่ประกาศระหว่างการสืบทอดจะไม่เปลี่ยนแปลง ซึ่งหมายความว่าเมื่อชนิดย่อยแทนที่เมธอดพาเรนต์ประเภทการส่งคืนของเด็กจะต้องตรงกับพาเรนต์ทุกประการและไม่สามารถละเว้นได้ หากผู้ปกครองไม่ได้ประกาศประเภทการส่งคืนเด็กจะได้รับอนุญาตให้ประกาศประเภทหนึ่ง

...

เดิม RFC นี้เสนอประเภทการส่งคืนโควาเรียน แต่เปลี่ยนเป็นค่าคงที่เนื่องจากปัญหาบางประการ เป็นไปได้ที่จะเพิ่มประเภทการส่งคืนโควาเรียในบางจุดในอนาคต


ในขณะที่อย่างน้อยที่สุดที่คุณสามารถทำได้คือ:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : iFoo  {...}
}

19
ที่กล่าวว่าฉันคาดหวังว่าคำใบ้ประเภทผลตอบแทนstaticจะได้ผล แต่ก็ไม่เป็นที่รู้จัก
Mark Baker

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

19
พอลการลบความคิดเห็นที่คุณลบในที่นี้ถือเป็นอันตรายจริง ๆ เพราะ (A) มันสูญเสียข้อมูลสำคัญและ (B) มันขัดขวางการสนทนาที่สัมพันธ์กับความคิดเห็นอื่น ๆ ฉันไม่เห็นเหตุผลใด ๆ ว่าทำไมความคิดเห็นของคุณที่ต้องใช้ Mark และ Gordon จึงจำเป็นต้องถูกลบ ในความเป็นจริงคุณกำลังทำสิ่งนี้ทั่วทุกที่และจำเป็นต้องหยุด ไม่มีเหตุผลที่ดีอย่างแน่นอนที่จะย้อนกลับไปที่คำถามเก่า ๆ หนึ่งปีและลบความคิดเห็นทั้งหมดของคุณซึ่งจะทำลายการสนทนาอย่างสิ้นเชิง ในความเป็นจริงมันเป็นอันตรายและก่อกวน
โคดี้เกรย์

มีคำนำที่สำคัญสำหรับส่วนหนึ่งของคำพูดของคุณที่เห็นที่นี่: " ประเภทการส่งคืน Covariant ถือเป็นประเภทเสียงและใช้ในภาษาอื่น ๆ อีกมากมาย (C ++ และ Java แต่ไม่ใช่ C # ฉันเชื่อ) RFC นี้เดิมเสนอประเภทการส่งคืนโควาเรียน แต่ ถูกเปลี่ยนเป็นค่าคงที่เนื่องจากมีปัญหาบางประการ ". ฉันสงสัยว่า PHP มีปัญหาอะไร ทางเลือกในการออกแบบของพวกเขาทำให้เกิดข้อ จำกัด หลายประการซึ่งทำให้เกิดปัญหาแปลก ๆ เกี่ยวกับลักษณะการแสดงผลค่อนข้างไร้ประโยชน์ในหลาย ๆ กรณีเว้นแต่คุณจะคลายข้อ จำกัด ประเภทการส่งคืน (ดังที่เห็นในคำตอบด้านล่าง) น่าผิดหวังมาก
John Pancoast

1
@MarkBaker ประเภทผลตอบแทนstaticจะถูกเพิ่มไปยัง PHP 8
Ricardo Boss

16

นอกจากนี้ยังสามารถเป็นวิธีแก้ปัญหาที่คุณไม่ได้กำหนดประเภทการส่งคืนอย่างชัดเจนในอินเทอร์เฟซเฉพาะใน PHPDoc จากนั้นคุณสามารถกำหนดประเภทผลตอบแทนที่แน่นอนในการใช้งาน:

interface iFoo
{
    public function bar (string $baz);
}

class Foo implements iFoo
{
    public function bar (string $baz) : Foo  {...}
}

2
หรือแทนการใช้เพียงFoo self
แทน

2

ในกรณีที่คุณต้องการบังคับจากอินเทอร์เฟซเมธอดนั้นจะส่งคืนอ็อบเจ็กต์ แต่ประเภทของอ็อบเจ็กต์จะไม่ใช่ประเภทของอินเทอร์เฟซ แต่เป็นคลาสนั้นเองคุณสามารถเขียนได้ด้วยวิธีนี้:

interface iFoo {
    public function bar (string $baz) : object;
}

class Foo implements iFoo {
    public function bar (string $baz) : self  {...}
}

ใช้งานได้ตั้งแต่ PHP 7.4



0

นี่ดูเหมือนพฤติกรรมที่คาดหวังสำหรับฉัน

เพียงแค่เปลี่ยนFoo::barวิธีการของคุณเพื่อส่งคืนiFooแทนselfและดำเนินการกับมัน

คำอธิบาย:

selfตามที่ใช้ในอินเทอร์เฟซหมายถึง "วัตถุประเภทiFoo"
selfตามที่ใช้ในการนำไปใช้หมายถึง "วัตถุประเภทFooหนึ่ง"

ดังนั้นประเภทผลตอบแทนในอินเทอร์เฟซและการนำไปใช้งานจึงไม่เหมือนกันอย่างชัดเจน

ความคิดเห็นหนึ่งกล่าวถึง Java และคุณจะมีปัญหานี้หรือไม่ คำตอบคือใช่คุณจะมีปัญหาเดียวกันหาก Java อนุญาตให้คุณเขียนโค้ดแบบนั้นซึ่งไม่ได้ เนื่องจาก Java ต้องการให้คุณใช้ชื่อประเภทแทนselfทางลัดของ PHP คุณจะไม่เห็นสิ่งนี้จริง (ดูที่นี่สำหรับการอภิปรายเกี่ยวกับปัญหาที่คล้ายกันใน Java)


การประกาศselfก็คล้ายกับการประกาศ MyClass::class?
peterchaula

1
@ เลเซอร์ใช่ค่ะ
Moshe Katz

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