必须用 usort() 处理自定义排序逻辑,因其支持多字段、空值优先及类型安全比较;需归一化数据类型、避免外部函数未判空、禁用箭头函数、显式兜底脏数据。

用 usort() 而不是 sort() 或 asort() 处理自定义逻辑
PHP 原生排序函数里,sort()、rsort()、asort() 只能按值或键的默认规则排——字符串字典序、数字大小,没法处理对象属性、多字段、空值优先等真实业务需求。一旦要按 $user['score'] 降序、$user['name'] 升序稳定排序,必须上 usort()。
常见错误是把比较逻辑写成内联表达式(比如 return $a['score'] $b['score'];)却忽略类型安全:当 $a['score'] 是 null 或字符串 "N/A" 时, 会静默转为 0,导致排序错乱。
- 始终在比较前做类型归一化,例如
(float)($a['score'] ?? 0) - 避免在回调里调用外部函数(如
strtolower())而不加isset()判断,否则usort()可能因 warning 中断且不报错 - 如果需保持原始键名(比如关联数组),改用
uasort(),别硬套usort()后再用array_values()丢键
多字段排序必须手动实现“级联比较”,别信 array_multisort() 的链式假象
array_multisort() 看似支持多列,但它的行为是“先按第一列分组,再在每组内按第二列排”,不是真正的复合比较。更麻烦的是它要求你先把各字段抽成独立数组,内存翻倍,且无法处理嵌套结构(比如 $items[0]['meta']['priority'])。
正确做法是单个 usort() 回调里用多个 if 分支或短路表达式:
立即学习“PHP免费学习笔记(深入)”;
usort($items, function($a, $b) {
// 优先级高者在前
$p1 = $a['priority'] ?? 0;
$p2 = $b['priority'] ?? 0;
if ($p1 !== $p2) return $p2 <=> $p1;
// 优先级相同时,按创建时间升序
$t1 = strtotime($a['created_at'] ?? '1970-01-01');
$t2 = strtotime($b['created_at'] ?? '1970-01-01');
return $t1 <=> $t2;
});
- 每个字段比较后必须用
return提前退出,否则后续逻辑会覆盖结果 - 时间字段务必转为时间戳再比,直接比字符串日期(如
"2023-10-01")虽可行,但遇到"2023/10/01"就崩 - 别在回调里查数据库或读文件——
usort()可能调用 N² 次,性能直接雪崩
注意 PHP 7.4+ 的箭头函数不能用于 usort() 回调
箭头函数(fn($a, $b) => $a['id'] $b['id'])看起来简洁,但它隐式绑定当前作用域的 $this 和变量,而 usort() 内部调用时并不保证上下文完整。实测在某些 SAPI(如 CLI)下会抛 Fatal error: Uncaught Error: Cannot use object as array,尤其当箭头函数捕获了对象实例时。
- 一律用传统
function($a, $b) { ... }匿名函数 - 若真想复用逻辑,把比较器抽成独立函数(如
function cmp_users_by_score($a, $b) { ... }),再传函数名字符串给usort($arr, 'cmp_users_by_score') - PHP 8.1+ 支持
enum定义排序策略,但目前usort()不接受 enum 实例作回调,别踩这个坑
空值、缺失键、非数组项必须显式兜底,别依赖“PHP 自动转 0”
线上最常崩的不是逻辑错,而是数据脏:某个 $item 是 null,或 $item['price'] 根本不存在,或 $item['price'] 是字符串 "free"。PHP 的松散比较()会把 null、false、空字符串都转成 0,导致所有空值挤在排序中间,而不是开头或结尾。
- 统一用 null 合并操作符和类型转换:如
($a['price'] ?? 0) + 0强制转数字,或is_numeric($a['price']) ? (float)$a['price'] : -INF控制空值位置 - 对字符串字段排序前用
trim()和mb_strtolower()预处理,否则" Apple"会排在"apple"前面 - 如果数组里混了对象和数组,
usort()回调开头加if (!is_array($a) || !is_array($b)) return 0;防止Notice: Trying to access array offset on value of type int
?? 和 is_numeric(),也别赌数据格式永远标准。



















