การประกาศวิธีการควรเข้ากันได้กับวิธีการหลักใน PHP


107
มาตรฐานที่เข้มงวด: การประกาศ childClass :: customMethod () ควรเข้ากันได้กับ parentClass :: customMethod ()

สาเหตุที่เป็นไปได้ของข้อผิดพลาดนี้ใน PHP คืออะไร? ฉันจะหาข้อมูลเกี่ยวกับความหมายของการใช้งานร่วมกันได้ที่ไหน


ไม่จิมมีมันถูกต้อง @ waiwai933 หากคุณสามารถโพสต์ส่วนหัว (เพียงบรรทัดแรก :) function customMethod( ... )สำหรับแต่ละฟังก์ชั่นเราสามารถบอกปัญหาที่เฉพาะเจาะจงได้
nickf

รายละเอียดเพิ่มเติมเกี่ยวกับข้อความแสดงข้อผิดพลาดและผลกระทบของเวลาในการคอมไพล์ PHP: bugs.php.net/bug.php?id=46851
hakre


1
ปัญหาของฉันคืออาร์กิวเมนต์เป็นuse Closure;คำใบ้ประเภท แต่ฉันไม่ได้เพิ่มไว้ที่ด้านบนสุดของชั้นเรียน (เนื่องจากคำใบ้ประเภทคือClosure) ดังนั้น ... อย่าลืมตรวจสอบว่าคุณขาดการอ้างอิงเช่นนั้นหรือไม่
Ryan

คำตอบ:


126

childClass::customMethod()มีข้อโต้แย้งที่แตกต่างกันหรือระดับการเข้าถึงที่แตกต่างกัน (รัฐ / ภาคเอกชน / ป้องกัน) parentClass::customMethod()กว่า


1
อาจเป็นเพราะการมองเห็นลายเซ็นของวิธีการไม่ใช่ปัญหาใน PHP
Gabriel Sosa

43
การมีค่าเริ่มต้นของอาร์กิวเมนต์ที่เหมือนกันก็มีความสำคัญเช่นกัน ตัวอย่างเช่นparentClass::customMethod($thing = false)และchildClass::customMethod($thing)จะทำให้เกิดข้อผิดพลาดเนื่องจากเมธอดของเด็กไม่ได้กำหนดค่าเริ่มต้นสำหรับอาร์กิวเมนต์แรก
Charles

1
ฉันเชื่อว่าการเปิดเผยเป็นข้อผิดพลาดที่แตกต่างกัน อย่างไรก็ตามที่ร้านของฉันเราไม่ได้ใช้โหมดเข้มงวดเพราะเหตุนี้ (เราใช้ E_ALL, IIRC)
davidtbernal

12
สิ่งนี้มีการเปลี่ยนแปลงใน PHP 5.4 btw: * ตอนนี้ E_ALL มีข้อผิดพลาดระดับ E_STRICT ในคำสั่งการกำหนดค่า error_reporting ดูที่นี่: php.net/manual/en/migration54.other.php
Duncan Lock

1
การไม่มีเครื่องหมายแอมเพอร์แซนด์ ( &) ในอาร์กิวเมนต์อาจทำให้เกิดข้อผิดพลาดนี้ได้เช่นกัน
IvanRF

36

ข้อความนี้หมายความว่ามีการเรียกวิธีการบางอย่างที่เป็นไปได้ซึ่งอาจล้มเหลวในขณะรันไทม์ สมมติว่าคุณมี

class A { public function foo($a = 1) {;}}
class B extends A { public function foo($a) {;}}
function bar(A $a) {$a->foo();}

คอมไพเลอร์ตรวจสอบเฉพาะการเรียก $ a-> foo () กับข้อกำหนดของ A :: foo () ซึ่งไม่ต้องใช้พารามิเตอร์ อย่างไรก็ตาม $ a อาจเป็นอ็อบเจ็กต์ของคลาส B ซึ่งต้องใช้พารามิเตอร์ดังนั้นการเรียกจะล้มเหลวเมื่อรันไทม์

อย่างไรก็ตามสิ่งนี้ไม่สามารถล้มเหลวและไม่ทำให้เกิดข้อผิดพลาด

class A { public function foo($a) {;}}
class B extends A { public function foo($a = 1) {;}}
function bar(A $a) {$a->foo();}

ดังนั้นไม่มีวิธีใดที่อาจมีพารามิเตอร์ที่ต้องการมากกว่าเมธอดหลัก

ข้อความเดียวกันนี้ยังถูกสร้างขึ้นเมื่อคำแนะนำประเภทไม่ตรงกัน แต่ในกรณีนี้ PHP มีข้อ จำกัด มากกว่า สิ่งนี้ทำให้เกิดข้อผิดพลาด:

class A { public function foo(StdClass $a) {;}}
class B extends A { public function foo($a) {;}}

เช่นนี้:

class A { public function foo($a) {;}}
class B extends A { public function foo(StdClass $a) {;}}

ดูเหมือนจะเข้มงวดกว่าที่ควรจะเป็นและฉันคิดว่าเกิดจากภายใน

ความแตกต่างในการมองเห็นทำให้เกิดข้อผิดพลาดที่แตกต่างกัน แต่ด้วยเหตุผลพื้นฐานเดียวกัน ไม่มีวิธีใดที่สามารถมองเห็นได้น้อยไปกว่าวิธีหลัก


