PSR-19: PHPDoc tagsを予習してみよう

本稿はBASE Advent Calendar 2019 🎅🎄の14日目です!

f:id:o0h:20191210142022p:plain

こんにちは、Product Divisionの金城(@o0h_)と申します。
10月にBASEに入社しまして、ブログ投稿は本稿が初となります。よろしくおねがいします。

さて、早速掲題の件に入らせていただきます。

PSR, PHP-FIG

「PSRはPHP-FIGが策定し運用する一連の規格であり、その19号はPHPDoc Tagsについての決め事」です。
PSRには色々なレイヤーのルールが ─時には「本来の主旨を超えている」という批判をもさえ巻き起こしながら─ 策定されています。1

PHP-FIGは、元々はオートローディング活用のための取り組み2を契機として作られた団体です3。つまり、元来から「PHPの書き方」に強い関心を持っていたとも言えます。
PSRの「0番目がAutoloading Standard」という事だけに留まらず、「続く1番目はBasic Coding Standard」「2番目がCoding Style Guide」であることからも「初期にフォーカスしていた領域がどこであるか?」というのを伺い知れるのではないでしょうか。

更に言い加えると、PSR-0,2のみが現状でDeprecatedステータスに配置されています4
すなわち、「より洗練した規格を後から作り、採択している」のが、AutoloadingとConding Styleに関する話題であるという事です。5

f:id:o0h:20191210142504p:plain

そして、2019年12月現在で「Draft」にあるのが「PSR-5: PHPDoc Standard」と「PSR-19: PHPDoc tags」です。 f:id:o0h:20191210142525p:plain

ある意味で「実装のし易さ」と同じくらいに「コードを読みやすく・違和感なく」運用していく事に重きを置いていた団体が、「doccomment/tagの書き方について未だに標準化できていない」・・というのは、ちょっと面白いと思いませんか?一体どんな経緯があったのでしょうね。

そんな訳で、本稿では「PSR-19なんなのよ」という事について見ていきたいと思います。

PHPと「doc comment」

一昔前まで、PHPでdoc commentというと「いろんな方言がある」という印象を抱いた方も多いと思います。私もその1人です。 (以下、doc commentについて「PHPDoc」6と表記することとします。)
「phpDocumentorのそれに従おう」「いやPhpStormに合わせりゃよくない?」「Phanが」「PHPStanが」といったソレです。

もちろん、それぞれ目的が違うものであり、自分達の目的を果たすために独自の拡張的な記法なども導入していく事には必然性があります。完全に統一され得るものではないでしょう。・・とはいえ、いち利用者的な観点からすると「似たような事は同じ書き方で出来ればいいのにな」と思う訳です。まさに「相互運用性のための決まり事」があると良かろうなぁという場面で、PSRに目が向きます。
なるほど、それがPSR-5です。

しかしこのPSR-5、提案自体は数年前になされたものの、「広範に支持されるような勧告」といった地位にまでは至りませんでした。結局は「色々な書き方がある内のいち方言」のような雰囲気のままです。そうして遂には議論が停止、草案は「STATUS:ABANDONED」7に・・・・
なったのですが、これが再提案されて「DRAFT」化8、今に至ります。そういった訳で「5番」などという非常に若い番号が未だに議論されている訳ですね。

そして新生PSR-5はEditor/Working group membersとして「phpDocumentetor」「PhpStorm」「Psalm」「PHPStan」の関係者をそれぞれ迎えています。これは現実的な落とし所を見つけつつ、各ツールのサポートの観点からも実用性を伴う状況が実現できるのではないでしょうか。期待ですね!

昨年時点でのPSR-5の状況については、こちらの記事に大変よくまとまっておりました。ご参照ください。 qiita.com

PSR-19

PSR-5の「復活」にあたって、一緒に提出されているのがPSR-19です。
これは、PSR-5にまとまっていた内容を2つに分けようという提案9に基づくものになります。

