はじめに
古いPHPで書かれた古いプロダクトの保守をしているといろいろと大変なことがあります。
例えば、メソッドや変数に型情報がないためコードを読んだり書いたりするのにも以下のようなことから一苦労です。
- コード補完がきかない
- コードジャンプもきかない
少しだけ手間をかけることで、ちょっとでもコードを読み解きやすくしたいと思います。
環境
ご長寿
- PHP 5.3
- Symfony 1.4
- Doctrine 1.2
- PhpStorm
PhpStorm は素の状態です
PHPDoc を書く
PHPDoc とは
簡単に言うと、メソッドやクラスなどに定義上明記されていない情報を特別な記法でコメントに書くことで IDE などが解析しやすくするものです。
機械が解析できるようにすることで、ドキュメントを自動生成できるようになります。
この記事では変数の型やその説明が書ける、と理解してもらえれば良いです。
実際に書いてみましょう。
メソッドに書く場合
メソッドにはこんなのが書けます。
/**
* Counts the number of items in the provided array.
*
* @param mixed[] $items Array structure to count the elements of.
* @param bool $recursive Optional. Whether or not to recursively
* count elements in nested arrays.
* Defaults to `false`.
*
* @return int Returns the number of elements.
*/
public function count($items, $recursive = false)
{
<...>
}
ここで見てほしいのは @param
と @return
です。
@param
引数に関する情報を書きます。
@param [型] [名前] [説明]
重要なのは [型]
と [説明]
です。
[型]
には int
のようなプリミティブ型やクラスなどを書きます。
型について詳しくはこちらで↓確認できます。
[説明]
には引数にどんなものを渡してほしいのかなどを簡潔に書くと良いです。
@return
返り値に関する情報を書きます。
名前がないこと以外は @param
と同様です。
@return [型] [説明]
先程のメソッドを呼び出してみましょう
$this->
と打ち込んだところで候補に count
が出てきました。
ここまでは PHPDoc コメントがなくても出てくると思います。
ここで count
を選択してみます
なにか出てきました。
こんなこと↓が書いてあるようですね
- 上段 :
引数の名前: 引数の型
- 下段 : 引数の説明
引数の説明のところを丁寧に書いておくと
呼び出すときにどんなものを渡せば良いのかが、わざわざ定義を読まなくてもわかります。
嬉しい。
ちなみに PHPDoc コメントを書いていないと
こうなります
型が類推できる場合はでてくることもありますが、説明は当然ながらありません。
items
に何を渡すべきなのかを知るには count()
の定義を確認する必要がありそうです。
直接 Doc コメントが書けないメソッドもある
すべてのメソッドに PHPDoc コメントを書きましょう
といいたいところなんですが、そうわけにいかないこともあります。
歴史あるプロダクトには一筋縄ではいかない場合もあったりするのです。
例えば古い Symfony + Doctine なプロダクトを泳いでいるとこんなものに遭遇することがあります。
$segment = SomeSegmentsTable::getInstance()->findOneBySomeUserStatus($user->getStatus());
どうやらこの findOneBySomeUserStatus
というメソッドには PHPDoc コメントは書かれていないようです。
ここままだと $segment
を使うときに補完がききません。
困った。
渋々定義を確認しようとします
が、よく見ると定義がありませんとあります。
まさかと思って確認しますが確かに SomeSegmentsTable
クラスには書いてありません。
<?php
/**
* SomeSegmentsTable
*
* This class has been auto-generated by the Doctrine ORM Framework
*/
class SomeSegmentsTable extends Doctrine_Table
{
/**
* Returns an instance of this class.
*
* @return object SomeSegmentsTable
*/
public static function getInstance()
{
return Doctrine_Core::getTable('SomeSegments');
}
}
これだけ。
詳細は割愛しますが、 findOneBySomeUserStatus
はマジックメソッドで、 Doctrine 本体で定義されています。
ライブラリのコードなので、自分で Doc コメントを書き加えたりするわけにもいかないでしょう。
しかしこのままではどうしても嫌なのでなんとかしてみます。
変数に書く場合
先程のこちら、
$segment = SomeSegmentsTable::getInstance()->findOneBySomeUserStatus($user->getStatus());
詳しいことは省きますが、
findOneBy~~
はデータベースからデータを1件取得してくれます。
この例ではデータを1件分のクラスは SomeSegments
です。
こんなとき、この変数 $segment
に対してこのように PHPDoc コメントを書きます。
/** @var SomeSegments $segment This is user segment. */
$segment = SomeSegmentsTable::getInstance()->findOneBySomeUserType($user->getType());
// もしくはこう↓書く
/**
* This is user segment.
*
* @var SomeSegments $segment
*/
$segment = SomeSegmentsTable::getInstance()->findOneBySomeUserType($user->getType());
@var
1行で書く場合
/** @var [型] [変数名] [説明] */
複数行で書く場合
/**
* [説明]
*
* @var [型] [変数名]
*/
先程の変数を使ってみます
ちゃんと補完してくれますね。
嬉しい。
ただしメソッドの正体は謎のままです
そんなメソッドはありませんよ、と言われる。
ちょっとモヤモヤしますが、気にしなければ先へ進むことができます。
気にしていられないことも多いのでこのまま突き進むこともよくあります。
クラスに書く場合
上述のように呼び出す度に書いても良いんですが、よく使う場合はちょっと邪魔くさく感じるかもしれません。
そこで、先程の定義が書いてあることを期待したけど書いてなかったクラス、
こちらに Doc コメントを書くこともできます。
@method
を使います。
/**
* SomeSegmentsTable
*
* This class has been auto-generated by the Doctrine ORM Framework
*
* @method SomeSegments findOneBySomeUserType(string $someUserType)
*/
class SomeSegmentsTable extends Doctrine_Table
@method
@method [[static] 返り値の型] [名前]([[引数の型] [引数]<, ...>]) [説明]
改めて呼び出し側をみてみましょう
今度は変数の Doc コメントがなくても補完されます。
いいですね。
メソッドの型定義もわかります。
すばらしい。
まとめ
これだけのことでも少しは息ができるようになりました。
おまけ
VSCode の場合は Extention を入れると同じように補完してくれるみたいです。