我像這樣多載query了類的方法mysqli:
class MySql extends \mysqli
{
function query(string $sql): ?MySqlResult // line #30
{
$result = parent::query($sql);
return new MySqlResult($result);
}
}
在 PHP8.0 中這不是問題。但是,從 PHP8.1 開始,我現在收到此錯誤:
已棄用:回傳型別 of
Repository\MySql\MySql::query($sql, $resultmode = null)應與 兼容mysqli::query(string $query, int $result_mode = MYSQLI_STORE_RESULT): mysqli_result|bool,或者#[\ReturnTypeWillChange]應使用該屬性暫時抑制第repository\src\MySql\MySql.php30 行中的通知
我知道如何修復錯誤——我可能最終會更改方法的名稱,因為我想回傳一個我自己的自定義物件。
題
我正在尋找一個從理論和面向物件的角度捕捉這種變化需求的答案,可能使用語言理論,或者將其與其他語言進行比較。
為什么這個改變是必要的?進行此更改的需要或原因是什么?擴展類時,有什么方法可以在 PHP 中允許多載回傳型別?
uj5u.com熱心網友回復:
為了使語言保持一致,這種改變是必要的。如果被覆寫的方法可以回傳不同的型別,則會產生非常混亂的代碼。這基本上意味著被覆寫的函式做了一些完全不同的事情。這種行為在 PHP 中是不允許的。像這樣的代碼總是會拋出:
class A {
public function foo():string {
return '';
}
}
class B extends A {
public function foo():int {
return 1;
}
}
唯一的問題是標準類沒有在內部指定回傳型別。由于回傳資源、混合、聯合型別等,許多方法無法指定型別。這意味著它們實際上沒有回傳型別。PHP 規則說,如果被覆寫的方法沒有回傳型別,則子方法可以指定(縮小)型別:
class A {
public function foo() { // this could also be :mixed but that was only introduced in PHP 8
return '';
}
}
class B extends A {
public function foo():int {
return 1;
}
}
所以,你問錯問題了。問題不在于為什么自 PHP 8.1 以來不能覆寫回傳型別,因為總是如此,而是為什么 PHP 內部類沒有指定回傳型別。
自 PHP 8.1 起,宣告大多數回傳型別成為可能。但是,由于這會導致重大更改,與通常會產生的致命錯誤相比,內部方法暫時僅拋出棄用訊息。在 PHP 9.0 中,所有這些都將得到修復。
對于您的特定情況,您應該使用組合而不是繼承。大多數時候應該避免繼承,尤其是內部類。組合提供了更大的靈活性并且更容易測驗。
uj5u.com熱心網友回復:
理解這一點的方法是將函式簽名視為合約。在內置mysqli類中,我們有以下簽名:
public function query(string $query, int $result_mode = MYSQLI_STORE_RESULT): mysqli_result|bool
我們可以將其翻譯成英文,如下所示:
- 如果我有一個實體
mysqli... - ...我可以呼叫
query它的方法(public)... - ... 以 a
string作為第一個引數 ... - ...以及可選的
int作為第二個引數... - ...我將回傳一個
mysqli_result物件或布林值
因此,以下代碼由合約保證成功運行:
assert($foo instanceof \mysqli);
$result = $foo->query('Select 1', MYSQLI_USE_RESULT);
assert($result instanceof mysqli_result || is_bool($result));
現在讓我們使用您提議的類的實體運行該代碼:
assert($foo instanceof \mysqli);
// Success: `MySql` is a sub-type of `\mysqli`
$result = $foo->query('Select 1', MYSQLI_USE_RESULT);
// Success, but second argument ignored
assert($result instanceof mysqli_result || is_bool($result));
// Failure! Function may return null, which doesn't meet this assertion
// If the custom MysqlResult doesn't extend mysqli_result, that will also fail
因此,如您所見,您的類不符合內置類的約定。
從邏輯上講,這始終是一個錯誤,因為您“在精神上”違反了合同,但是直到最近 PHP 才有可能強制執行此操作。這就是為什么它目前不是一個硬錯誤,因此您有機會修復“僥幸逃脫”的舊代碼。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/429031.html
