PhpStorm の静的解析機能をさらに活用するための3つのアノテーション
先の M-1グランプリ決勝ではスーパーマラドーナを応援していた kagata です。「人に話すとツキが落ちる」という非科学的な理由からあまり他言しないようにしていたのですが、その努力もむなしくファーストラウンド敗退に終わりました。結果は残念でしたが、関東の地上波ゴールデンタイムで「ひき肉にしてやんよ」が聴けたのでそれで満足することにします。
さて、今回は PhpStorm についての Tips です。PHP のソースコードにアノテーションを追加することで、PhpStorm の強力な静的解析機能をさらに有効活用できる例をご紹介します。これにより、バグの作りこみをへらしたり、自動補完でコーディングの効率をアップさせたりすることができます。ぜひ活用してみてください。
それにしても、「○○を◎◎する N 個の☆☆」なんてありがちな記事タイトルをたまにつけると、なんだかすごく恥ずかしいですね。
PhpStorm の静的解析機能
PhpStorm は強力な静的解析機能を備えています。PhpStorm が表示する静的解析エラーの情報に注意することで、プログラムを動かす前にバグの作りこみを察知することができます。
例えば、次のようなコードを用意します。
function plus($a, $b)
{
$c = $a + $b;
return $a + $d;
}
このコードを PhpStorm で表示すると、次のようになります。
変数 $c
がグレーアウトしています。これは、この変数を参照する箇所がこの先どこにもないことを示しています。また、変数 $d
の下に赤い波線が引かれています。これは、この変数がどこにも定義されないまま参照されてしまっていることを表しています。どうやら $b
の typo のようですね。
この警告にしたがって、 `$c` を宣言する文を削除したり、`$d` を `$b` に直したりすることで、無駄やバグのないコードが書けるというわけです。
PHP のアノテーション
アノテーションとは、プログラムのソースコードにそれ自体からは読み取れないような情報を補足するためのコメントなどの記述のことです。PHP では phpDocumentor に準拠したアノテーションがよく用いられます。例えば以下のようなものです:
/**
* 2つの整数の和をとる
*
* @param int $a 整数値
* @param int $b 整数値
* @return int 2つの引数の和
*/
function plus($a, $b)
{
return $a + $b;
}
上のコードでは、 @param
アノテーションで引数の型と内容、 @return
アノテーションで返り値の内容を説明しています。型の取り扱いがゆるい PHP でも、アノテーションを充実させることで型の情報をきちんと管理し、予期しないバグを作りこまないようにすることができます。
PhpStorm の静的解析にアノテーションの情報をいかす
それでは、アノテーションの情報を充実させることで PhpStorm の静的解析機能をさらに活用する例を3つご紹介します。
@property アノテーション でクラスのプロパティに関する情報を補足する
例えば、下のようなコードがあったとします。
// User.php
class User
{
private $name;
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
// Group.php
include_once 'User.php';
class Group
{
private $leader;
private $members = [];
public function setLeader(User $user)
{
$this->leader = $user;
}
public function getLeaderName()
{
return $this->leader->getName();
}
public function addMember(User $user)
{
$this->members[] = $user;
}
public function getMemberName($index)
{
return $this->members[$index]->getName();
}
}
このとき、Group.php を PhpStorm で表示すると、次のような状態になります。
Group::$leader
が getName()
メソッドを持っていないと判断され、警告表示となっています。
こんなときは、 @property
パラメータでプロパティの型を指定してやりましょう。 $leader
には User
インスタンスが入ります。 $members
は User
インスタンスの配列です。型名としては array
となりそうですが、 User[]
とすることで配列要素の型まで詳細に指定できます。
/**
* Class Group
*
* @property User $leader
* @property User[] $members
*/
class Group
{
すると、次のような表示になりました。
さっき出ていた警告が消えているのがわかります。
また、 getName()
メソッドにカーソルを置いて Ctrl + B
をたたくと getName()
メソッドの定義へジャンプすることもできるようになっています。
@method アノテーションでクラスのメソッドに関する情報を補足する
例えば、CakePHP ではマジックメソッドを使ったオーバーロードがよく使われます。次のようなものが典型的です。
class OneModel extends AppModel
{
public function oneFunction()
{
return $this->findById(1);
}
}
これを PhpStorm で表示すると、次のような状態になります。
findById()
なんていうメソッドは存在しないと警告されています。ところが、実際にはこのコードは問題なく動作します。CakePHP の提供するマジック find と呼ばれる機能です。
findById()
は問題なく呼び出せるから心配するな、と PhpStorm に教えてあげましょう。そんなときに @method
アノテーションが使えます。
/**
* Class OneModel
*
* @method array findById($id, $fields = null)
*/
class OneModel extends AppModel
{
すると、PhpStorm での表示は次のようになります。
findById()
メソッドが存在しないという警告が消えました。なお、 @method
アノテーションの引数や返り値の記述も静的解析に有用です。
@uses アノテーションでメソッドの使用状況を補足する
例えば、こんなコードがあるとします。
include_once 'User.php';
include_once 'Guest.php';
class Group
{
private $users = [];
private $guests = [];
public function addMember($member)
{
$adder = sprintf('add%sMember', get_class($member));
$this->$adder($member); // $member のクラスにより addUserMember() や addGuestMember() が呼ばれる
}
private function addUserMember($user)
{
$this->users[] = $user;
}
private function addGuestMember($guest)
{
$this->guests[] = $guest;
}
}
これを PhpStorm で表示すると次の状態になります。
addUserMember()
や addGuestMember()
のメソッド名の部分がグレーアウトしています。これは、プロジェクトのどこからもこのメソッドが使われていないという警告です。
しかし、実際にはこれらのメソッドは PHP の可変関数のしくみを使って呼び出されています。そうとは知らない作業者が PhpStorm の警告に従ってメソッドを削除してしまってはたいへんです。
そこで、これもアノテーションを使って正しく静的解析されるようにしましょう。こういうときには @uses
アノテーションが使えます。
/**
* @uses addUserMember(), addGuestMember()
*/
public function addMember($member)
{
アノテーションを追加した上で、PhpStorm でどのような表示になるか確認します。
グレーアウトしていたメソッドが通常の表示になりました。プロジェクト内でそれらメソッドが使われていることを PhpStorm が認識している状態です。
なお、呼び出し元に @uses
アノテーションを追加するだけで PhpStorm の静的解析には有効ですが、呼び出される側にも @used-by
アノテーションを追加しておきましょう。phpDocumentor のドキュメントでそのようにすすめられています(SHOULD)。
/**
* @param User $user
* @used-by addMember()
*/
private function addUserMember($user)
参考:@uses & @used-by | phpDocumentor
まとめ
PhpStorm で覚えておくと役立つ PHP のアノテーションを3つご紹介しました。
- プロパティの情報
@property
- メソッドの情報
@method
- プロパティやメソッドの呼び出し元を示す
@uses
これらを駆使することで、PhpStorm の静的解析機能をさらに活用することができます。
なお、アノテーションの使用にあたってはこまめな内容の見直しを心がけましょう。コードの実態にかかわらず、アノテーションにはどんなうそでも書き込めてしまいます。不用意にコピペを繰り返すなどしてアノテーションの内容とコードの実態が食い違ってしまうと、PhpStorm も実態と異なる静的解析結果を返し、かえってバグを作りこむもとになってしまいます。ご利用は計画的に。