← PHP array_uintersect_uassoc函数详解:键与值双重自定义回调的交集运算 PHP array_unique函数详解:数组去重的排序标志与键名保留机制 →

PHP uksort函数详解:用自定义回调按数组键名排序

著
原创 2026-10-09 PHP 已有人查阅

一、函数定位与核心机制

uksort是PHP内置的数组排序函数,从PHP4.0开始提供。名字里的u代表userfunction,意思是「按键名排序,比较规则由用户自定义回调决定」。

这里要区分三个容易混的函数:

  • ksort:按键名排序,比较规则由PHP内部决定

  • usort:按值排序,比较规则由用户回调决定

  • uksort:按键名排序,比较规则由用户回调决定

uksort的独特之处在于,它操作的对象是数组的键,而不是值。键名在PHP数组里可能是字符串、整数,甚至是混合类型。当业务上需要对键名做特殊排序(比如按字母逆序、按业务编码规则、按版本号顺序)时,内部比较规则不够用,就得用uksort。

语法结构

bool uksort(array &$array, callable $key_compare_func): bool

参数说明:

参数 是否必需 说明
array 必需 待排序的数组,传引用,函数直接修改原数组
key_compare_func 必需 用户自定义的键比较函数

返回值只有两种:成功返回true,失败返回false。注意这里返回的是布尔值,不是排序后的数组。排序结果直接体现在传入的数组变量上,这一点和array_uintersect系列返回新数组的行为不同。

回调返回值语义

回调函数接收两个键名作为参数,返回整数:

  • 第一个参数小于第二个参数,返回负数

  • 两个参数相等,返回0

  • 第一个参数大于第二个参数,返回正数

原文里有个细节值得注意:示例中的回调return($a>$b)?-1:1;返回的是-1和1,也就是把「大于」映射成-1,「小于」映射成1。这实际上实现的是降序排序。返回值决定排序方向,不是固定的,这是理解uksort的关键。

传引用机制

uksort的第一个参数是&$array,传引用。函数执行后原数组被就地修改,不会产生新数组。这一点和array_uintersect返回新数组的行为形成对比。写代码时如果想把排序结果保留到另一个变量,需要先复制一份再排序:

$sorted = $original;
uksort($sorted, "codehub_cmp");

否则原数组会被直接改动,后续逻辑可能受影响。这个坑在排查数据被意外修改的问题时经常遇到。

二、排序方向的控制

回调返回值的正负决定排序方向,这是uksort里最需要理清的一点。用一个对照表说明:

回调返回逻辑 排序结果
($a>$b)?1:-1 升序(从小到大)
($a>$b)?-1:1 降序(从大到小)
$a<=>$b 升序
$b<=>$a 降序

PHP7之后引入了太空船操作符<=>,它直接返回-1、0、1,写起来比三元表达式简洁:

function codehub_cmp($a, $b) {
    return $a <=> $b;
}

等价于升序排序。要降序就把两个参数换个位置:return$b<=>$a;。这是目前比较推荐的写法,可读性比嵌套三元表达式好。

三、回调编写要点

回调必须对称。内部排序后,参数哪个来自前面、哪个来自后面是随机的。回调不能依赖参数顺序做逻辑分支,否则结果会漂移。

返回0的场景要处理。两个键名相等时返回0,这是规范。如果漏掉这个判断,只返回1和-1,排序仍能跑,但内部算法遇到相等元素时的行为不如返回0稳定。

键名类型要留意。PHP数组里数字字符串键名"1"会被自动转成整数1。回调里收到的参数类型可能和字面量不一致。如果键名混有字符串和整数,用==比较会做类型转换,用===比较更严格。这个选择取决于业务语义。

回调里不要做副作用操作。内部排序会让回调被调用多次,调用次数和顺序不固定。在回调里写日志、改全局变量、做数据库查询,结果都不可预期。早期项目里我在回调里累加计数器想统计比较次数,结果发现同一个键被比较了多次,计数对不上。

大数据量下注意性能。排序的时间复杂度大约是O(nlogn),回调本身的开销会被放大。如果数组有几千条以上,回调里避免做字符串拼接、正则匹配这类相对重的操作。必要时先把键名预处理成规范形式,再排序。

四、项目实战示例

示例一:按键名降序排序

<?php
function codehub_cmp($a, $b) {
    if ($a == $b) {
        return 0;
    }
    return ($a > $b) ? -1 : 1;
}

$input = array("d" => "mango", "a" => "apple", "b" => "banana");
uksort($input, "codehub_cmp");
print_r($input);

