PHP で interface を使って良い感じにコードを補完される(主に PhpStorm で)書き方をしたい！という、メモ(暫定版)

はじめに、今回のエントリーは、根本的には PHP の話であって、 laravel 固有ではないのですが、個人的に laravel での開発で遭遇した状況だったため、 laravel を前提としたメモになっています。

(laravel 以外のフレームワークでも同等の状況になるかと思います。また、下記の記載方法だと、 laravel らしい箇所は `app()` を使ってるという点しか無い…しいて言えば DI で Interface を入れる前提としています、というぐらいかも知れません。。。 laravel 要素薄すぎなので、 tag 不適切かも知れません。。)

# 何を実現したいか

- ある Interface（InterfaceA） が存在している
- その Interface（InterfaceA） を継承した Interface（InterfaceB） が存在している
- この時、 InterfaceB 固有のメソッドが存在してるものとする
- InterfaceB から、 InterfaceA に定義されているメソッドを呼び出した時、継承関係を前提として InterfaceB 固有のメソッドも呼べるようにコード補完されて欲しい

これを PhpStorm で実現したい。

どうすれば実現できるのかを確認した。

## 具体的には？

以下を実現したいとする

- RepositoryInterface が存在する
- RepositoryInterface には resetQuery メソッドが定義されている
- CompanyRepositoryInterface は RepositoryInterface を継承している
- CompanyRepositoryInterface には、 findByCompanyName メソッドが定義されている
- CompanyRepositoryInterface を implements した具象 Class にて、
  - resetQuery メソッドを呼び出した時に
  - RepositoryInterface の resetQuery メソッドを呼んだ後
  - そのまま CompanyRepositoryInterface の findByCompanyName がコード補完されてて欲しい

## やってみた

取り敢えず、良さそうな方法を探して実験した。

何を持って良いと判断するかは、デフォルトの設定のままの PhpStorm が警告を表示しないかどうか、ちゃんとコード補完してくれるかどうか、を基準とした。

やっぱり警告(赤い波線)はよろしくない。ファイルを開いた時点でもうずっと Project Tree の該当ファイルに赤い波線だし、 Commit する時も警告出るし、まぁとにかく精神安定上良くない。。。

### 面倒だが今のところコレが良さそう

以下のコードならば、最終行の `->` にて、 PhpStorm は `findByCompanyName` をコード補完してくれる。

親である RepositoryInterface に定義している resetQuery を CompanyRepositoryInterface にも書く必要があり、かつ、 RepositoryInterface の resetQuery と違う戻り値の型を annotation で補完するという力技。

※ RepositoryInterface で戻り値の型を書かなくても何とかなるけど、まぁ、根本的には二回書くという残念な状態。

なんとも残念…だけど、今のところ、コレが良さそうなんやないか？という結論になった。

```php
/**
 * Interface RepositoryInterface
 */
interface RepositoryInterface
{
    /**
     * @return RepositoryInterface
     */
    public function resetQuery(): RepositoryInterface;

    public function all($columns = ['*']);

    public function find(int $id, $columns = ['*']);
}

/**
 * Interface CompanyRepositoryInterface
 */
interface CompanyRepositoryInterface extends RepositoryInterface
{
    /**
     * @param $name
     * @param array $columns
     * @return CompanyInterface
     */
    public function findByCompanyName($name, $columns = ['*']): CompanyInterface;

    /**
     * @return self
     */
    public function resetQuery(): RepositoryInterface;
}

/** @var CompanyRepositoryInterface $repository */
$repository = app(CompanyRepositoryInterface::class);
$repository->resetQuery()->
```

こんな感じ。
<img width="644" alt="success.png" src="https://qiita-image-store.s3.amazonaws.com/0/3243/ec203347-d399-b578-0d51-fb083c88614b.png">

### ダメだった:その1

何がダメか。

以下のコードの、最終行の `->` の後、 `find` しかコード補完されない。

まぁ、そうですよね。。。

```php
/**
 * Interface RepositoryInterface
 */
interface RepositoryInterface
{
    /**
     * @return RepositoryInterface
     */
    public function resetQuery(): RepositoryInterface;

    public function all($columns = ['*']);

    public function find(int $id, $columns = ['*']);
}

/**
 * Interface CompanyRepositoryInterface
 */
interface CompanyRepositoryInterface extends RepositoryInterface
{
    /**
     * @param $name
     * @param array $columns
     * @return CompanyInterface
     */
    public function findByCompanyName($name, $columns = ['*']): CompanyInterface;
}

/** @var CompanyRepositoryInterface $repository */
$repository = app(CompanyRepositoryInterface::class);
$repository->resetQuery()->
```

こんな感じ。

<img width="645" alt="fail1.png" src="https://qiita-image-store.s3.amazonaws.com/0/3243/a83e9fa6-8278-cfc0-0218-6307daae4d9f.png">

### ダメだった:その2

何がダメか。

具象クラスで警告が出る。もう、これは、精神安定上、非常にダメ。

以下のコードの、最終行の `->` の後、 `find` と `findByCompanyName` は補完されてる！

が、 `class CompanyRepository extends Repository implements CompanyRepositoryInterface` 部分の `extends Repository` に警告が出る。

Interface の戻り値の型が違うんやから、まぁそうやな。でも、動く。(非常に良くないけど。)

```php
/**
 * Interface RepositoryInterface
 */
interface RepositoryInterface
{
    /**
     * @return RepositoryInterface
     */
    public function resetQuery(): RepositoryInterface;

    public function all($columns = ['*']);

    public function find(int $id, $columns = ['*']);
}

/**
 * Interface CompanyRepositoryInterface
 * @method CompanyRepositoryInterface resetQuery
 */
interface CompanyRepositoryInterface extends RepositoryInterface
{
    /**
     * @param $name
     * @param array $columns
     * @return CompanyInterface
     */
    public function findByCompanyName($name, $columns = ['*']): CompanyInterface;
}

/**
 * Class Repository
 * @package Fail2
 */
class Repository implements RepositoryInterface
{
    public function resetQuery(): RepositoryInterface {}

    public function all($columns = ['*']) {}

    public function find(int $id, $columns = ['*']) {}

}

/**
 * Class CompanyRepository
 * @package Fail2
 */
class CompanyRepository extends Repository implements CompanyRepositoryInterface
{
    public function findByCompanyName($name, $columns = ['*']): CompanyInterface {}
}

/** @var CompanyRepositoryInterface $repository */
$repository = app(CompanyRepositoryInterface::class);
$repository->resetQuery()
```

こんな感じ。

<img width="882" alt="not-so-good.png" src="https://qiita-image-store.s3.amazonaws.com/0/3243/b9705638-cbc9-c73a-7b1e-11ecd3db22b4.png">

## 結論

そんなわけで、今のところ面倒やけど BaseInterface を継承した側の Interface で、戻り値の方ではなく、 Annotation で補正するという力技で何とかする（面倒やけど）が良いんではないかと思ってる。というメモです。

他に良い方法があるんかも知れんけど…