Phan で静的コード解析すると「
PhanUnreferencedUseNormal Possibly zero references to use statement for classlike/namespace MyClass
」エラーが出る。特に Symfony の Component など、composer 経由で autoload したパッケージを
use
で宣言した際にも出る。
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」でググっても「何か違う」記事や Issue しか出てこなかったので、自分のググラビリティとして。
TL; DR
エラー自体は「ソースコード冒頭の use
ステートメント(声明)で宣言されるも、そのファイル内で使われている形跡がなさげ」という意味です。
まずは該当するライブラリ、つまり use
文のクラス/オブジェクトのインスタンスが、そのファイル中で本当に使われているか確認します。
使われているのにエラーが出る場合は「名前空間付きのライブラリか」を確認します。use \Hoge
といったグローバル空間に設置されるタイプのクラスだったり PSR-0 で読み込まれているタイプのクラスの場合は、Phan の設定でパッケージを指定する必要があります。
例えば、パッケージが ./vendor/my_vendor/my_package
に設置されている場合は、Phan の設定ファイルは以下のようになります。
<?php
return [
// Phan 解析時に読み込みたいパッケージのディレクトリ
'directory_list' => [
'src',
'vendor/my_vendor/my_package',
],
// 静的解析時に「解析対象から除外」したいディレクトリ
'exclude_analysis_directory_list' => [
'vendor/'
],
];
次に、Phan を呼び出す時に config-file
オプションで設定ファイルを指定します。
$ ./vendor/bin/phan --config-file ./path/to/phan.php
最後に use
文の「あり」「なし」でテストしてみます。PSR-0 タイプのライブラリの場合は use
文を削除が必要になります。
TS; DR
「この PHP がテンプレートエンジンのくせに慎重すぎる」シリーズに啓示を受けて、プロジェクトの開始時から静的解析ツールを使うようになりました。
どの静的解析ツールも基本中の基本は同じ確認をしてくれます。しかし、各々に解析する目線が異なっており、片方ではエラーが出なくても、もう片方でエラーが出ることが多々あります。口うるさく感じるものの、リファレンスを読むとそれなりの理由があり、思い込みや決めつけが強くポカが多い自分にはいいポカヨケになっています。
そんな中、とある俺様パッケージ開発中に、ユニットテストをパスしたのでいよいよ解析ツールからのエラー潰し込みに入ったものの、表題のエラーで予想外の時間を消費してしまいました。
ネットで調べても「Symfony 形式コメント対応 Phan プラグインを入れろ」だの、Symfony のコメント記法は独特だから解析できないなど、何か求めている情報とは違うようです。盲目的に試してみるも、効果なし。
一晩置いたら、エラー・メッセージに理由が書いてありました。。。
-
PhanUnreferencedUseNormal
- 「Phan です。
use
が普通に参照されていませんエラー」という意味。
- 「Phan です。
-
Possibly zero references to use statement for classlike/namespace MyClass
- 「
/クラス風の/俺様名前空間/MyClass
のためのuse
声明文に対して参照ナッシングの可能性あり」という意味。
- 「
落ち着いてエラー内容を読めばその通りなのですが、静的解析のエラーを1つ1つ潰していたら、何か麻痺していたようです。叱られる時に「あの時も。。。」と連続して色々注意されると、結局何を注意されていたのかわからずトンチンカンな行動を取るのと似ています。とほほ。。。
このエラーは、実際には俺様クラスだけでなく、Symfony の HttpClient コンポーネントでも出ていました。きっかけは、本体クラスから汎用メソッドを Trait 化した時です。
メソッドの引数でクラス名で型宣言する場合、use
文で利用するクラスを宣言しておかないと、静的解析ツールから叱られます。
<?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