news 2026/10/9 9:45:27

PHP字符串比较函数strcmp()和strcasecmp()使用总结

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PHP字符串比较函数strcmp()和strcasecmp()使用总结

前言


strcmp()和strcasecmp()是一对孪生函数:前者区分大小写,后者不区分大小写,其余行为一致,都做二进制安全的字节比较。


关于它们,有三个反复出现的误解。


第一个是返回值。手册明确只保证「小于零 / 等于零 / 大于零」这三种情况,不保证具体的整数。所以if (strcmp($a, $b) === -1)这种写法是不可靠的,正确姿势是=== 0、< 0、> 0。


第二个是「strcmp()能防时序攻击」。不能。它是普通的字节比较,一发现不同就返回;比较哈希值、校验签名这类场景要用hash_equals()。而校验用户密码,连hash_equals()都不该直接用——应该用password_verify()。


第三个是「strcmp()能比较数字大小」。也不能。它逐字节比,strcmp('10', '9')的结果是负数,因为字符'1'比'9'小。要按数值比就先转成数字,要按「人能接受的顺序」比就用strnatcmp()。


本文讲清这两个函数的签名、返回值的正确用法、字节比较带来的各种后果,以及在现代 PHP 里该用哪些新函数替代它们。


一、签名与返回值


以 PHP 官方手册为准:


strcmp(string $string1, string $string2): int
strcasecmp(string $string1, string $string2): int

两个函数的返回值语义完全相同:




返回值含义



小于 0$string1小于$string2

等于 0两个字符串相等

大于 0$string1大于$string2



注意这里刻意没有写「返回 -1」「返回 1」——手册的承诺只有符号。历史实现里有的直接返回两个字节的差值,有的做了归一化,具体数字不是可依赖的接口。稳妥写法一律是判断符号:


<?php
// 适用于 PHP 8.0+

// 相等判断:必须是 === 0
var_dump(strcmp('abc', 'abc') === 0); // bool(true)
var_dump(strcmp('abc', 'abd') === 0); // bool(false)

// 大小关系:判断符号
var_dump(strcmp('abc', 'abd') < 0); // bool(true)
var_dump(strcmp('abd', 'abc') > 0); // bool(true)

// 大小写:小写字母的 ASCII 值更大
var_dump(strcmp('a', 'A') > 0); // bool(true)

// strcasecmp:忽略 ASCII 大小写
var_dump(strcasecmp('Hello', 'hello') === 0); // bool(true)
var_dump(strcasecmp('Hello', 'World') === 0); // bool(false)

二、strcmp() 的比较规则


strcmp()的比较规则可以用三句话概括:



  1. 二进制安全:按字符串的实际长度逐字节比较,内容中带\0也不会提前结束。

  2. 按字节的数值大小比较:本质上是比较字符的编码值,所以小写字母大于大写字母,数字字符之间也是按字符比。

  3. 前缀相同的两个字符串,短的更小。


<?php
// 适用于 PHP 8.0+

// 数字字符比较不是数值比较
var_dump(strcmp('10', '9') < 0); // bool(true) '1' < '9'
var_dump(10 < 9); // bool(false) 这才是数值比较

// 前缀相同时,短的更小
var_dump(strcmp('abc', 'abcd') < 0); // bool(true)
var_dump(strcmp('', '') === 0); // bool(true)

// 二进制安全:中间的 NUL 字节照样参与比较
var_dump(strcmp("a\0b", "a\0c") < 0); // bool(true)

关于中文:strcmp()比较的是 UTF-8 字节序列,得到的是字节序,既不是拼音序,也不是笔画序。给中文做「按拼音排序」这种需求,strcmp()帮不上忙,需要 intl 扩展的Collator,或者自己把中文转成拼音/编码后再排。这一点在建索引、做排序列表时是硬约束,别指望靠strcmp()蒙对。


三、strcasecmp() 的边界:只折叠 ASCII 大小写


strcasecmp()的「不区分大小写」仅限ASCII 的A到Z。也就是说,它只把英文字母的大小写视为等价,非 ASCII 字符(中文、带重音符号的拉丁字母等)完全按原样比较。


<?php
// 适用于 PHP 8.0+

