emptyとissetについて(今一度おさらいしておきたい)

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
だいたいそんなか〜んじ〜♪(参照:ギャグ漫画日和)

この記事を三行にまとめると

emptyは0でもtrueを返す
issetはnullでもfalseを返す
これでも五年近くやってきました
PHPで開発していて、よく使う関数ランキングみたいなものを作ったら、おそらく上位に食い込むであろう、empty()とisset()。僕もよく使います。特にempty()はしょっちゅう使う。カラスが鳴かない日はあっても、PHPのコードを書いていてempty()を記述しない日はないくらい……? 主にif文で使うことが多いですかね。

まあ誰でもよく使うものだから、今さらこの二つの関数についての働きをここで書く必要もないんですけど、たま〜に、予期せぬ動きをすることがある。システムのバグ的なことではなくて、emptyやissetの動きについて、実は知らなかった部分があったっつー話でして……今日はそれを、あえてここで明示しようじゃないかと。



emptyは0でもtrueになる

PHPのマニュアルによると、emptyの説明はこんな風に書いてある。

変数が空であるかどうかを検査します。 変数が空であるとみなされるのは、変数が存在しない場合や 変数の値が false に等しい場合です。 empty() は、変数が存在しない場合でも警告を発しません。

変数が存在しない場合のチェックもできるってところが、使い勝手の良いところでしょうか。ぶっちゃけ僕は大半の場合、isset()で十分なところでも、empty()を使って判定することが多い。何でかって言うと、まだPHPを覚えたての頃、emptyとissetの違いがよく分からなくて、だったら全部emptyって書いときゃ間違いないだろうと思ってたから。

変数の値がfalseに等しいっていうのも昔はよく分かってなかったんですが、基本、この辺の値は全部falseに等しいっす。

$var = ''(空文字)
$var = array()(空の配列)
$var = null
$var = false
$var = 0

厳密にはもうちょっとありますが、まあその辺を知りたきゃマニュアルとかを見てもらうとして、微妙に厄介なのが、変数の中身が「0」だったときにも、falseに等しくなるってとこなんですよね。数値の0でも文字列の0でも同じ。

プログラマになってもうそろそろ五年経ちますけど、一年くらい前までは、あまりここを気にする必要がなかったんですよ。例えばフォームなんかを作成していて、0が返って来るようなところはほとんど、1だったらtrue、0だったらfalseっていう判定くらいにしか使ってなかったら。だからあまり気にしてなかったんです。

でも、今はフォームの値が0になる場合があるんですよね。ほら、例えば数学の問題演習みたいなサイトを作ったりしたら、答えが0になる場合もあるじゃん?

僕は仕事で学習サイトみたいなものを運用しておりまして、そのサイトでは、空の値が飛んで来たら「未解答」っていう判定にしてるんですよ。同じ不正解でも、答えを間違えてるのか、解答をパスして不正解なのかを判別できるようにしないといけないので。

最初は空かどうかを判定するんだから、いつも通りemptyを使ってりゃ良いと思っていたんですがね。あるとき、答えを入力してるのに未解答になっちゃうっていうクレームが来まして……バグが出てるわけじゃなかったんでしばらくは何がいけないのか全っ然分からなかったんですが、改めてemptyの役割を確認したんですよ。そしたら0でもfalseになっちゃうってのに気づきまして、ああこれか、と。

いや〜、四年もそのことに気づかなかったよ。ちゃんと勉強してないと、こういうことが起こるっていう良い教訓ですね。

初めは、0のときにtrueが返って来てほしいから『!is_null($var)』って書けばええやんって思ったんだけど、これだと今度は0だけじゃなくて、空文字のときもtrueが返って来ちゃうのよね。変数の中身がnullのときはfalseになるんだけど。だからそれはそれで僕の望む挙動にはならない。

ってことで結局、自前で0のときはtrueを返すようなemptyのオーバーライド的な関数を作れば良いと思って、実際作って、ことあるごとemptyの代わりにそっちの関数を使ってたんですが、そうなると今度は、変数が定義されてないときに、警告が出ちゃうんですよね。

何かemptyの第二引数にパラメータを渡せたりできれば良いんですけどね。例えばtrim()とかは、第二引数に削除する文字を指定できたりするじゃないですか。あんな感じで、『empty($var, 0)』とかって書いたら、0はfalse扱いしないみたいな感じにできたら良いんですけど。

何か良い解決方法ないかなぁ。emptyの機能をそのままに、特定のときだけ、特定の値は回避できるような。変数は未定義でもスルーできるような。



issetは変数があってもfalseになることがある

PHPのマニュアルによると、emptyの説明はこんな風に書いてある。

変数がセットされており、それが NULL でないことを調べます。

こちらに関しても、僕はずっと、変数がセットされているかどうかだけを調べる関数だと思ってたんで、NULLだとfalseになるってのを知らなかったんですよ。中身がどうあれ、変数が存在さえしていればtrueが返って来るんだと、そう思っておりやした。

先日も、isset()を使って判定していたのに、何度やってもfalseが返って来るから、おかしいな〜変数はちゃんとセットしてあるのにって思いながら、他のところに原因があるんじゃないかってあれこれ無駄に時間を使って調査してたんですけど、原因は変数にnullが入ってたからでした。

参っちゃいますよね〜、ほんまに。






知ってる人にとっては当たり前のことですからね。特別難しい技術に関する記事とかではありやせん。

この記事を読んだら「お前、五年もPHPを触ってて、そんな当たり前のことを知らなかったのかよ」って怒られちゃいそうですが、そんな当たり前のことを知らなかったんです、僕は。ふひひさーせん。

まあ、おそらくは似たようなものって、他にもあると思うんです。実はそんな基本的なところを知らなかった、みたいなね。

今回の教訓としては、いつまで経っても勉強は必要ってことですかね。

時間が経てば、仕様とかも変わって来ますからね。そのうち本当に、emptyにパラメータをセットできる日が来るかもしれないし。そうなったときにそれを知らないと、損ですもんね。
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください