写登录校验时用了 php strpos,代码写成 if (strpos($url, 'https')),结果 http 的地址也被放行了。打印返回值才发现,匹配在开头时返回 0,被 if 当成了 false。

返回值是位置,不是布尔值

strpos 找的是子串第一次出现的位置,下标从 0 开始,找不到才返回 false。判断是否存在必须用 !== false,不能简写成 if (strpos(...))。更稳妥的写法是 if (strpos($url, 'https') === 0) 判断开头,或者 PHP 8 起直接用 str_contains。

参数顺序别记反:strpos(被搜索的字符串, 要查找的内容, 起始偏移)。第三个参数传 5,就从第 5 个字节开始找,前面的部分不算,返回值仍是原字符串里的绝对位置。只想知道有没有,不关心位置,PHP 8 的 str_contains 更省事,返回值直接是 true 或 false。

大小写和中文的坑

strpos 区分大小写,找 HTTPS 找不到小写 https,这时用 stripos。它同样返回位置或 false,判断方式一样。中文在 UTF-8 下一个字通常占 3 个字节,strpos 按字节算,返回的位置不是第几个汉字,别拿它直接做中文截取。截取用 mb_strpos 和 mb_substr 更合适,记得把编码参数写成 UTF-8。

空字符串和类型问题

新版本 PHP 对给 strpos 传 null 会提示弃用警告,严格类型下还可能直接报错,老代码从 7 升上来常遇到。先确认变量是字符串,必要时用 (string) 转一下。needle 传空字符串的情况,不同版本行为有差别,业务里最好提前判断,别依赖这个边界。数组、对象传进去同样会报错,调试时先 var_dump 看一眼类型。

几个相关的函数

从后往前找用 strrpos,找开头和结尾在 PHP 8 里用 str_starts_with、str_ends_with,判断子串出现次数用 substr_count。strstr 返回的是子串之后的部分,不是位置,别和 strpos 混着用。老项目里还常见 stristr,它是 strstr 的不区分大小写版本。

实际项目里用 php strpos,只要记住返回值可能是 0,判断一律写 !== false,大部分坑都能避开。新项目如果能用 PHP 8,判断存在性直接上 str_contains,语义更清楚。