var_dump(strcasecmp('abc', 'ABC') === 0); // bool(true) ASCII 折叠
var_dump(strcasecmp('straße', 'STRASSE') === 0); // bool(false) 非 ASCII 不做折叠
var_dump(strcasecmp('中文', '中文') === 0); // bool(true) 完全相同的字节序列

// 需要多字节的忽略大小写比较时,先统一转小写再比
$s1 = mb_strtolower('Hello 世界', 'UTF-8');
$s2 = mb_strtolower('hello 世界', 'UTF-8');
var_dump(strcmp($s1, $s2) === 0); // bool(true)

要点是:strcasecmp()不读取mb_internal_encoding(),也不认识 UTF-8,它就是一个逐字节的 ASCII 比较器。涉及到多字节文本的忽略大小写比较,老老实实mb_strtolower()两边之后再strcmp()。


四、在什么场景用它们,以及现代替代方案


场景一:当排序回调。这是这两个函数在实战中最常见的用法。排序只需要返回值的符号,所以strcmp()可以直接当回调传进去。


<?php
// 适用于 PHP 8.0+

$fruits = ['banana', 'Apple', 'cherry'];

$a = $fruits;
usort($a, 'strcmp'); // 区分大小写:大写 'A' 的编码更小,排在前面
var_dump($a); // ['Apple', 'banana', 'cherry']

$b = $fruits;
usort($b, 'strcasecmp'); // 忽略大小写
var_dump($b); // ['Apple', 'banana', 'cherry']

$c = $fruits;
sort($c, SORT_STRING); // 等价于按字符串比较排序
var_dump($c); // ['Apple', 'banana', 'cherry']

场景二:比较相等。单纯判断两个字符串是否相等时,===比strcmp()更直接,也没有函数调用开销。但要注意它和==的差别:==在两侧都是「数字字符串」时会做数值比较,而strcmp()永远按字节比。


<?php
// 适用于 PHP 8.0+

var_dump('10' == '10.0'); // bool(true) 松散比较:按数字比
var_dump(strcmp('10', '10.0') === 0); // bool(false) 按字节比:长度都不同
var_dump('10' === '10.0'); // bool(false) 严格比较:类型与内容都要求一致

这就是「判断字符串相等到底该用哪个」的答案:普通业务判断用===;需要明确「按字节比较、不参与任何数值语义」时用strcmp(...) === 0。绝对不要用==比较两个来路不明的字符串,数字字符串的松散比较会带来意外相等。


场景三:前缀、包含关系。这类需求现在有更直白的函数可用,不需要再借道strcmp():




需求现代写法(PHP 8.0+)老写法



是否以某串开头str_starts_with($s, $prefix)strncmp($s, $prefix, strlen($prefix)) === 0

是否包含某串str_contains($s, $needle)strpos($s, $needle) !== false

是否以某串结尾str_ends_with($s, $suffix)手动substr()比较

自然顺序比较strnatcmp($a, $b)无(strcmp做不到)

按当前区域设置比较strcoll($a, $b)无



场景四:比较敏感数据。这里必须换函数。比较两个哈希值、两个签名、两个令牌时,strcmp()会「一发现不同就返回」,比较耗时会泄露「前多少个字节是对的」,这就是时序攻击的入口。正确做法是用hash_equals():


<?php
// 适用于 PHP 8.0+(hash_equals 自 PHP 5.6 起提供)

$expected = hash_hmac('sha256', $message, $key);
$received = $_POST['signature'] ?? '';

if (!hash_equals($expected, $received)) {
exit("签名校验失败\n");
}

而校验用户密码连hash_equals()都不该直接上手,应该用password_verify()——它会从哈希串里读取盐值和算法参数,自己完成比对:


<?php
// 适用于 PHP 8.0+

$hash = password_hash('用户密码', PASSWORD_DEFAULT);

if (password_verify($_POST['password'] ?? '', $hash)) {
echo "密码正确\n";
} else {
echo "密码错误\n";
}

千万不要自己去比md5($input) === $stored,也不要自己用strcmp()拼比对逻辑。