PSR-5についてはPHPDocのフォーマットを、PSR-19についてはタグの種類を決める内容になります。二者が分割された動機については「PHPdocのフォーマット(PSR-5)を一般的な内容にしつつ、タグの内容(PSR-19)は拡充しやすくする。また、将来的にはPHPDoc外でも利用可能になることを見越して」と説明されています。

The rationale behind the splitting is to make it easier to add new tag dictionary PSRs in the future and also make PHPDoc more generic so it can be used for non-docblock standard such as Doctrine-style annotations.

例として Doctrine-style annotations が挙げられていますが、例えば

<?php
/**
 * @MyAnnotation(myProperty="value")
 */
private $bar;

のようなものでしょうか。

ということで、本稿は(既にその内容には見慣れているであろうPSR-5よりも)PSR-19の中身はどうなっているのかな?と興味を持って調べてみるものです。

中身を見てみる

現時点でのPSR-19のproposalはこちらから確認できます。
fig-standards/phpdoc-tags.md at 2668020622d9d9eaf11d403bc1d26664dfc3ef8e · php-fig/fig-standards · GitHub

また、GitHub上での議論(PR)についてはラベルで辿ってみるのが良いでしょう。
Pull Requests · php-fig/fig-standards · GitHub

中身を見てみると、5つのセクションに分かれています。

  1. PHPDoc standard(PSR-5)を補完するのが主目的であること
  2. MUSTやSHOULDなどの語についてはRFC 2119に準ずること (いつものやつですね)
  3. 用語の定義等はPSR-5に準ずること
  4. inheritDocの扱いについて
    • 改変無しで全体を引き継ぐ場合は @inheritDocを、インラインで埋め込む場合は {@inheritDoc}を用いることなど

そして第5セクションが「タグ」の定義です。
現時点では以下のリストが掲載されています

  • @api
  • @author
  • @copyright
  • @deprecated
  • @internal
  • @link
  • @method
  • @package
  • @param
  • @property
  • @return
  • @see
  • @since
  • @throws
  • @todo
  • @uses
  • @var
  • @version

このうち、 @method @param @property @return @throws @var などは、すっかりお馴染みなのではないでしょうか。クラスやメソッドの機能に関連するもので、PhpStormなどのIDEやPHPStan・Phanといった静的解析を利用している場合には欠かせません。

残る内容について、その書式や意味を掻い摘んで見ていきましょう。
独断にて、関連するタグごとに以下の4つの群にまとめながら取り上げていきます。

  1. 利用者の想定・提供範囲に関わるもの
  2. 著作者・権利に関わるもの
  3. 関連情報・参照情報に関わるもの
  4. バージョン・更新に関わるもの

利用者の想定・提供範囲に関わるもの

論理的な空間・括りと、誰に向けて提供しているものか?に関する情報です

@package

@package は、「論理上の区分」を示すために用いるものと説明されています。
PHPの持つ言語機能であるNamespaceと近い概念を取り扱うものであり、実際に「階層は(Namespaceと同様に) \ で区切って記述する」と規定していたり、「Namespaceと完全に一致する場合に@packageタグを利用することは推奨しない」と言及されています。
あくまでNamespaceで表現される「機能的な区分」とは別に、論理上の区分を明らかにしておきたいという場合に利用されるものになります。

@api

@api は、 「primary public API of a package」に付与されるものと説明されています。
publicなメソッドの中でも、そのパッケージ外から利用される ようなものを区別するためのものです。

@internal

@internal は、@apiと対象的に「only for use within the application, library or package to which it belongs.」と説明されています。
「primary public API of a package」に付与されるものと説明されています。
このタグが規定されている動機についても説明していて、

  • 1つには「ライブラリの作者が、ユーザーに対して"破壊的な変更を含む可能性がある"ということを示すため」
  • 2つには「静的解析時に、パッケージ外からの利用を警告できるようにするため」

というポイントを取り上げています。
実際に、PhpStormでは違反的な利用に対して警告を出すようになっています。 f:id:o0h:20191210143841p:plain

