プログラミングにおいて、処理を様々な目的で分割つまり関数化することは基本中の基本です。
関数は作成しただけでは意味がなく、別の処理から呼び出されて初めて機能します。しかし、この「関数を使う」と言う局面でいくつかの問題が発生することがあります(少なくとも個人的経験としては)。
- 関数の機能やインタフェース等を失念し、いちいち定義箇所を探し、確認する必要が生じる
- 関数の存在自体を忘れていて、関数を使わずに処理を書こうとする
- 関数の存在自体を忘れていて、同じような機能の関数を別に作成してしまう
1のケースでは、関数名だけでも覚えていれば検索等で効率的に探せますが、関数名も曖昧で漠然とした機能イメージだけしか記憶にないこともあり、そのような場合は色々なファイルの中を探し回る羽目になります。
2,3のようなケースも馬鹿馬鹿しいながらままあることで、あとで気づいた時の脱力感はなかなかです。
そもそも作成した関数の整理がされていないことが問題であり、上記のような問題以外でも例えば自製プログラムを他者に引き継ぐ場合などにも資料はあった方が良い訳で、結論としては「関数仕様書をしっかりと作成すべし」となるのですが、言うは易しで実現にはなかなか至りません。
関数仕様書作成に抵抗があるのは、それがプログラミングとは別作業だからです。プログラミング言語で書いたものを改めて他の形式で書き直すと言う二度手間が発生することに抵抗がある訳です。と言うことは、プログラミング言語で書かれたものから自動的に関数仕様書が作成できないものかと考えたくなります。
関数内のロジックから「この関数は…する機能です」のように要約してくれることはAIの進化をもう少し待つ必要がありそうですが、関数の概要くらいは今でもプログラム内にコメントとして書いていたりするのではないかと思いますので、関数のインタフェース関連情報(関数名、引数、戻り値など)と処理内容に関して書かれたコメントを抽出してまとめられれば、関数仕様書として十分機能しそうです。
その程度だったら頑張れば実現できそう…と、どこかの賢い方も考えたのでしょう。PHPに関してはそのようなツールがあります。
それが「phpDocumentor」です。
phpDocumentorの概要
phpDocumentorは前述の通り既存のPHPファイルを解析し関数仕様書にまとめてくれるツールです。
生成された関数仕様書はHTML形式で、ブラウザ経由で閲覧できます。個人的にはWebサーバ環境を独自に用意できるので出力結果である関数仕様書もWeb経由で見ていますが、HTMLファイル直起動でも問題なさそうです(機能説明においてオフラインでも閲覧可能っぽい表現が出てきますので多分大丈夫)。
実はphpDocumentor自体は以前にも少し使ったことのあったツールなのですが、表示形式に今一つ馴染めず、便利ではあるもののあまり使わない状態になっていました。しかし、昨今改めて試用してみたところ、どうも昨年(2020年)10月にバージョン3が出たようで、それまでのバージョン2と表示形式がかなり変わっており、洗練された印象になっています(あくまで個人の感想ですが)。
また、仕様書内に対する全文検索機能が実装されたようです(多分、バージョン2ではなかったはず)。最初の方で書いたように「関数を探す」と言う行為もしばしば必要になりますので、これはなかなかに重要な機能です。
phpDocumentorが仕様書にまとめてくれるのはあくまで元ソースに書かれている情報であり、結局はソースを直接検索するのと同じでは?と言う見方もあるのですが、phpDocumentorが抽出するのはあくまで所定の書式で記述された情報だけなので、見方を変えれば余計な情報に邪魔されずに見つけたい情報が見つけやすくなっているとも言えます。
この辺は抽出させる情報をいかに過不足なく適切な内容にするかと言う使い方の問題でもあるかと思います。
インストール
インストール方法に関しては、公式サイトではPHARもしくはDockerを使う方法が紹介されています。
一方でComposerに関しては「できるけど推奨しない」旨が書かれています。理由としては、phpDocumentorは多くのライブラリ類を必要としており、それらが既存のライブラリとの間で競合(conflict)する可能性が非常に高いため、と述べています。実際、既存のLaravel環境にcomposerでphpDocumentorをインストールしようとしたところ”予定通り”エラーが発生しました。
ただ、改めて考えてみるとphpDocumentorはあくまで開発ツールとして使用したいものであり、例えばLaravelをベースとしている特定のプロジェクト環境にインストールしたいものではありません。
と言うことで、あくまで馴染んだcomposerの使用にこだわりつつ、作業用ユーザーのホームディレクトリ配下に「~/util/phpdocumentor」と言うディレクトリを作成し、ここにphpDocumentorをインストールしてみました。
cd ~/util/phpdocumentor
composer require --dev phpdocumentor/phpdocumentor
普通にインストールできました。
パスも通しておきましょう。「.bashrc」に以下の内容を追加します。
PATH=$PATH:$HOME/util/phpdocumentor/vendor/bin
これで、同ユーザーであれば実行ディレクトリに関わらずphpDocumentorが使えるようになります。
なお、今回は先に「インストールはcomposerでできるか?」と言う発想があったため、あえて初志貫徹でcomposerを使ってみましたが、大きく手間が変わる訳でもなさそうなので、素直にPHARを使った方が良いかもしれません(今更ですが)。
使い方
phpDocumentorの使い方は至ってシンプルで、以下のコマンドを実行するのみです。
phpdoc run -d <ソースディレクトリ> -t <出力先ディレクトリ>
上記により「ソースディレクトリ」として指定したディレクトリ配下を再帰的に検索し、PHPで書かれたファイル(多分、拡張子が「.php」かどうかで判断しているのだと思いますが)から必要な情報を抽出し、その結果が「出力先ディレクトリ」として示したディレクトリ配下にHTMLファイルとして展開されます。
出力先ディレクトリにindex.htmlがありますので、これを開いてみます。
バージョン2までの表示形式を知っている人であれば、「おっ」と思うのではないでしょうか?
好みの問題はあるかと思いますが、随分スッキリしたデザインになっています。
右上に検索機能が付いています。少し試してみましたが、曖昧検索になっているように思います。
例えば「prop1」と入力した際に同文字列を含んだ箇所がヒットするのは当然として「Providers」や「prop2」なども対象として表示されます。
表示順としては完全一致・部分一致したものを優先的に表示してくれるようになっていますし、オフラインでも実行可能な検索としてはかなり頑張ってくれている印象ですが、一方で「このページが何でヒットしたんだろう?」と言うページも結構含まれてきますので、この辺は賛否が分かれるところかと思います(これはGoogle先生にも言えることですが)。
左メニューは大きく「Namespaces」「Reports」「Indices」の3パートに分かれています。
「Namespaces」に関しては指定した環境内の名前空間の構造を示しています。上記例はLaravel環境の「App」配下を対象としたもので、Laravelに馴染みのある方であれば雰囲気が伝わるのではないかと思います。だた、左メニュー上に表示される名前空間の階層は上記にあるように二層までで、それ以下に関しては選択した名前空間配下のクラスやさらに下位の名前空間がメインコンテンツ部にリスト表示されますので、そこから選択していく形になります。
「Reports」に関しては良くわかりません。「Errors」などはphpDocumentorの実行において発生したエラーが報告されるようですが、幸か不幸か今のところエラーが発生していないので「No errors have been found in this project.」と言うメッセージが表示されているのみです。
「Deprecated」「Markers」に関してもそれぞれ「No deprecated elements have been found in this project.」「No markers have been found in this project.」と表示されているのみで、どのような状況でどのようなメッセージが表示されるのかは、その状況になってみないと分からないです。
「Indices」には「Files」のみ存在し、これをクリックすると文字通り指定した「ソースディレクトリ」配下のファイル一覧がアルファベット順に表示されます。
phpDocumentorの出力結果としてはファイルと関数仕様1ページが対応しているため、文書構造的にはページ(ファイル)単位の「Indices(索引)」があることは理解できるのですが、ここはもう一声頑張ってメソッド単位の一覧もあると良かったように思います。
ソースとドキュメントの関係
では、具体的にどのようなソースがどのように表示されるかについて見ていきたいと思います。細かい点まで含めると情報が膨大になりそうなので、とりあえず基本的な部分としてクラス、プロパティ、メソッド辺りの表示がどのように行われるかくらいに絞って確認したいと思います。
なお、内容にはあまり関係ありませんが、Laravel環境を前提としています。
確認用サンプルとして以下のような内容で「app/Http/Controllers/SampleController.php」を作成してみました。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
/**
* クラスに関する概要1
* クラスに関する概要2
*
* クラスに関する説明1
* クラスに関する説明2
*
* クラスに関する説明3
* クラスに関する説明4
*/
class SampleController extends Controller
{
/**
* プロパティ1の概略1
* プロパティ1の概略2
*
* プロパティ1の説明1
* プロパティ1の説明2
*
* プロパティ1の説明3
* プロパティ1の説明4
* @var int $prop1
*/
public int $prop1;
/** @var int $prop2 プロパティ2の説明 */
protected string $prop2;
/**
* @var bool $prop3 プロパティ3の説明
* @var bool $prop4 プロパティ4の説明
*/
private bool $prop3;
private bool $prop4;
/**
* sample_publicの概略1
* sample_publicの概略2
*
* sample_publicの説明1
* sample_publicの説明2
*
* sample_publicの説明3
* sample_publicの説明4
*
* @param Request $request 引数の説明
* @return void なし
*/
public function sample_public(Request $request) : void
{
echo $this->sample_protected(1);
echo $this->sample_private('hello', 'world');
}
protected function sample_protected(int $arg) : int
{
return $arg * 2;
}
/**
* サンプルアクションから呼ばれるprivateメソッド
* @param string $arg1 引数1の説明
* @param string $arg2 引数2の説明
* @return string 戻り値の説明
*/
private function sample_private(string $arg1, string $arg2) : string
{
return $arg1." ".$arg2;
}
}
上記に関する表示結果を見ていきます。
クラス情報
クラス関連の情報としては、クラス名に加えて名前空間や継承している親クラス情報などが表示されています。
親クラス名をクリックすると親クラスの説明ページに移動します。
クラス定義の直前に記述したコメントの内容がクラスの説明として反映されています。
なお、phpDocumentorが参照するコメントの構造に関しては大きく「概要(Summary)」「説明(Description)」「タグ」の3部分に分かれており、この構造はプロパティやメソッドでも共通します(プロパティに関しては別の書式もあるのですが、それは後述)。
コメント上の「概要」と「説明」は最初の空行を挟んで区別され、最初の空行までの複数行が「概要」、空行以降の複数行(空行を含んでもOK)が「説明」とみなされます。
ただ、クラスに関しては「概要」と「説明」の区別がはっきりしませんね…
もう少し細かく見ていくと、ソースのコメント上では「概要」「説明」に該当する文字列が複数行に分かれていますが、表示では上記のように連結して1行にされます。実はHTML上でもコメントと同様に改行された状態で文字列が並んでいるのですが、そもそもHTML表示時には文字列としての改行は改行として扱われないため、結果として上記のように連結された状態で表示されています。
一方で「説明」を構成する行の中に空行が存在する場合、空行を挟んだ前後の行の塊それぞれが<p>タグで囲まれるため、それぞれが独立した行として表示されます(「クラスに関する説明1クラスに関する説明2」と「クラスに関する説明3クラスに関する説明4」のように)。
なお、コメントに記述した文字列はサニタイズされているようですが、<br>タグに関してはそのまま採用される模様で、これにより「概要」や「説明」内で本来の「改行」を行うことは可能です。ただ、PHPとして見た場合にコメントにHTMLタグが入っているのは何となく違和感があるので、あまり使用するつもりはありませんが。
目次(プロパティとメソッドの概要一覧)
次に「Table of Contents(目次)」としてプロパティとメソッドの概要が一覧表示されます。
プロパティとメソッドの違い、およびpublic, protected, privateの違いがアイコンで表現されています。
その他情報としては、プロパティでは型および概要、メソッドでは戻り値の型および概要のみ表示されるようです。
プロパティに関するタグとしては「@var」が使用でき、型や補足説明が記述できます。
メソッドに関するタグとしては「@param」で引数の型や補足説明が、「@return」で戻り値に関する補足説明が記述できます。
ただ、今回のサンプルソースではプロパティおよびメソッドの引数、戻り値に関してはいずれもPHPの書式として明示的に型宣言を行っています。プロパティに関してもPHP7.4から型宣言が行えるようになったようです。この状態で上記タグにおける型の記述に意味があるかは少々疑問になってきます。試しにタグと実際の型宣言で違う型を指定したところ、型宣言の情報が有効になりました。つまり明示的型宣言を行っていればタグ上の型の記述は実質無意味です(タグの書式として記述する必要は残るかと思いますが)。
古いPHPのバージョンにおいては上記タグにおける型の記述は継続して有効かもしれませんが、PHP7.4以降においてはこれらタグはあくまで対象となるプロパティ、引数、戻り値の補足説明を書くものと位置付けた方が良いかもしれません。
また、ソースを見ていただくと分かるように、プロパティに関してはコメントの書き方に3タイプあり、それぞれを試してみました。しかし、今のところ「prop1」に対して付加したコメントの形式(クラスやメソッドと同様に「概要」「説明」「タグ」を複数行に渡って記述する形式)しか有効になっていません。一応ネット情報によればこの3つの書式はいずれも有効なはずなので、この点は謎です。
ただ、前述したようにPHP7.4以降ではコメントにおける型の記述には意味はないので、プロパティとして行いたいことは「概要」(必要に応じて「説明」)を記述できれば良い程度になるかと思います。そうなると前述のコメントの書式に関するバリエーションにあまりこだわりはなくなります。
プロパティ情報
プロパティに関する表示ですが、「目次」と比較して情報量的には「説明」が増えているくらいですね。
書式的に「prop1」のパターンしか有効になっていない点は「目次」と同様です。
プロパティに関してあまりガッツリした解説を書くことはないと思うので「prop2」に適用したシンプルな書式(軽い概説を書く形)が一番適した形式のように思います。今のところそれが使えていないのが残念ですが、これも先に触れたようにプロパティに対してコメントとして書くべきことが概説くらいしかない(型の説明は不要)とも言える状況なので、そもそもあえて「@var」タグを使用する必要もなく、普通にコメント(概説)を書いた後に当該プロパティの定義を書けば良いだけのように思います。
メソッド情報
メソッドに関する表示ですが、「目次」と比較して情報量的には説明、引数に関する型や説明、戻り値の説明等が増えていて、この辺の情報は関数仕様として主要な内容になるので、関連するコメントに関してもしっかりと記述しておきたいところです。
「概要」と「説明」の出力に関してはクラスやプロパティと同様です。
引数の説明に関してはインデントが付いていて、戻り値の説明にはインデントが付いていませんが、これは引数は複数存在し得るのに対し戻り値は1つに限定されるので、特に引数に関しては見やすさを考慮すると説明にインデントを付けておきたかったのだと解釈しています(一方で戻り値の説明にインデントを付けたら不自然かと言うとそうでもないのですが、この辺は重要ではないですし、深く追求しないことにします)。
ある程度コメントを記述すれば上記のように表示されますが、所定の書式でのコメントが付加されていないメソッドに関してはどのように表示されるかと言う点を確認したのが以下です。
当然ながらプログラムの要素として存在している情報はそのまま表示対象になります。
これだけでも無いよりはマシですが、やはりある程度の解説は付けておきたいところかと思います。
と言うことで、最低限この程度はと思われる内容を反映した形が以下です。
総括
インストールも実行も簡単で、本機能を想定したコメントがない状態でも相応の出力はしてくれますので、まずは使ってみるのが吉かと。
なお、コメントとして記述する情報において、特にタグとして指定できるものはもっと色々とあります。例えば「@see」では合わせて参照させたい別のメソッドの解説箇所へのリンクが設定できるようなのですが、これも今のところ期待通りには機能していないので今回の内容には含めませんでした。先にも述べたプロパティのコメントの件も含めて色々と謎は残っています。
とりあえず使えるようになるまでは早いのですが、思ったように使えるようになるまでは相応に研鑽が必要なようです。
ただ、どんなツールでも100%期待通りと言うことはないと思いますので、現在使える機能をいかに有効に使うかと言う工夫も必要かと思います。
今回紹介したのはphpDocumentorに関するほんの一部ですし、前述したような問題も含めて本記事で紹介した内容が適切であるかが怪しい部分もありますので、下記本家サイトも合わせて参照してもらうことを推奨します(英語ですが)。