【PHP】Phan の静的コード解析で use 指定したクラスオブジェクトを認識してくれない

<h1>Phan から PhanUndeclaredTypeProperty やら PhanUndeclaredMethod やら PhanUndeclaredClassMethod やら PhanUndeclaredFunction と叱られる</h1>

> 助っ人メンバーを入れたのに、一部の熱狂的な[ファン](https://github.com/phan/phan/)が認めてくれず活動できません。

```php:sample.php
<?php

declare(strict_types=1);

namespace MYNAME\MYPACKAGE;

use Symfony\Component\Cache\Adapter\FilesystemAdapter; //助っ人。でも Phan からは「こんなやつ、聞いとらん」と怒られる

class MyClass
{
    /** @var FilesystemAdapter */ //← Phan から「こんなやつ、しらん」と怒られる
    protected $my_property;

    public function __construct()
    {
        $this->my_property = new FilesystemAdapter();
    }

    public function set(string $key, $value)
    {
        $cache_item = $this->cache_pool->getItem($key); //← Phan から「そんなん、できん」と叱られる

        $cache_item->set($value);              //← Phan から「なんやそれ」と叱られる
        $this->cache_pool->save($cache_item);  //← Phan から「出なおしてこい」と叱られる
    }
}
```

```text:エラー内容
PhanUndeclaredTypeProperty Property \MYNAME\MYPACKAGE\MyClass->my_property has undeclared type \Symfony\Component\Cache\Adapter\FilesystemAdapter
```

```text:エラー内容
phan PhanUndeclaredClassMethod Call to method __construct from undeclared class ...
```

`FilesystemAdapter` オブジェクトのメソッド、例えば `getItem()` を使っても、「そんなメンバー（メソッド）しらんし、聞いとらん」と叱られます。`use` で告知したよ？

```text:エラー内容
PhanUndeclaredMethod Call to undeclared method \MYNAME\MYPACKAGE\MyClass->my_property->getItem($key)
```

## TS; DR

> Phan が `vendor` ディレクトリなどにある依存パッケージ（ライブラリ）を読み込んでから静的解析をしていないことが原因です。

各メソッドの PHPDoc コメントに `@suppress PhanUndeclaredClassMethod` を追加してもいいのですが、Phan の `config.php` ファイルを作成して、対象ディレクトリに `vendor` を追加しつつ解析対象対象から外すと、パッケージがインポートされるようになります。

```php:phan.php
<?php
return [
    'directory_list' => [
        'src',
        'vendor/symfony', // use で使うパッケージを追加
    ],
    'exclude_analysis_directory_list' => [
        'vendor/' // 解析対象から除外
    ],
];
```

```bash:解析の実行例
$ # --allow-polyfill-parser  → php-ast がなくても解析する設定
$ # --config-file ./phan.php → 設定ファイルを指定
$ ./vendor/bin/phan --allow-polyfill-parser --config-file ./phan.php
...
```

## TL; DR

### 作曲家（`composer`）の言うことなんか聞かない Phan

PHP の[静的解析](https://ja.wikipedia.org/wiki/%E9%9D%99%E7%9A%84%E3%82%B3%E3%83%BC%E3%83%89%E8%A7%A3%E6%9E%90)ツール「Phan」は静的解析時、`composer` などの `autoloader` による**ダイナミックなインポート先を参照しません**。

そのため、`use` で宣言しても解析時に必要なパッケージ／ライブラリがインポートされていないため解析エラーが出ます。

`suppress` コメント（エラー無視コメント）の「`@suppress PhanUndeclaredClassMethod`」を PHPDoc のブロックコメントに入れて黙殺してもいいのですが、やはり Phan の意見は尊重したいものです。

色々ググってみたのですが、[Phan の公式 Wiki](https://github.com/phan/phan/wiki/Frequently-Asked-Questions#phan-says-that-an-internal-class-constant-or-function-eg-xdebug_is_enabled-soap-memcached-etc-is-undeclared) の FAQ にちゃんと載ってました。

> **Phan says that a composer dependency of my project(a class, constant, or function) is undeclared**
>
> - You must add the directory (or file) of that dependency to `'directory_list'` or `'file_list'` in `.phan/config.php`. Usually, you will want to add `'vendor'` to `'exclude_analysis_directory_list'` as well. See https://github.com/phan/phan/wiki/Getting-Started#creating-a-config-file
> - Make sure that the file exists in vendor/ (e.g. make sure that `composer.phar install` was executed if this is running in CI)
> - Make sure that there are no typos in the variable name, that the namespace of the class is correct, and that the file containing the class/constant/function exists.
>
> This is a common cause of `PhanUndeclaredClassMethod`, `PhanUndeclaredClass`, `PhanUndeclaredFunction`, etc.
> <sup>（[Frequently Asked Questions](https://github.com/phan/phan/wiki/Frequently-Asked-Questions#phan-says-that-a-composer-dependency-of-my-projecta-class-constant-or-function-is-undeclared) | Phan 公式 Wiki @ GitHub より）</sup>
>
> （筆者適当訳）
> **作曲家による（クラス、定数、または関数）の依存関係に対し、熱狂的なファンが「宣言していない」と怒っています。**
>
> - 依存関係のディレクトリ（またはファイル）を `.phan/config.php` の `'directory_list'` もしくは `'file_list'` に追加する必要があります。その場合、一般的に `'vendor'` ディレクトリを `'exclude_analysis_directory_list'` にも追加します。詳しくは https://github.com/phan/phan/wiki/Getting-Started#creating-a-config-file を参照ください。
> - ファイルが `vendor/` 以下に存在することを確認してください。（たとえば [CI](https://ja.wikipedia.org/wiki/%E7%B6%99%E7%B6%9A%E7%9A%84%E3%82%A4%E3%83%B3%E3%83%86%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3) で解析を実行する場合に「あらかじめ `composer.phar install` が実行されているか」などです）
> - 変数名にタイプミスがないこと、クラスの名前空間が正しいこと、およびクラス/定数/関数を含むファイルが存在することを確認してください。
>
> これは `PhanUndeclaredClassMethod`、`PhanUndeclaredClass`、`PhanUndeclaredFunction` などが発生する「あるある」な原因です。

とほほ。

### 参考文献

- [Creating a Config File](https://github.com/phan/phan/wiki/Getting-Started#creating-a-config-file) | Phan 公式 Wiki @ GitHub
- [このPHPがテンプレートエンジンのくせに慎重すぎる (中篇)](https://qiita.com/tadsan/items/d192115d2499aaf99a98) @ Qiita
- [Phanで静的解析](https://qiita.com/msmsny/items/46aaeda7e565cfc7ec48) @ Qiita