输出:

Array
(
    [d] => mango
    [b] => banana
    [a] => apple
)

回调里($a>$b)?-1:1表示第一个参数大时返回负数,即排在前面,实现降序。结果键名按d、b、a排列。

示例二:数字键名的降序排列

<?php
function codehub_num_cmp($x, $y) {
    if ($x == $y) {
        return 0;
    }
    return ($x > $y) ? -1 : 1;
}

$language = array("1" => "java", "2" => "php", "6" => "python", "4" => "ruby");
uksort($language, "codehub_num_cmp");
print_r($language);

输出:

Array
(
    [6] => python
    [4] => ruby
    [2] => php
    [1] => java
)

键名"1"、"2"、"6"、"4"在PHP内部都转成了整数,回调收到的也是整数。降序排列后是6、4、2、1。

示例三:升序排序的写法

<?php
function codehub_asc($a, $b) {
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

$numbers = array("a" => 9, "b" => 2, "c" => 60, "d" => 78);
uksort($numbers, "codehub_asc");
print_r($numbers);

输出:

Array
(
    [a] => 9
    [b] => 2
    [c] => 60
    [d] => 78
)

回调里($a<$b)?-1:1表示第一个参数小时返回负数,排在前面,实现升序。键名a、b、c、d本身就是升序排列的,所以结果顺序不变。

示例四:用太空船操作符简化

<?php
function codehub_spaceship($x, $y) {
    return $y <=> $x;
}

$sports = array("1" => "cricket", "2" => "hockey", "8" => "football", "4" => "badminton");
uksort($sports, "codehub_spaceship");
print_r($sports);

输出:

Array
(
    [8] => football
    [4] => badminton
    [2] => hockey
    [1] => cricket
)

$y<=>$x把两个参数位置对调,等价于降序。结果和示例二一致,但写法更简洁。PHP7及以上版本推荐用这种写法。

五、容易踩的坑

返回值和排序方向的关系容易搞反。很多人以为返回1就是排在后面,实际上返回正数表示第一个参数排在第二个参数之后。降序和升序的区别就在返回值正负上,写的时候先在纸上画两个键名验证一下。

传引用导致原数组被改。uksort直接修改传入的数组,不返回新数组。如果业务上需要保留原数组,先复制一份。这个坑在多层函数调用里容易被忽略,导致上游数据被意外改动。

键名类型混合时比较结果不确定。数组里同时有字符串键名和整数键名时,==比较会做类型转换,可能得到意料之外的结果。比如"1abc"==1在旧版本PHP里是true,PHP8之后行为有变化。混合类型键名的数组建议先统一键名类型,再排序。

失败时返回false但不报错。uksort在参数类型不对时返回false,但不会抛出异常。如果不检查返回值,可能以为排序成功,实际上数组没动。写代码时很好加一层判断:

if (!uksort($data, "codehub_cmp")) {
    // 处理失败情况
}

性能敏感场景的取舍。如果只是按字母升序或数字升序排列键名,ksort就够了,性能比uksort好,因为比较规则在内部用C实现。只有当排序规则确实需要业务自定义时,才用uksort。这一点在做大批量数据排序时值得考虑。

六、本节课程知识要点

  • uksort按键名排序,比较规则由用户回调决定

  • 回调返回负数表示第一个参数排前面,返回正数表示排后面

  • 回调返回值的正负决定排序方向,降序和升序通过调换返回值实现

  • 第一个参数传引用,函数直接修改原数组,返回布尔值

  • PHP7及以上推荐用太空船操作符<=>简化回调

  • 与ksort的区别在于比较规则是否自定义,与usort的区别在于排序对象是键还是值

  • 大数据量下注意回调开销,简单排序优先用ksort

七、适用场景

这个函数适合以下场景:

  • 键名需要按业务编码规则排序(比如订单号、版本号)

  • 键名需要按字母逆序或自定义顺序排列

  • 键名大小写不敏感排序(回调里用strcasecmp)

  • 键名是复合结构需要拆分后比较

不适合的场景:

  • 简单的字母升序或数字升序,用ksort性能更好

  • 需要按值排序,用usort或uasort

  • 需要保留原数组不变,先复制再排序或改用其他返回新数组的函数

  • 键名类型混杂且未统一,建议先规范键名再排序

← PHP array_uintersect_uassoc函数详解:键与值双重自定义回调的交集运算 PHP array_unique函数详解:数组去重的排序标志与键名保留机制 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号