2
ในตัวอย่างสุดท้ายของคุณ - ไม่ควรมีข้อผิดพลาดที่นี่เนื่องจากเป็น Legit, stdClass $ a มีข้อ จำกัด มากกว่า $ a แบบผสม มีวิธีแก้ปัญหานี้ไหม ฉันหมายถึงในกรณีนี้ PHP ควรอนุญาต แต่ก็ยังให้ข้อผิดพลาด ...
galchen

2
ตัวอย่างสุดท้ายของคุณคือประเภทที่ปลอดภัยดังนั้นจึงเป็น "ข้อ จำกัด มากกว่าที่จำเป็น" อย่างแน่นอน นี่อาจเป็นกรณีของการเขียนโปรแกรมลัทธิขนส่งสินค้าเนื่องจากขัดแย้งกับความหลากหลายในภาษา C ++ และ Java en.wikipedia.org/wiki/…
Warbo

ขอบคุณสำหรับคำอธิบายในกรณีของฉันตัวอย่างแรกที่คุณให้คือสิ่งที่ทำให้เกิดข้อผิดพลาดของฉัน
billynoah

ขอบคุณสำหรับสิ่งนี้ครับ
Eldoïr

22

หากคุณต้องการคงรูปแบบ OOP โดยไม่ปิดข้อผิดพลาดใด ๆ คุณยังสามารถ:

class A
{
    public function foo() {
        ;
    }
}
class B extends A
{
    /*instead of : 
    public function foo($a, $b, $c) {*/
    public function foo() {
        list($a, $b, $c) = func_get_args();
        // ...

    }
}

ฉันชอบที่จะใช้แฮ็คนี้เพื่อแก้ไขข้อผิดพลาดเหล่านี้ ฉันกังวลว่าแนวทางนี้อาจมีผลเสียหรือไม่ ฉันจะค้นคว้าข้อมูลนี้ แต่ถ้าคุณมีแหล่งข้อมูลที่จะช่วยตอบคำถามนั้นก็จะดีมาก
Adam Friedman

ขึ้นอยู่กับสถานการณ์ที่ฉันเดา ยังใช่มันอาจจะแฮ็คนิดหน่อย แต่มันเป็น php? บางครั้งมันก็เป็นวิธีที่ดีขอบคุณ! <@
Master James

คุณช่วยวันของฉัน! นี่เป็นทางเลือกเดียวในการเปิดโครงการ php5 แบบเดิมบนเซิร์ฟเวอร์ด้วย php7 โดยไม่ต้องเจ็บปวด
vladkras

สำหรับกรณีนี้คุณสามารถใช้ค่าเริ่มต้นแทนfunc_get_args()คือในB, เช่นนี้ไม่ได้ทำลายสัญญาสัญญาโดยpublic function foo($a = null, $b = null, $c = null) A
Jake

1

เพียงเพื่อขยายข้อผิดพลาดนี้ในบริบทของอินเทอร์เฟซหากคุณพิมพ์คำใบ้พารามิเตอร์ฟังก์ชันของคุณดังนี้:

อินเทอร์เฟซ A

use Bar;

interface A
{
    public function foo(Bar $b);
}

คลาส B

class B implements A
{
    public function foo(Bar $b);
}

หากคุณลืมใส่useคำสั่งในคลาสการใช้งานของคุณ (คลาส B) คุณจะได้รับข้อผิดพลาดนี้แม้ว่าพารามิเตอร์วิธีการจะเหมือนกันก็ตาม


0

ฉันประสบปัญหานี้ขณะพยายามขยายคลาสที่มีอยู่จาก GitHub ฉันจะพยายามอธิบายตัวเองก่อนเขียนชั้นเรียนอย่างที่ฉันควรจะเป็นแล้วก็ชั้นเรียนเหมือนตอนนี้

สิ่งที่ฉันคิด

namespace mycompany\CutreApi;

use mycompany\CutreApi\ClassOfVendor;

class CutreApi extends \vendor\AwesomeApi\AwesomeApi
{
   public function whatever(): ClassOfVendor
   {
        return new ClassOfVendor();
   }
}

สิ่งที่ฉันทำในที่สุด

namespace mycompany\CutreApi;

use \vendor\AwesomeApi\ClassOfVendor;

class CutreApi extends \vendor\AwesomeApi\AwesomeApi
{
   public function whatever(): ClassOfVendor
   {
        return new \mycompany\CutreApi\ClassOfVendor();
   }
}

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

ข้อเสียอย่างหนึ่งของ aproach ก่อนหน้านี้คือ IDE ไม่รู้จักวิธีการใหม่ที่ใช้ใน \ mycompany \ CutreApi \ ClassOfVendor () ดังนั้นสำหรับตอนนี้ฉันจะดำเนินการตามนี้

ทำอยู่ในปัจจุบัน

namespace mycompany\CutreApi;

use mycompany\CutreApi\ClassOfVendor;

class CutreApi extends \vendor\AwesomeApi\AwesomeApi
{
   public function getWhatever(): ClassOfVendor
   {
        return new ClassOfVendor();
   }
}

ดังนั้นแทนที่จะพยายามใช้เมธอด "อะไรก็ได้" ฉันเขียนวิธีใหม่ที่เรียกว่า "getWhatever" ในความเป็นจริงทั้งคู่ทำแบบเดียวกันเพียงแค่ส่งคืนคลาส แต่มีเนมสเปซที่แตกต่างกันตามที่ฉันได้อธิบายไว้ก่อนหน้านี้

หวังว่านี่จะช่วยใครบางคนได้

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