著作者・権利に関わるもの

該当箇所に関する権利等に関する情報です

@author, @copyright

@authorは、その作者や重大な改修を行った人を示すものと説明されています。
@copyrightはそのコードに対する著作権と説明されています。
(特異な内容はないので、あとは割愛)

関連情報・参照情報に関わるもの

該当箇所の実装の理解を助ける情報です

@link

@link は関連する情報を示すものと説明されています。
( a custom relation between the associated "Structural Element" and a website, which is identified by an absolute URI. と書かれていますが、ここは「Webサイト」というより「情報の掲載されているWebサイト」といったニュアンスで解釈するほうが意味する所に近いのかな?と思います)
@linkタグを利用する場合には、絶対URIで示すように指示されています。
また、インラインでの利用も想定されています。

<?php
/**
 * This method counts the occurrences of Foo.
 *
 * When no more Foo ({@link http://example.com/my/bar}) are given this
 * function will add one as there must always be one Foo.
 *
 * @return int Indicates the number of items.
 */
function count()
{
    <...>
}
@see

@see は関連するリソースを示すものと説明されています。
@linkはURIだったのに対し、こちらはURI以外のリソースを利用することも可能です。例えば他のクラスやメソッドがあり、そのような要素を指す場合には「FQSEN」を利用するようにと規定されています10

また、関連先のリソースとの関連性(=なぜsee なのか?)という説明を載せるべきだとされています(SHOULD)。
@link が対象リソースの説明について「してもよい」とされているのに対して(The @link tag MAY have a description)、こちらが「べき」なのは、「ドキュメント」以外のリソースも扱えるからでしょうか。

@uses

@uses は利用される要素やファイル名を示すものと説明されています。
ただし、記述できるのは同じプロジェクト内のリソースに限るとされています。そのために、URI等の利用は禁止です。(システム外部のリソースに言及する場合は、 @see を利用することが出来ます)

例として、「コントローラーのメソッドに対して、描画されるView/templateファイル名を示す」といった内容が挙げられています。

これは、どういった場合に嬉しいのでしょうか?
例えば動的な関数の呼び出しを行っている場合やマジックメソッド経由で利用しているメソッドについて、@usesを用いてエディタや静的解析ツールに「実際に使っているよ!」ということを知らせることが出来ます。

余談ですが、 @used-by については、現時点ではPSR上での規定はありませんでした。

バージョン・更新に関わるもの

現行の状態や更新履歴といった情報です

@version

@versionは、現在のバージョン情報とそのバージョンで提供された内容を示すものと説明されています。
セマンティックバージョンを利用して言及すること、説明文を加えることを推奨されています(RECOMMENDED)。

@since

@sinceは提供が開始された(もしくは内容が現行の状態に変更された)バージョンを示すものと説明されています。
(@versionと同様に)セマンティックバージョンを利用して言及すること、説明文を加えることを推奨されています(RECOMMENDED)。

例えば「2.0.4から、第3引数が追加された」といった内容などは@sinceで示すのが良さそうです。
「提供のタイミング」「バージョニング」に関わるということで、@versionと混同することもあるかもしれませんが、「今がどのバージョンであるか」といった情報には@sinceによって提供されるべきではない(@versionを利用する) と説明されています。

@deprecated

@deprecatedは廃止予定・将来のバージョンにて削除されるものを示すものと説明されています。
廃止予定とされた理由について、説明文を加えることも可能とされています(MAY)。また、代替となる手段が用意されている場合には @see タグによって示すことが推奨されています(RECOMMENDED)

@todo

@todo は実施される(開発・改修される)べきことを示すものと説明されています。
このタグには必ず説明を入れなければならない(MUST)とのことです。
(todoが「バージョンに関するもの」か?というと、「将来的な変更に関するもの」という意味では、この群に属するのではないでしょうか)

その他の提案内容について