常见坑点



  • ❌ 写成if (strcmp($a, $b)),以为返回真就是相等。✅ 返回0才是相等,非零是不等;条件应写成if (strcmp($a, $b) === 0)。

  • ❌ 依赖具体返回值:strcmp($a, $b) === -1。✅ 手册只保证符号,不同实现返回的数字可能不同;判断< 0、=== 0、> 0。

  • ❌ 用strcmp()比较数字大小,strcmp('10', '9')得到负数。✅ 它逐字节比;按数值比要先转成数字,按「符合直觉的顺序」比用strnatcmp()。

  • ❌ 用strcasecmp()比较含中文或带重音字母的字符串,以为它会忽略大小写差异。✅ 它只折叠 ASCII 的 A 到 Z;多字节文本先mb_strtolower($s, 'UTF-8')再strcmp()。

  • ❌ 用strcmp()或strcasecmp()校验密码或比较令牌。✅ 密码用password_verify(),其他敏感数据的比对用hash_equals()。

  • ❌ 用strcmp()判断前缀:strncmp($s, $prefix, strlen($prefix)) === 0写起来容易错。✅ PHP 8.0 起直接用str_starts_with($s, $prefix)。

  • ❌ 认为strcmp($a, $b) === 0和$a == $b等价。✅ 对数字字符串两者结论可能相反,例如'10'与'10.0'用==相等、用strcmp()不等。

  • ❌ 向这两个函数传null(常见于读配置项没读到的时候)。✅ 参数是非可空string,PHP 8.1 起传null会触发废弃提示,先做类型转换。


总结




对比项strcmp()strcasecmp()



大小写区分不区分(仅 ASCII)

比较单位字节,二进制安全字节,二进制安全

返回值只保证符号(小于 0 /0/ 大于 0)同左

数字字符串按字符比,'10' < '9'同左

中文按 UTF-8 字节序,不是拼音序非 ASCII 不折叠

判等写法strcmp($a, $b) === 0strcasecmp($a, $b) === 0

典型用途排序回调、明确的字节比较忽略大小写的排序、键名匹配

不该用的场合密码、令牌、签名比对同左



strcmp()和strcasecmp()的核心是「按字节、看符号」:返回值只判断符号不判断数值,比较单位是字节所以不做数值比较、也不是多字节安全的。普通判等用===就够了;涉及密码用password_verify(),涉及其他敏感数据用hash_equals();前缀和后缀判断在 PHP 8.0 以后交给str_starts_with()这类语义化函数。把这几次分流做对,字符串比较这一类问题就基本清干净了。




版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 9:42:38

CPU 上跑 OpenCL:用 TaoToken 统一 Key 打通 OneAPI 工具链的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/9 9:41:44

设计质量管理实战:从评审门禁到数据追溯的落地指南

简介&#xff1a;这份PPT文档资料聚焦设计质量管理&#xff0c;面向产品开发、品质工程与制造管理方向的学习者和从业者&#xff0c;帮助理解如何在设计阶段就把品质、可制造性、经济性与可持续性纳入考量。内容围绕在线与离线质量工程展开&#xff0c;涵盖DFX&#xff08;DFA、…

作者头像 李华
网站建设 2026/10/9 9:36:49

Claude Code卡死排查实战:用pstack三板斧定位进程问题

写 Claude Code 这一年多&#xff0c;我调过的诡异问题比过去的 Node 工程加起来都多。最让人崩溃的不是报错&#xff0c;而是它安安静静卡在那里——光标还亮着&#xff0c;终端没退出&#xff0c;上下文还在&#xff0c;但你不知道它在思考、在等网络、在跑工具&#xff0c;还…

作者头像 李华
网站建设 2026/10/9 9:33:24

t3code 实战:构建本地化代码质量分析与复杂度度量体系

1. 项目全景拆解&#xff1a;t3code 到底是什么先聊点实际的。第一次看到t3code这个名字&#xff0c;你可能会和我一样好奇——它到底是一个新框架、一个代码库&#xff0c;还是一套开发流程&#xff1f;我在项目早期也经历过懵圈阶段&#xff0c;直到把它的定位彻底理清&#…

作者头像 李华
网站建设 2026/10/9 9:32:55

多元函数极值:从几何直觉到海森矩阵与拉格朗日法的系统解析

1. 为什么多元函数极值是高等数学里“绕不开的硬骨头”你翻过《高等数学》教材的多元函数章节&#xff0c;大概率会在“极值”这一节卡住——不是因为公式记不住&#xff0c;而是突然发现&#xff1a;一元函数求导找驻点&#xff0c;逻辑清晰得像走直线&#xff1b;可到了二元、…

作者头像 李华