@return $this
と @return self
と @return static
の違い
もしかしたら phpDocumentor 由来なのかもしれませんが、とりあえず手元の phpstorm 2017.2.4 で下記のような挙動を示したので備忘録として書き記します。
2019/11/02 追記・修正
- マジックメソッドの場合を追記
- phpstorm 2017.2.4 での結果
追記ここまで。
2018/02/17 修正
- phpstorm 2019.2.4 で挙動が変わってたので修正
追記ここまで。
親クラスの返り値の型記述が原因で、「継承したらメソッドチェーンでメソッドが消えちゃった」というシチュエーションを想定しています。
下記のコードを考えます。
/**
* @method static returnMagicStatic()
* @method self returnMagicSelf()
* @method $this returnMagicThis()
*/
class P
{
/**
* @return static
*/
public function returnStatic() { return $this; }
/**
* @return self
*/
public function returnSelf() { return $this; }
/**
* @return $this
*/
public function returnThis() { return $this; }
public function __toString()
{
return "";
}
}
class C extends P
{
public function func() { /* 何か処理 */ }
}
クラス P
に @return
アノテーションをそれぞれ
static
self
$this
を指定した3つのメソッド+3つのマジックメソッド(用アノテーション)を用意します( __toString
は後で触れます)。
この状態で下記のコードを記述するといくつか phpstorm が警告を発します(コメント参照)。
// パターン1. returnStatic は static を指定しているので戻り値は C として振る舞う(警告が出ない)
(new C())->returnStatic()->func();
// パターン2. returnSelf は self を指定しているので戻り値は P として振る舞う…かと思いきや C として振る舞う(警告が出ない。しかし補完にも出ない)
(new C())->returnSelf()->func();
// パターン3. returnThis は $this を指定しているので戻り値は P として振る舞う…かと思いきや C として振る舞う(警告が出ない)
(new C())->returnThis()->func();
// パターン4. returnMagicStatic は static を指定しているので戻り値は C として振る舞う…かと思いきや色々ダメになる(補完に出ないし警告がでる)
(new C())->returnMagicStatic()->func();
// パターン5. returnMagicSelf は self を指定しているので戻り値は P として振る舞う…かと思いきや C として振る舞う(警告が出ない。しかし補完にも出ない)
(new C())->returnMagicSelf()->func();
// パターン6. returnMagicThis は $this を指定しているので戻り値は P として振る舞う…かと思いきや C として振る舞う(警告が出ない)
(new C())->returnMagicThis()->func();
※「振る舞う」という記述は IDE が思い込んでいる型のことであって、実際の型はもちろんすべて C
です。実行自体はエラー無くできます
static
と self
は予想通りでしたが、 $this
がちょっと意外でした。
アノテーションのコンテキストではなく、実際の呼び出し側コンテキストでの $this
になるみたいですね。すごい。
が、マジックメソッドになると毛色が変わっていました。
具体的には self
でも C
のコンテキストになりました(パターン5)。
ただし、コード補完には出ないという謎挙動でした(手動で入力すればジャンプなどもできる)。
また、 static
であらゆる補完が消えていました(パターン4)。
self
はなぜかよくわかりませんが、 static
の方は多分 static メソッドだと判断されていそうです。 @method static returnMagicStatic()
って記法は「static なマジックメソッド」の記法そのままなので。
つまり、結果として @method
では static を返り型の明示としては使えません。
(ただ、 @method static static returnMagicStatic()
なら行けました。キモい)。
一覧でまとめると下記のようになります。
指定 | Cが呼び出せるか(普通のメソッド) | Cが呼び出せるか(マジックメソッド) | Cが補完に出るか(普通のメソッド) | Cが補完に出るか(マジックメソッド) |
---|---|---|---|---|
static | ○ | ×(静的なら可) | ○ | × |
self | ○ | ○ | × | × |
$this | ○ | ○ | ○ | ○ |
とりあえず $this
が万能のように見えるので、インターフェースや抽象クラスなどの、再実装前提のメソッドには $this
を指定するのが良さそうです。
(CakePHP は $this
を推奨してるっぽいです。参考)。
※ 下記の現象は phpstorm 2017.2.4 で直っていました
と、思ったんですが、下記の状況で妙な警告が出ました。
class O
{
public function hoge()
{
// Method __toString is not implemented for 'O'
echo (new C())->returnStatic();
// 警告なし
echo (new C())->returnSelf();
// Method __toString is not implemented for 'O'
echo (new C())->returnThis();
}
}
他のクラス内部で __toString
が実行される処理(この場合 echo
)で上記の警告が出ました。
いや、そのオブジェクト、 O
じゃないから!
static
や $this
がなにか悪さして記述中のコンテキストを $this だと思い込んでる?
と思ったんですが上記の警告を google 翻訳にかましたところ
メソッド__toStringは、「O」のために実装されていません
「ために」ってなんじゃらほい? __toString
使うのに、なんかオブジェクト同士の依存って必要だっけか?
なんだかよく分かりませんが、そう高い頻度で発生する事象でもないので、再実装前提のメソッドには static
か $this
を指定するのが良さそう、という結論は変わりません。