Chonaso's Commentary

InternetやIT技術などについて知ったこと、試したこと、考えたことを書いていきます。

PHPのfalseの闇

PHPで闇深いといわれる代表選手は == ですが、これについては使わなければいいだけですので、そこまで目くじらを立てる必要はなさそうです。

個人的に闇深いと感じたのが false の使われ方です。

規定の型 or false

PHPの関数によくある返り値が規定の型 or falseというパターン。 代表的なのがstrpos

返り値

needle が見つかった位置を、 haystack 文字列の先頭 (offset の値とは無関係) からの相対位置で返します。 文字列の開始位置は 0 であり、1 ではないことに注意しましょう。

needle が見つからない場合は FALSE を返します。

これがなかなか扱いづらいのです。

アノテーション

そういった関数のラッパー的な関数を書くときにこの流儀を引き継ぐと、アノテーションを書くときに

/**
 * @return int|boolean
 */

みたいな感じになるわけですが

といった心の叫びが漏れてしまってました。

Union Type V2では int|false みたいな書き方が許されてるようですね。


お前どっちのfalseよ!?

つまりRedisにbooleanの値が格納されていることが期待できる状況でfalseが返ってきた場合、値がセットされていないのかfalseという値が取れたのか区別がつかないわけです。

これに対してこういうコードを書きたくなるわけですが、これはgetしたときには値が消されている可能性が否定できないのでアウトです。

$value = $redis->exists('hoge') ? $redis->get('hoge') 
                                : getFromDatabase('hoge');

ワークアラウンドとして、boolean型の値を取得したい場合は一度自動シリアライズを無効にするってところでしょうか。

// デシリアライズしない
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
// redis上でbooleanのfalseが格納されていたら b:0 が返ってくる、存在しなかったら booleanのfalseが返ってくる。
$ret = $redis->get('hoge');
// シリアライズ設定戻す
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
// Redisから値が取得できている場合は手動でデシリアライズする
$value = ($ret !== false) ? unserialize($ret) : getFromDatabase('hoge');

こういった事情からこんなコードを書きたくなってしまうわけですが、

if($ret = $redis->get('hoge')){
    $value = unserialize($ret);
}else{
    logger.debug("******************");
    $value = getFromDatabase('hoge');
}

これはさすがにIDEにはやめろと言われます。

とりあえずfalseでなきゃOK的な

他にもあるかもしれませんが。

おまけ

これは true null にも言えることなんですが、大文字・小文字の区別が無いという点もなかなか闇深事案ですね。

PHPマニュアル内でも一貫性が無いという徹底ぶりです。