PHP 5.5で導入されたジェネレータ、便利です。
ジェネレータを使ったコードをいくつか書いたのですが、ふと気になることができました。
それは、ジェネレータ関数の「返値の型」は何なのか、ということです。
たとえば、
function stringGenerator()
{
yield 'string';
}
というジェネレータ関数を作ったとします。
ジェネレータ関数も関数なので、Docコメントで関数の説明を書きましょう。
/**
* 文字列をyieldするジェネレータ
*
* @return 型 返値の説明
*/
function stringGenerator()
{
yield 'string';
}
ここで疑問が生じます。
@return
タグの「型」には何と書くのがよいのでしょうか。
string
? それとも、\Generator
?
調査
過去の自分のコードを見返してみる
試しに私が過去に書いたコードのジェネレータ関数のDocコメントを見返してみると、
@return \Generator 返値の説明(※文字列をyieldする)
という記述と
@return string 返値の説明(※ジェネレータなので必ずforeach等で受けること)
という記述と両方あって、統一されていませんでした。
これまであまり深く考えたことがなかったので仕方ありませんが、これではいけません。統一した書式を決めなければ。
PHPのマニュアルを調べる
PHPマニュアルの「ジェネレータとは」のページの「Generator オブジェクト」の節によれば、
ジェネレータ関数を最初に呼んだときには、内部クラス Generator のオブジェクトを返します。
とのことなので、ジェネレータ関数の「返値の型」は\Generator
クラスのオブジェクトであり、Docコメントの@return
タグは
@return \Generator 返値の説明
とするのが正解な気がします。
しかし、yield
した値の型が分からないのは何とも不便です。
@return string 返値の説明
と書きたくなります。ですが、これだとreturn
文で文字列値を返す普通の関数と見分けがつきません。
phpDocumentorのマニュアルを調べる
ジェネレータ関数の「返値の型」が\Generator
オブジェクトであるとして、yield
される値をそのオブジェクトの要素と考えればどうでしょう。
配列であれば、型をarray
で表すこともできますし、要素が文字列の配列ならstring[]
、整数の配列ならint[]
のように、要素の型[]
の形で表すこともできます。
同じようにして、\Generator
オブジェクトと、yield
される値の型を同時に表すことはできないでしょうか。
そこで、phpDocumentorのマニュアルには何と書いてあるだろうかと、phpDocumentorマニュアルの「Types」のページを見てみました。しかし、それらしい記述は見つかりませんでした。
PSRを調べる
それではPHPのコーディング規約であるPSRに何かないかと見てみると、まだ草案(Draft)ではありますが、PSR-5でPHPDocの書き方を定めているとのこと。GitHubにある「PSR-5: PHPDoc」の草案を見てみると、使えそうなものが見つかりました。
それが、「Collections」です。
たとえば、\ArrayObject
というクラスのオブジェクトがあり、それが文字列型の要素を持っているとします。これの「型」を、Collectionsでは\ArrayObject<string>
と書くようです。
これを応用すれば、文字列値をyield
するジェネレータ関数の「返値の型」は、文字列型の要素を持つ\Generator
オブジェクトであると考えて、\Generator<string>
と表せそうです。つまり、Docコメントの@return
タグは、
@return \Generator<string> 返値の説明
と書けそうです。
結論
ジェネレータ関数のDocコメントの@return
タグは、
@return \Generator<yieldする値の型> 返値の説明
のように書くことにします。
補足
- 上記PSR-5の「Collections」では、オブジェクトの要素のキーの型を指定する方法も示されていました。たとえば、整数値のキーを持つ文字列値を要素に持つ
\ArrayObject
オブジェクトは\ArrayObject<int, string>
のように表すようです。これを用いれば、yield $key => $value;
というジェネレータ関数のDocコメントの@return
タグは、@return \Generator<$keyの型, $valueの型> 説明
のように書けそうです。 - この記事を執筆する(2016年4月8日)よりも前の2013年~2015年に、phpDocumentorのGitHubリポジトリにIssueが立てられ既に同様の議論が行われていたようです。こちらでも、本稿と同様
@return \Generator
か@return \Generator<string>
のように表すのがよいだろうという話のようでした。中にはGenerator::current()
の返値の型V
、Generator::key()
の返値の型K
、Generator::send()
の返値の型S
、Generator::getReturn()
の返値の型R
を使ってGenerator<V, K, S, R>
と表すべきだとの意見もありましたが、さすがにそれは面倒臭いなという印象を受けました。また、@return
タグの代わりに@yield
タグを新設すべきだという意見も出ていましたが流れてしまったようです。なかなか難しい問題ですね。 -
PSR-5がまだ「Draft」(草案)であることには十分注意する必要があると思います。
2017年10月に、PSR-5の策定状況が「Draft」から「Abandoned」(放棄)に変更されました。
2018年9月26日に、PSR-5は再び「Draft」になりました。また、PSR-5は分割され、PSR-5とPSR-19の2つのPSRになりました。分割後のPSR-5はPHPDocの書式を規定し、PSR-19はタグの一覧を規定します。
参考文献
- 『PHP: ジェネレータとは - Manual』
-
『Types — phpDocumentor』
本記事執筆時は https://www.phpdoc.org/docs/latest/guides/types.html を参照していたのですが、2018年9月2日現在では https://docs.phpdoc.org/guides/types.html にリダイレクトされるようです。書かれている内容が記事執筆時とは異なるかもしれません。 -
PHP-FIG『PHP Standards Recommendations - PHP-FIG』
PSRとその策定状況の一覧 -
PHP-FIG『PSR-5: PHPDoc』
PHP-FIGのGitHubリポジトリにあるPSR-5の草稿。なおPHP-FIGのサイトのPSR一覧表では、こちらではなくphpDocumentorによるフォークの方にリンクされているようです。これはphpDocumentorのフォークを参照した方が良いということでしょうか。ちなみに本記事を書いた時にはphpDocumentorのフォークの方を見て執筆し、その後本家PHP-FIGのGitHubリポジトリの方にリンクを張り直しました。当時は両者とも内容にほぼ差は無かったのですが、2018年9月2日現在ではかなり差が出ています。phpDocumentor版は最終更新が2016年2月(2年前)なのに対し、PHP-FIG版は今年2018年7月に入って大きな変更が加えられています。2018年9月にはPSR-5とPSR-19とに分割されました。 - PHP-FIG『PSR-19: PHPDoc tags』
- mvriel・他『Yield? · Issue #5 · phpDocumentor/fig-standards · GitHub』
関連記事
-
tadasan『2018年のPHPDoc事情とPSR-5』
本記事とは直接関係ありませんが、PHPDocの状況についてまとめてくださっています(2018年3月現在の情報)。この記事でPSR-5が放棄されたのを知りました。