2026.09.23 泊まれる場所4,223 立ち寄りスポット59,693 営業中を確認したキャンプ場1,005 note を読む

PHPの「==」の罠|自分のCMSのコードに、同じ書き方が実在した

自分のCMSのコードに、PHPの==を使った古い分岐が残っていた。
危ない書き方だと聞いたことはあったが、実際に何が起きるのか、コンテナの中で試してみた。

僕が書いたコードを読み返していたら、見覚えのある書き方に手が止まった。

コンテナCMSの管理画面で、ディレクトリを選ぶポップアップの中身を作っている部分だった。
第2階層以降かどうかを振り分けるところに、こう書いてあった。

} elseif (isset($argData['directory_id']) && $argData['directory_id'] == 0) {

「TOP」を選んだときに通る分岐だ。
数年前の僕が書いたコードで、==(イコールふたつ)を使っている。
PHPの==は型を無視して比べにいく、危ない書き方だと聞いたことがある。
僕のコードは本当に大丈夫なんだっけ、と急に不安になった。

PHPの==は、数字と文字列の区別をあいまいにする

PHPの==(非厳密な比較)は、両辺の型が違っても、なるべくそろえてから比べようとする。
昔から有名なのが、数字の0と、中身のない文字列や適当な文字列を比べたときの挙動だ。

実際に、僕のCMSが動いているコンテナの中でPHPを直接動かして試してみた。

var_dump(0 == "abc");

結果はfalseだった。
少し前のPHPなら、ここはtrueになっていたはずだ。通用していた話が、もう通用しなくなっていることに、このとき初めて気づいた。

PHP8で、比較のルールが静かに変わっていた

調べてみると、PHP8.0で比較のルールが変わっていた。
数値と「数値に見えない」文字列を比べるとき、以前は文字列の方を無理やり数値に変換して比べていたが、いまは逆で、数値の方を文字列に変換してから比べるようになっていた。
0と"abc"を比べるなら、以前は"abc"を数値としてむりやり0に変換していたので一致し、今は0を文字列の"0"に変換してから比べるので一致しない。

同じコンテナで、もう少し試した。

var_dump(0 == "");
var_dump(0 == "0");
var_dump("0" == "");

結果は順に false・true・false だった。
空文字は「数値に見えない文字列」として扱われるので、0と一致しなくなっていた。
一方で"0"という文字列は「数値に見える文字列」なので、これまでどおり数値の0と一致する。
同じ==なのに、右側に何を書くかで結果がころころ変わる。==がずっと嫌われてきた理由に、あらためて腹落ちした。

僕のコードが無事だったのは、正しさじゃなくて「偶然」だった

僕の書いたdirectory_idの分岐に話を戻す。
この値がどこから来ているかを追いかけると、画面に並ぶディレクトリの行を作っている、すぐ近くのコードに行き着いた。

data-id="' . $childVal['directory_id'] . '"

ここで出しているdirectory_idは、データベースの数値をそのまま埋め込んだ、常に数字だけの文字列だった。
クリックされて送り返されてくる値も、"0"や"5"のような数字の並びにしかならない。
つまりこのコードが壊れなかったのは、==の挙動を理解した上で書いていたからではなく、送られてくる値がたまたま毎回「数字に見える文字列」だったから、というだけだった。

もしこのAPIに、未入力の空文字や、数字以外の文字列がまぎれ込む経路が一本でも増えたら、PHPのバージョンが変わった瞬間に分岐の結果ごと変わる。
今日まで一度もエラーにならなかったことと、これからも大丈夫なことは、別の話だと分かった。

動いているコードほど、===で書き直しておく

結局、このコード自体は書き換えなかった。
今の呼び出し方が変わらない限り危なくないと分かったし、コンテナCMSの中には同じ==の書き方があちこちに残っていて、一つずつ全部を===に直して回るのは今回の範囲を超えている。

ただ、次に0との比較を書くときは、素直に===を使うことに決めた。
===は型まで含めて比べるので、"abc"はもちろん、空文字も"0"という文字列も、数値の0とは別物として扱ってくれる。
型を保証したいなら、比較の前にis_numeric()を通して弾く、という一段階を足す方法もある。

PHPを書いていて怖いのは、エラーにならないままそっと結果が変わるバグだ。
以前書いたforeachの参照渡しで値が上書きされていた話も、原因は違うのに「構文としては完全に正しいまま、結果だけ違う」という点は同じだった。

「動いているから安全」と「型として保証されているから安全」は、似ているようでまったく別のことだった。
僕のコードが今回たまたま後者じゃなかっただけで助かった、というのが今回の一番の収穫だと思う。

まる子パパ

まる子パパ

会社員。受託開発のエンジニアで、その前は寿司職人・長距離運転手・農業。 技術ブログは、キャンプの合間に踏んだバグと、AI・Web開発の運用の失敗を書いています。 このサイト自体が、開発に携わっているCMS「コンテナ」の稼働中の実例です。 このサイトについて

—

ほかにも書いています

note・スタンプ

※noteは運営者が個人で書いているものです。

技術ブログ一覧へ戻る