PHPStanのStubファイル作ってみる

# 初めに
PHPStanにはStubという機能があり、解析時、本来のファイルではなく別ファイルに定義したPHPDocを参照することができます。今回はこのStubについて解説します。

# Stubとは
前述の通り、実際のコードの代わりにメソッドやクラスのインターフェースを定義できる機能です。
vendor配下にあるサードパーティ製のコードのPHPDocが誤っている場合PHPStanはうまく解析ができません。その場合でもstubファイルを用いれば正しく解析を行うことができます。

スタブファイルの拡張子は`.stub`か`.php`です。

https://phpstan.org/user-guide/stub-files

# Stubを作ってみよう
ということでStubファイルを作ってみましょう。
ディレクトリ構成は以下です。

※サードパーティのファイルは本来vendor配下にあると思うのですが、簡潔にしたいので一旦以下のようにしてます
```
Project
    ├ src
    |   ├ analyseObject
    |   |   └ StubTest.php ← 解析対象
    |   └ thirdparty
    |       └ ThirdParty.php ← サードパーティ製の間違ったPHPDocを持つファイル
    └ stub
        └ ThirdParty.stub ← 正しいPHPDocを記載したスタブファイル
```

## 課題：サードパーティ製のコードのPHPDocが間違っていて、解析結果が正しくない
まずはサードパーティ製のソースを以下のように作成しました。
PHPDocには`@return int`とありあたかもintを返すようですが、実際にはstringにcastをしており、PHPDocが間違っていることが分かると思います。

```php: ThirdParty.php
<?php
namespace Path\to\project\thirdparty;

class ThirdParty
{
    /**
     * @param int $intArg
     * @return int
     */
    public function thirdPartyMethod($intArg)
    {
        return (string)$intArg; // stringを返すが、PHPDocではintが返るように記載されている！
    }
}
```

このサードパーティ製のメソッドを利用する箇所は以下です。
先ほど見せたサードパーティ製のメソッドを呼び出してそのまま返却しているため、returnするタイプをstringにしています。

```php: StubTest.php
class StubTest
{
    public function call(): string
    {
        $thirdParty = new ThirdParty();
        return $thirdParty->thirdPartyMethod(10);
    }
}

```

この状態で解析をかけると、以下のようにreturのタイプが間違っているという旨のエラーが出てきます。しかし、実際に間違っているのはサードパーティ製のコードのPHPDocであり、このエラーは過検知であるといえます。

```sh
$ ./vendor/bin/phpstan analyse .\src\analyseObject\StubTest.php
Note: Using configuration file C:\Path\to\project\phpstan.neon.
 1/1 [============================] 100%

 ------ ---------------------------------------------------------------------------------------------- 
  Line   StubTest.php
 ------ ----------------------------------------------------------------------------------------------
  :11    Method Path\to\project\analyseObject\StubTest::call() should return string but returns int.
         ✏️  StubTest.php
 ------ ----------------------------------------------------------------------------------------------
                                                                                                                        
 [ERROR] Found 1 error                                                                         
```

## 解決策：Stubファイルを利用する

ここでstubファイルの出番です。
以下のように、サードパーティ製のファイルと同じ名前空間で同じクラス名、同じメソッド名で定義しています。違う箇所は、returnするタイプをstringに変更したのみです。

```php: ThirdParty.stub
<?php
namespace Path\to\project\thirdparty;

class ThirdParty
{
    /**
     * @phpstan-param int $intArg
     * @phpstan-return string
     */
    public function thirdPartyMethod($intArg);
}

```

続いて、作成したスタブファイルをPHPStanに認識させましょう。
neonファイルに以下のように追加してください。

```yaml: phpstan.neon
parameters:
    stubFiles:
        - stub/ThirdParty.stub
```

この状態でもう一度解析を実行すると、先ほどのエラーが消えてくれました。
```sh
$ ./vendor/bin/phpstan analyse .\src\analyseObject\StubTest.php
Note: Using configuration file C:\Path\to\project\phpstan.neon.
 1/1 [============================] 100%

 [OK] No errors                                                                                
```

# 終わりに
PHPStanのスタブファイルはサードパーティ製のコードを変更せずに正しい結果を返すことができる素晴らしい機能です。このような場合には是非使ってみてください。

ここまでご覧いただきありがとうございました！