1

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 の時点で、次のエラーが発生しています。

非推奨: の戻り値の型はRepository\MySql\MySql::query($sql, $resultmode = null)と互換性があるmysqli::query(string $query, int $result_mode = MYSQLI_STORE_RESULT): mysqli_result|boolか、30 行目#[\ReturnTypeWillChange]の通知を一時的に抑制するために属性を使用する必要がありますrepository\src\MySql\MySql.php

エラーを修正する方法はわかっています。独自のカスタム オブジェクトを返したいので、おそらくメソッドの名前を変更することになるでしょう。

質問

おそらく言語理論を使用したり、他の言語と比較したりして、理論的およびオブジェクト指向の観点からこの変更の必要性を捉えた答えを探しています。

なぜこの変更が必要だったのですか?この変更を行う必要性または理由は何でしたか? クラスを拡張するときにPHPでオーバーロードされた戻り値の型を許可する方法は何ですか?

4

2 に答える 2

3

これを理解する方法は、関数シグネチャを契約と考えることです。組み込みmysqliクラスには、次の署名があります。

public function query(string $query, int $result_mode = MYSQLI_STORE_RESULT): mysqli_result|bool

これを英語に訳すと、次のようになります。

  • のインスタンスがある場合mysqli...
  • ... その上でメソッドを呼び出すことが許可されqueryています ( public) ...
  • ...string最初のパラメーターとして a を使用 ...
  • ...そして必要に応じintて 2 番目のパラメータとして ...
  • ...そして、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がこれを強制できるようになったのはごく最近のことです。そのため、現在はハード エラーではないため、「うまくいかない」古いコードを修正する機会があります。

于 2022-02-14T16:56:29.610 に答える