タイプヒンティング使用上の注意

intやstringといったプリミティブ型には使用できないタイプヒンティング。*1
そんなタイプヒンティングを使っていく中で知ったことを書き残しておきます。
とは言っても、マニュアルページの`User Contributed Notes`に書かれていることの焼き直しです。

PHP 5では、タイプヒンティング(Type Hinting)が導入されました。 これにより、関数は、 クラスの名前を関数プロトタイプの中に指定することにより、パラメータを オブジェクトが必ず指定されるようにすることができるようになりました。

PHP: タイプヒンティング - Manual より

つまり、

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

タイプヒンティングを御使用の際はくれぐれもお気をつけて・・・。

*1:PHP5.1よりarrayが使用できるようになったとはいえ「何の」arrayかまでは見てくれないので事実上、使い勝手が向上していません。

*2:あえて嗜好と書いてます、あしからず

*3:この事象の原因を調べていて`User Contributed Notes`にたどり着いたときの気分そのままです :(