PHP5.3にてタイプヒンティングでスカラー型が使える!?(勘違い編)
by @dekokun on 2012/12/05 22:13
Tagged as: PHP.
まえがき
どうも、最近PHPとHaskellを学んでおります。今日はPHPネタ。php5.3でスカラー型のタイプヒンティングが使えるという勘違いが発生したお話。
発端
皆さんご存知の通り、PHP5.3までは、arrayもしくはオブジェクトの場合はタイプヒントが使用できるのですが、integerやstringなどのスカラー型に対してはタイプヒントは使えません。
しかし、ある日、私はPHPでタイプヒンティングについてググっていたら、下記のようなサイトを発見したのでした
そこに載っていたコードはこんなです。
function myfunc(int $i) {}
myfunc('abc');
-> PHP Catchable fatal error: Argument 1 passed to myfunc() must be an instance of int, string given, called in - on line 5 and defined in - on line 4
Catchable fatal error: Argument 1 passed to myfunc() must be an instance of int, string given, called in - on line 5 and defined in - on line 4
ふむふむ確かにエラーメッセージを見た限りではintかどうかを判別しているっぽい。実際に自分でもコードを動かし、同じエラーメッセージが出力されるのを見て、私は以下のように完全に勘違いしてしまいました。
おいPHPのタイプヒンティングってintやstringのような型では使えないんじゃなかったのか。マニュアルにそう書いてあるぞ。
— でこくんさん (@dekokun) 12月 4, 2012
php -r ‘function myfunc(int $i) {}; myfunc(“abc”);’ -> PHP Catchable fatal error:Argument 1 passed to myfunc() must be an instance of int
— でこくんさん (@dekokun) 12月 4, 2012
すみません、若干マニュアルをdisってしまいました。。しかし比較的すぐに誤りに気付き、myfuncに実際にint型のものを渡して実験してみました
php -r ’function myfunc(int $i) {}; myfunc(1);’PHP Catchable fatal error:Argument 1 passed to myfunc() must be an instance of int,
— でこくんさん (@dekokun) 12月 4, 2012
あれ、intでタイプヒントしているところにintの1を渡してもエラーになっているぞ!?
解決編
上記の正確なエラーメッセージはこんな感じ
PHP Catchable fatal error: Argument 1 passed to myfunc() must be an instance of int, integer given, called in Command line code on line 1 and defined in Command line code on line 1 PHP Stack trace: PHP 1. {main}() Command line code:0 PHP 2. myfunc() Command line code:1
ここが重要そうなメッセージ
Argument 1 passed to myfunc() must be an instance of int, integer given
このメッセージを見ると、「なるほどタイプヒントに使うべき型名はintじゃなくてintegerだったか」という感じがしますのでintegerに変えてもう一発
function myfunc(integer $i){};
myfunc(1);
PHP Catchable fatal error: Argument 1 passed to myfunc() must be an instance of Integer, integer given, called in /Users/dekokun/test.php on line 13 and defined in /Users/dekokun/test.php on line 12 PHP Stack trace: PHP 1. {main}() /Users/dekokun/test.php:0 PHP 2. myfunc() /Users/dekokun/test.php:13
あれ、駄目だ。エラーメッセージを読んでも、問題なさそう。だって、
must be an instance of integer, integer given
「integerである必要があって、integerが与えられた」って、全然エラーじゃないじゃないですか!!!
まぁ、聡明な皆さんはお気づきかと思いますが、上記エラーメッセージの最初にあるintegerはクラス名を表し、次のintegerはスカラー型のintegerのことを表しているわけですな。わかりづらいですね。
上記エラーを回避するためには、Javaのようにラッパークラスを作ってあげなくては…
というわけで、全くやるきのないラッパークラスを作りました。なんの役にもたちませんね。最低限の礼儀として不変型にしておきました(intは値がコピーされますし不変型になってますよね?)ので許してください。
class Integer
{
private $i;
function __construct($i)
{
$this->i = intval($i);
}
public function __get($key)
{
return $this->$key;
}
}
実際に使ってみた
function myfunc(integer $i){};
myfunc(new Integer(1));
エラーは出ませんでした。めでたしめでたし。(どうでもいいですが、クラス名って大文字小文字区別しないんですね。上記実験で初めて知りました。)
すべては私の勘違いだったのであった(PHPの出力するエラーがわかりづらいというのも間違いなくあると思うが)。
勘違い恥ずかしい。
— でこくんさん (@dekokun) 12月 4, 2012
あとがき
PHP5.4ではintなどに対してもタイプヒントが使えますので(2013/02/05 PHP5.4にタイプヒントが取り込まれるというのは間違い。参考:Scalar type hinting is harder than you think PHP5.5に乞うご期待!)スカラー型のタイプヒントを行いたい方々も多いと思いますが少々お待ち下さい。
ところで、関係ない話ですが、Rubyとか使ってる人たちは、タイプヒンティングがない世界でどのようにストラテジーパターンなどを実現しているのでしょうか。メソッドの存在を信じるしかないのでしょうか。
ダックタイピング全盛なRubyとかでメソッドを呼び出す時って、メソッドが存在することを信じて呼ぶんだっけ。まさか毎回メソッドを呼ぶ前にメソッドの存在確認をしてから呼ぶということはあるまい。PHPにはインターフェースとタイプヒンティングがあるが。
— でこくんさん (@dekokun) 12月 3, 2012
@dekokun メソッドミッシングとかあるし、信じるしかないんじゃないんですかね。よくわかんないけど。
— あやぴー(21)さん (@ayato_p) 12月 3, 2012
確かに言われてみればそうかも。
@dekokun railsのコード見ると、respond_to?よく見かける
— くろさん (@closer009) 12月 3, 2012
やはり必要なところではメソッドの存在を確認するのか…だるいな…
まぁ、タイプヒンティングがあろうが実行時エラーであることには変わらんが。
— でこくんさん (@dekokun) 12月 3, 2012
以上、最近、静的型付けの言語にとりつかれ気味な私なのでした〜
comments powered by Disqus