【PHP】Phan の "PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace" エラー時注意点

> Phan で静的コード解析すると「`PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace MyClass`」エラーが出る。
> 
> 特に [Symfony の Component](https://symfony.com/components) など、composer 経由で autoload したパッケージを `use` で宣言した際にも出る。

```text:Phanのエラー
PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace MyClass (\MyVendor\MyPackage\MyNameSpace\MyClass)
```

[「phan PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace」でググって](https://www.google.com/search?q=phan+PhanUnreferencedUseNormal+Possibly+zero+references+to+use+statement+for+classlike%2Fnamespace)も「何か違う」記事や Issue しか出てこなかったので、自分のググラビリティとして。

## TL; DR

エラー自体は「ソースコード冒頭の `use` ステートメント（声明）で宣言されるも、そのファイル内で使われている形跡がなさげ」という意味です。

まずは該当するライブラリ、つまり `use` 文のクラス／オブジェクトのインスタンスが、そのファイル中で本当に使われているか確認します。

使われているのにエラーが出る場合は「名前空間付きのライブラリか」を確認します。`use \Hoge` といった**グローバル空間に設置されるタイプのクラス**だったり PSR-0 で読み込まれているタイプのクラスの場合は、Phan の設定でパッケージを指定する必要があります。

例えば、パッケージが `./vendor/my_vendor/my_package` に設置されている場合は、Phan の設定ファイルは以下のようになります。

```php:./path/to/phan.php
<?php

return [
    // Phan 解析時に読み込みたいパッケージのディレクトリ
    'directory_list' => [
        'src',
        'vendor/my_vendor/my_package',
    ],
    // 静的解析時に「解析対象から除外」したいディレクトリ
    'exclude_analysis_directory_list' => [
        'vendor/'
    ],
];
```

次に、Phan を呼び出す時に `config-file` オプションで設定ファイルを指定します。

```shell-session
$ ./vendor/bin/phan --config-file ./path/to/phan.php
```

最後に `use` 文の「あり」「なし」でテストしてみます。PSR-0 タイプのライブラリの場合は `use` 文を削除が必要になります。

## TS; DR

[「この PHP がテンプレートエンジンのくせに慎重すぎる」シリーズ](https://qiita.com/search?sort=&q=title%3A%E3%81%93%E3%81%AEPHP%E3%81%8C%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3%E3%81%AE%E3%81%8F%E3%81%9B%E3%81%AB%E6%85%8E%E9%87%8D%E3%81%99%E3%81%8E%E3%82%8B)に啓示を受けて、[プロジェクトの開始時から静的解析ツールを使う](https://github.com/KEINOS/TPL-PHP-HelloWorld)ようになりました。

どの静的解析ツールも基本中の基本は同じ確認をしてくれます。しかし、各々に解析する目線が異なっており、片方ではエラーが出なくても、もう片方でエラーが出ることが多々あります。口うるさく感じるものの、リファレンスを読むとそれなりの理由があり、思い込みや決めつけが強くポカが多い自分にはいい[ポカヨケ](https://ja.wikipedia.org/wiki/%E3%83%9D%E3%82%AB%E3%83%A8%E3%82%B1)になっています。

そんな中、とある俺様パッケージ開発中に、ユニットテストをパスしたのでいよいよ解析ツールからのエラー潰し込みに入ったものの、表題のエラーで予想外の時間を消費してしまいました。

[ネットで調べて](https://www.google.com/search?q=phan+PhanUnreferencedUseNormal+Possibly+zero+references+to+use+statement+for+classlike%2Fnamespace)も「[Symfony 形式コメント対応 Phan プラグイン](https://github.com/Drenso/PhanExtensions#annotationsymfonyannotationplugin)を入れろ」だの、Symfony のコメント記法は独特だから解析できないなど、何か求めている情報とは違うようです。盲目的に試してみるも、効果なし。

一晩置いたら、エラー・メッセージに理由が書いてありました。。。

- **`PhanUnreferencedUseNormal`**
    - 「Phan です。`use` が普通に参照されていませんエラー」という意味。
- **`Possibly zero references to use statement for classlike/namespace MyClass`**
    - 「`/クラス風の/俺様名前空間/MyClass` のための `use` 声明文に対して参照ナッシングの可能性あり」という意味。

落ち着いてエラー内容を読めばその通りなのですが、静的解析のエラーを１つ１つ潰していたら、何か麻痺していたようです。叱られる時に「あの時も。。。」と連続して色々注意されると、結局何を注意されていたのかわからずトンチンカンな行動を取るのと似ています。とほほ。。。

このエラーは、実際には俺様クラスだけでなく、[Symfony の HttpClient コンポーネント](https://symfony.com/doc/current/http_client.html)でも出ていました。きっかけは、本体クラスから汎用メソッドを Trait 化した時です。

メソッドの引数でクラス名で型宣言する場合、`use` 文で利用するクラスを宣言しておかないと、静的解析ツールから叱られます。

```php:SampleTrait.php
<?php

declare(strict_types=1);

namespace \MyVendor\MyPackage\MyNameSpace;

use \Symfony\Component\HttpClient\HttpClient;

trait Sample
{
    public function requestUrlAsGet(HttpClient $client_http, string $url_target): array
    {
        $response = $client_http->request('GET', $url_target);
        return $response->toArray();
    }
}
```

実際には Trait を利用する先で `use` 宣言していればスクリプトは動くことは動きます。

しかし、ダイレクトに Trait ファイルを見た時に「なんじゃ、この降って湧いたようなクラスは」とならないように「このファイルで使うクラスを予め言っておくよ」と宣言しておけということです。

問題は、使っていないくせに「使うよ」と宣言されると、「どこ？どこ？」となってしまうので、無駄な宣言に対しても厳しいということです。

未来の俺よ、またここに来たらまずはもちつけ。落ち着いてエラーを読めばわかる。エラーの内容を読んで理解する前にググるな。orz