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の致命的なエラー:Foo :: bar(int $ baz)の宣言:FooはiFoo :: bar(int $ baz)と互換性がある必要があります:7行目のtest.phpのiFoo

重要なのは、FooはiFooの実装であり、私が知る限り、実装は特定のインターフェイスと完全に互換性があるはずです。おそらく、インターフェイスまたは実装クラスのいずれか(または両方)を変更して、を使用する代わりに名前でインターフェイスのヒントを返すことでこの問題を修正できますselfが、意味的にselfは「メソッドを呼び出したばかりのクラスのインスタンスを返す」という意味です"。したがって、それをインターフェースに変更することは、理論的には、呼び出されたインスタンスが返されることを意図しているときに、インターフェースを実装する何かのインスタンスを返すことができることを意味します。

これはPHPの見落としですか、それとも意図的な設計上の決定ですか?前者の場合、PHP 7.1で修正される可能性はありますか?そうでない場合、チェーンのためにメソッドを呼び出したばかりのインスタンスを返すことをインターフェースが期待していることを示唆する正しい戻り方法は何ですか?


これはPHPの戻り値の型ヒントのエラーだと思います。おそらくバグとして提起する必要があります。しかし、この後期段階でPHP 7.1に修正が加えられる可能性はほとんどありません
Mark Ba​​ker

7.1の最後のベータバージョンが数日前にオンラインになったので、修正が7.1に入る可能性はほとんどありません。
シャルロット・デュノワ

興味深いことに、self戻り値の型がどのように機能するかについての解釈をどこで読んでいますか?
アダムキャメロン

@Adam:self「これを呼び出したインスタンスを返し、同じインターフェースを実装する他のインスタンスを返さない」という意味は論理的であるように思われます。Javaにも同様の戻り値の型があったことを覚えているようです(Javaプログラミングを行ってからしばらく経ちましたが)
GordonM 2016

1
こんにちはゴードン。まあ、それがどこかで機能することが文書化されていない限り、私は論理的に現実であるかもしれないものを当てにしません。私が説明する状況でのTBHは、可能な限り宣言型であり、戻り値の型としてiFooを使用します。それが実際に機能しない状況はありますか?(私はこれはかなり言った「答え」、より「アドバイス」/意見です実現。
アダム・キャメロン

回答:


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 Ba​​ker

私はそれが事実であると考えました、そしてそれは私が問題を解決する方法です。ただし、クラスがインターフェイスを実装している場合、selfの戻り値は暗黙的にインターフェイスのインスタンスの戻り値でもあるため、可能であれば:selfを使用することをお勧めします。
GordonM 2016

19
ポール、ここで削除したコメントを削除すると、(A)重要な情報が失われ、(B)他のコメントと比較して議論の流れが妨げられるため、実際には有害です。マークとゴードンに依存しているあなたのコメントを削除する必要があった理由はわかりません。実際、あなたはあちこちでこれをやっていて、それを止める必要があります。1年前の質問に戻ってコメントをすべて削除し、議論の流れを完全に破壊する正当な理由はまったくありません。実際、それは有害で破壊的です。
コーディグレイ

ここにある引用の一部には重要な序文があります。「共変リターン型はタイプサウンドと見なされ、他の多くの言語で使用されます(C ++とJavaですが、C#ではないと思います)。このRFCは当初、共変リターンタイプを提案しましたがいくつかの問題のために不変に変更されました。」PHPにどのような問題があったのか知りたいです。それらの設計上の選択は、特性に奇妙な問題を引き起こすいくつかの制限を引き起こし、戻り値の型の制限を緩めない限り、多くの場合、それらをいくらか役に立たなくします(以下のいくつかの回答に見られるように)。非常にイライラします。
ジョンパンコースト

1
@MarkBaker戻り型がstaticPHP 8に追加されている
リカルドボス

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  {...}
}

PHP7.4以降で動作しています。



0

これは私には予想される動作のように見えます。

Foo::barメソッドを変更して、iFoo代わりに戻るようにしてself、それで完了します。

説明:

selfインターフェイスで使用されるのは、「タイプのオブジェクト」を意味しますiFoo
self実装で使用されるのは、「タイプのオブジェクト」を意味しますFoo

したがって、インターフェイスと実装の戻り値の型は明らかに同じではありません。

コメントの1つは、Javaと、この問題が発生するかどうかについて言及しています。答えは「はい」です。Javaでそのようなコードの記述が許可されていれば、同じ問題が発生しますが、そうではありません。JavaではPHPのselfショートカットの代わりに型の名前を使用する必要があるため、実際にこれが表示されることはありません。(Javaの同様の問題については、ここを参照してください。)


それで、宣言することはself、宣言することに似てい MyClass::classますか?
peterchaula 2016

1
@Laserはい、そうです。
Moshe Katz

4
しかし、FooがiFooを実装している場合、FooはタイプiFooの定義によるものです
GordonM 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.