str_contains系関数でstrposの===false比較から卒業する
str_contains・str_starts_with・str_ends_withはPHP8で追加された関数で、文字列に語が含まれるか・前方一致・後方一致を、真偽値でそのまま判定できる書き方ですね。
strposの比較はなぜ面倒だったのか
「文字列に特定の語が含まれるか」を調べるだけなのに、PHP7 までは strpos を使わざるを得ず、その戻り値の扱いが地味に厄介でした。マッチ位置を返す関数なので、先頭でヒットすると 0 が返り、これを真偽で判定すると偽になってしまうんですね。
// 先頭にあると 0 が返り、含まれているのに false 扱いになる罠
if (strpos('apple pie', 'apple')) {
// ここには入らない!(0 は偽)
}
// だから必ず型まで比較する必要があった
if (strpos('apple pie', 'apple') !== false) {
// 正しくはこう書く
}
この !== false を書き忘れて、たまたま先頭にあるケースだけ動かない、という不具合を何度か見てきました。
PHP8で追加された素直な三兄弟
PHP8 からは str_contains、str_starts_with、str_ends_with が入って、この手の判定は真偽値をそのまま返してくれるようになりました。読んだままの意味で、コードの意図がぱっと伝わるのがいいところですね。
str_contains('apple pie', 'apple'); // true
str_starts_with('apple pie', 'apple'); // true
str_ends_with('apple pie', 'pie'); // true
if (str_contains($email, '@')) {
// 位置は要らない、含まれるかだけ知りたいときに最適
}
位置が要るときは今まで通りstrpos
誤解しやすいのですが、str_contains は位置を教えてくれません。何文字目にあるかを使って後続処理をする場合は、これまで通り strpos を使います。用途で使い分けるのがきれいですね。
$pos = strpos($path, '?');
if ($pos !== false) {
$query = substr($path, $pos + 1); // 位置が必要ならstrpos
}
まとめ
含まれるかどうかを知りたいだけなら str_contains、前方一致・後方一致は str_starts_with と str_ends_with、位置まで欲しいときだけ strpos、という整理をしておくと迷いません。PHP8 以降の環境なら、可読性の面でも積極的に新しい三つを使っていきたいところだと思います。
よくある質問
Q. str_containsは何文字目にあるかも教えてくれますか。
A. 位置は返しません。含まれるかどうかの真偽値だけです。位置が必要なときは今まで通りstrposを使います。
Q. なぜstrposは !== false と書く必要があったのですか。
A. 先頭でヒットすると0が返り、真偽で判定すると偽になってしまうためです。str_containsならこの罠がありません。