15
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

`@return $this` と `@return self` と `@return static` の違い

Last updated at Posted at 2015-12-14

@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 です。実行自体はエラー無くできます

staticself は予想通りでしたが、 $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 を指定するのが良さそう、という結論は変わりません。

15
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?