さよならstrpos。今までありがとう。

phpを使ってる人なら1回はstrpos関数を使ったことがあると思います。 そして罠にも引っかかったことがあると思います。。。

実装

<?php
$haystack = '吾輩は猫である';
$needle = '吾輩';
if (strpos($haystack, $needle)) {
  var_dump('夏目漱石');
}

何も表示されません

<?php
$haystack = '吾輩は猫である';
$needle = '猫';
if (strpos($haystack, $needle)) {
  var_dump('夏目漱石');
}

夏目漱石が表示されます

なぜか

strposのドキュメントを見てみましょう

www.php.net

strpos(string $haystack, string $needle, int $offset = 0): int|false

返り値に0も入ってしまうのが原因でした。

<?php
$haystack = '吾輩は猫である';
$needle = '吾輩';
var_dump(strpos($haystack, $needle));

0が返ってきます。

<?php
$haystack = '吾輩は猫である';
$needle = '猫';
var_dump(strpos($haystack, $needle));

9が返ってきます。

吾輩はUTF-8で9バイトをとっているのでその値が返ってきます。

正しい実装はこちら

<?php
$haystack = '吾輩は猫である';
$needle = '吾輩';
if (strpos($haystack, $needle) !== false) {
  var_dump('夏目漱石');
}

!== falseを入れる必要があったんですね

こんなこともう気にしないでいいんだ。そう、php8ならね

php8のstr_containsを使えば全部忘れちゃって問題なしです!

シンプルに文字が含まれるかどうかboolで返してくれます

PHP: str_contains - Manual

str_contains(string $haystack, string $needle): bool

<?php
$haystack = '吾輩は猫である';
$needle = '吾輩';
if (str_contains($haystack, $needle)) {
  var_dump('夏目漱石');
}

夏目漱石が表示されます

さよならstrpos。今までありがとう。 そしてこんにちはstr_contains