なんとなくPHPマニュアルを眺めていたところ、リフレクション機能に下記のようなメソッドを見つけました。
ReflectionMethod::getPrototype — メソッドのプロトタイプを (存在すれば) 取得する
特定のメソッドについて、「プロトタイプ」の情報を返してくれるもののようです。しかし、この説明だけでは何の値が返ってくるのか想像がつきませんよね。本稿ではこのメソッドについて調べてみます。
そもそもPHPでプロトタイプとは何を意味するのでしょう?PHPの文脈では耳慣れない単語のような気がします。
私も全くわからなかったのでPHPのCソースコードを眺めてみたところ、プロトタイプとは関数の型宣言の意味だとわかりました。Cの「関数プロトタイプ」と同じ使い方です。
この型宣言はインターフェースと継承の実現で利用されています*1。インターフェースを実装した場合、実装したメソッドはインターフェースと同じ個数の引数が必要で、全て同じ型でないといけません。これはまさに関数の型チェックそのものです。継承でメソッドをオーバーライドした場合も同様で、親メソッドの型宣言と矛盾しないかどうかのチェックが走ります。
では、実際にReflectionMethod::getPrototype()
の動作を確認していきましょう。次のようなコードを動かしてみます。
<?php interface Foo { public function func1(int $x); } abstract class Bar implements Foo { public function func1(int $x) { } abstract public function func2(int $x, double $y); private function func3() { } } class Baz extends Bar { public function func1(int $x) { } public function func2(int $x, double $y) { } protected function func3() { } } class Baaz extends Baz { public function func2(int $x, double $y) { } public function func3() { } } $cl = new ReflectionClass(new Baaz()); $methods = $cl->getMethods(); foreach ($methods as $mt) { $proto = $mt->getPrototype(); printf("method=%s::%s(), prototype=%s::%s() \n", $mt->getDeclaringClass()->getName(), $mt->getName(), $proto->getDeclaringClass()->getName(), $proto->getName()); }
これを実行すると次のような結果になります。
method=Baaz::func2(), prototype=Bar::func2() method=Baaz::func3(), prototype=Baz::func3() method=Baz::func1(), prototype=Foo::func1()
これはBaazクラスの全メソッドについて、それぞれのプロトタイプを表示したものです。
Baaz::func2
のプロトタイプは抽象メソッドのBar::func2
です。型チェックをするだけなら親のメソッドであるBaz::func2
がプロトタイプになっていても良い気がしますが、どうやら親子関係として一番上位で定義されたものがプロトタイプになるようです。
Baaz::func3
のプロトタイプは親のprotectedメソッドであるBar::func3
です。親の親には同名のprivateメソッドが定義されていますが、これは子にもエクスポートされないので単に無視されています。
func1
のプロトタイプはインターフェースであるFoo::func1
となります。これもやはり最上位で定義されたものがプロトタイプになっています。
PHPメソッドのプロトタイプとは型宣言のことであり、クラスやインターフェースの親子関係において最上位で定義されたメソッドが実体となります。これは主に子メソッドの型チェックに利用されます。
普段のPHPプログラミングでは全く役に立たない知識だと思いますが、PHPのCソースコードを読むときに少しだけ役立つかも知れません。
*1:他にはClosure::fromCallable()
でも使われています