hnwの日記

PHPメソッドのprototypeとは何か

なんとなくPHPマニュアルを眺めていたところ、リフレクション機能に下記のようなメソッドを見つけました。

ReflectionMethod::getPrototype — メソッドのプロトタイプを (存在すれば) 取得する


http://php.net/manual/ja/reflectionmethod.getprototype.php


特定のメソッドについて、「プロトタイプ」の情報を返してくれるもののようです。しかし、この説明だけでは何の値が返ってくるのか想像がつきませんよね。本稿ではこのメソッドについて調べてみます。

「プロトタイプ」の意味

そもそもPHPでプロトタイプとは何を意味するのでしょう?PHPの文脈では耳慣れない単語のような気がします。


私も全くわからなかったのでPHPのCソースコードを眺めてみたところ、プロトタイプとは関数の型宣言の意味だとわかりました。Cの「関数プロトタイプ」と同じ使い方です。


この型宣言はインターフェースと継承の実現で利用されています((他にはClosure::fromCallable()でも使われています))。インターフェースを実装した場合、実装したメソッドはインターフェースと同じ個数の引数が必要で、全て同じ型でないといけません。これはまさに関数の型チェックそのものです。継承でメソッドをオーバーライドした場合も同様で、親メソッドの型と矛盾しないかどうかのチェックが走ります。

プロトタイプを確認する

では、実際に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ソースコードを読むときに少しだけ役立つかも知れません。