てきとうなさいと べぇたばん

PHPのビルトイン関数は、引数の取り扱いを間違えると返り値がNULLになることが「多い」

マニュアルにない返り値

ビルトイン関数の引数の扱いを間違えると、予想だにしない値が返ってくることがある。PHPの公式のマニュアルに、こんな説明がある。

注意: 関数へのパラメータとして関数が想定しているのとは異なるものを渡した場合、 例えば文字列を想定しているところに配列を渡した場合などの場合は 関数の返り値は未定義となります。たいていの場合は NULL を返すでしょう。しかしこれはあくまでも規約にすぎず、 これに依存することはできません。

見つけた限りでは、strpos(http://jp.php.net/strpos)を始め、大体はNULLを返す。NULLが返ってくる可能性があることがstrposのページに書いていないにもかかわらずである。しかし、mb_strpos(http://jp.php.net/mb_strpos)で引数の数を間違えるとFALSEが返る。

strpos

$ php -r 'var_dump(strpos());'
PHP Warning:  strpos() expects at least 2 parameters, 0 given in Command line code on line 1
PHP Stack trace:
PHP   1. {main}() Command line code:0
PHP   2. strpos() Command line code:1
NULL

mb_strpos

$ php -r 'var_dump(mb_strpos());'
PHP Warning:  mb_strpos() expects at least 2 parameters, 0 given in Command line code on line 1
PHP Stack trace:
PHP   1. {main}() Command line code:0
PHP   2. mb_strpos() Command line code:1
bool(false)

ext/mbstring/mbstring.cを読むと、多分、「大体」FALSEが返ってくるらしい。zend_parse_parametersがFAILUREを返すとき

if (zend_parse_parameters(...) == FAILURE) {
    return;
}

NULLを返すが、RETURN_FALSEマクロになっているところがある。

if (zend_parse_parameters(...) == FAILURE) {
    RETURN_FALSE;
}

こうなっていればFALSEが返ってくる。PHPのバージョンは5.2.17と5.3.22。同じ挙動だった。

「しかしこれはあくまでも規約にすぎず、これに依存することはできません。」とある。確かに、NULLが返ってくるかもしれないし、そうではないかもしれない。

E_WARNINGは結構重い

時々、エラー制御演算子(@)でE_WARNINGを抑制するコードを見かける。例えば、file_get_contents(http://jp.php.net/file_get_contents)。ファイルを読み込むことに失敗したとしても、お構いなしに無視というケースとか。引数の数を間違えるとFALSEではなくNULLが返ってくるのだから、FALSEで失敗したと判断しているプログラムはNULLに対応できないことになる。

$cache = @file_get_contents();
if ($cache === FALSE) {
    // よみこめなかったので回避手段のつもりが…
}

想定している値・型であることを意識することはもちろん、E_WARNINGは抑制してはならない。E_WARNINGでプログラムは停止しないが、意外と重たいエラーだとは思う。

マニュアルに書いていないのに、NULLが返ってくるかもしれないと考えておかないといけないらしい。

PHPって知れば知るほどよくわかんなくなる

不思議な話だなぁ。

PHPってなんとなく使っているとなんとなく動くんだけど、ソースコードを覗いて動きを確認してPHPの厳格な挙動を知ろうとする瞬間、何が正しいのかわからなくなる。。。