ここに列挙したタグは既にproposalにコミットされているもので、ある程度の同意を得たものという扱いになります。
もちろん、「今はリストに含まれている」というだけであり、最終的には削除されるかもしれないし他のタグが追加される可能性もあります。(そもそもPSR19自体が採択されていないですからね)

また、興味深い議論としては、PSR-5の領域ですが PR#1191にて「no-return type」についての提案がなされています。

github.com

これは、TypeScriptでいうneverに相当するもので、exit()やtrigger_error()の発火により「呼び出し元に処理を戻さない」性質を説明するものです。PHPStanやPsalmでも、それぞれ@return never, @return no-returnという表記によって扱われています。

PSR-19についても、いくつかのタグの追加に関するPRが作成されている状況です。

まとめ

ざっくりと、現時点でPSR-19として規定されているPHPDocのタグについて見ていきました。 多くのタグは「定番化」しているもので、既存のコードに対して衝撃を与える事はないように思います。実際に、PSR自らがInterfaceの定義等において既に利用している状況が見受けられます。

PSR自体は決して「PHPを書く際に従わなければならない」ものではなく、「フレームワークやツールを作る時に準拠しておくことで、何か良いことがあると良いよね」程度のものです。そのため、実質的なデファクトスタンダード(今だとphpDocumentorになるでしょうか)が存在すれば、大きく困ることもありません。実際に、過去に議論が停滞したのも「困らなかったから」という側面はあるでしょう。
ですが、個人的には、先述の通り「PSR自体がPHPDocを利用している」こともあり、やはり明確な定義を持つべきなのではないかな?と考えています。

繰り返しになりますが、PSR-5,PSR-19はその内容がまだ確定しておらず、現在進行系で議論がなされている状況にあります。なので、ここにまとめた内容も採択時には変更されているかもしれません。
それでも、その内容や議論に興味を持つことは、今っぽいPHPを考える上で多少なり意味のあることではないかと考えます。 PSRはその性質的に「最大公約数」を示す力も持っていると捉えているからです。
皆さんもぜひ、PSR-5/PSR-19の内容を眺めながら「ドキュメント性能の高いソースコード」について想いを馳せてみるのはいかがでしょうか!

ここまで読んでいただきありがとうございました。 明日は、Data Strategy Groupの齋藤とFrontend Group加藤が更新予定です!✨


  1. 前身となる「PHP Standards Group」は「PHP本体への提案」を目指して結成されたグループです。しかし「標準化」に際しては考慮すべき点が多く困難性があったため、「より柔軟な提案をするために、言語仕様そのものではなく、その上に乗るプラクティスとして規約を求めよう」と方向転換をして今のPHP-FIGが生まれました。そのため、「広い範囲を扱う」というのは、サジ加減はあれど、FIGにおける本質的な振る舞いだと私は解釈しています。

  2. https://wiki.php.net/rfc/splclassloader

  3. 歴史については、こちらの記事に詳しいです The Past, Present and Future of the PHP-FIG — SitePoint

  4. PSRのステータスや議論の進行については、https://www.php-fig.org/bylaws/psr-workflow/ をご覧ください

  5. と言いつつ、「PSR-3を踏まえて簡素化した(利便性を高めた)PSR-16:Simple Cache」や「PSR-7をコアとした一連のHTTP関連Interface(15,17,18)」もあるので、「特定の領域について深める・広める」という事自体は他にも積極的に行われています。

  6. https://github.com/php-fig/fig-standards/blob/2668020622d9d9eaf11d403bc1d26664dfc3ef8e/proposed/phpdoc.md#3-definitions

  7. GItHub上でステータスを変更されたソースはこちらです https://github.com/mcneely/fig-standards/pull/1

  8. https://github.com/php-fig/fig-standards/pull/1078

  9. https://groups.google.com/d/msg/php-fig/5Yd0XGd349Q/w-uTRA2nEgAJ

  10. FQSENについては、PSR-5を参照してください https://github.com/Chofoteddy/fig-standards/blob/e89924d320c269678fe3f5822c9bf9ef95db1af0/proposed/phpdoc.md#3-definitions