タイプヒンティング使用上の注意
intやstringといったプリミティブ型には使用できないタイプヒンティング。*1
そんなタイプヒンティングを使っていく中で知ったことを書き残しておきます。
とは言っても、マニュアルページの`User Contributed Notes`に書かれていることの焼き直しです。
PHP 5では、タイプヒンティング(Type Hinting)が導入されました。 これにより、関数は、 クラスの名前を関数プロトタイプの中に指定することにより、パラメータを オブジェクトが必ず指定されるようにすることができるようになりました。
つまり、
public function run(Hoge $arg);
と宣言された関数には、Hogeクラス(orインターフェイス)のインスタンス、もしくはその継承(or実装)クラスのインスタンスのみ渡せる、というのがタイプヒンティングの機能です。
型嗜好、インターフェイス嗜好*2の強い人 -- 代表、mit_tk :) -- などは非常に魅力的に映る気がします。たとえプリミティブや配列の型が指定できないとしても、それなりに。
ところが、マニュアルにある事柄を正しく書いていないがために、思わぬ落とし穴に落ちました。
それは「PHP5のタイプヒンティングとは関数呼び出し時に暗黙のうちにinstanceofでの比較が行われているだけ」のものなのです。
つまり何なんだといわれれば、関数内の実装はお構いなしということです。
ということを知らずに、以下のような罠にはまってしまったのですが・・・。
<?php interface TestInterface { public function gate(); } ?><?php class TestImpl implements TestInterface { public function gate() { echo "OK. :)\r\n"; } public function trap() { echo "KO. :(\r\n"; } } ?><?php class TypeHintingRunner { public static function doInterfaceTest(TestInterface $test) { // ------------- $test->gate(); $test->trap(); // <-- TestInterfaceでは宣言されていない } } ?><?php TypeHintingRunner::doInterfaceTest(new TestImpl()); // -------------- ?>
この結果は以下のとおり*3
OK. :) KO. :(
なお、クラスの親子関係でも同様のことがおこります。
<?php class TestParent { public function parent() { echo "TestParent is parent. :-)\r\n"; } } ?><?php class TestChild extends TestParent { public function child() { echo "TestParent is child!? :-0\r\n"; } } ?><?php class TypeHintingExRunner { public static function doExtendsTest(TestParent $test) { // ---------- $test->parent(); $test->child(); // <-- TestParent では宣言されていない } } ?><?php TypeHintingExRunner::doExtendsTest(new TestChild()); --------------- ?>
結果は以下のとおりになります。
TestParent is parent. :-) TestParent is child!? :-0
タイプヒンティングを御使用の際はくれぐれもお気をつけて・・・。