PHP 8.2.0与7.4.33中usort()行为差异及兼容方案
问题描述
我有如下关联数组:
$all_works = [ ["title"=>"A Children’s Story", "year"=>"2024"], ["title"=>"Occurred during the Day", "year"=>"2021"], ["title"=>"Future and Past", "year"=>"2021"], ["title"=>"Future and History", "year"=>"2020"], ["title"=>"Open the School, Close the Studio", "year"=>"2023"], ["title"=>"To Reconstruct, To Rebuild", "year"=>"2024"], ["title"=>"Eyeglasses, a Story", "year"=>"2023"], ["title"=>"Nomadic Life", "year"=>"2024"], ["title"=>"Chipotle!", "year"=>"2022"], ["title"=>"between Art & Technology", "year"=>"2020"], ["title"=>"interview Script", "year"=>"2018"], ["title"=>"memories and confession", "year"=>"2024"], ["title"=>"Guest of Pluto", "year"=>"2022"], ["title"=>"A Prologue", "year"=>"2022"], ["title"=>"You are in this place", "year"=>"2023"], ["title"=>"Happening Now", "year"=>"2024"], ["title"=>"Review Catalogue", "year"=>"2022"], ["title"=>"A living being of Mars", "year"=>"2021"], ];
需求:按子数组的year数值降序排序,当year相同时,按title的自然字母数字顺序排序。
我编写的代码如下:
usort($all_works, function($work1, $work2) { return $work2["year"] <=> $work1["year"]; }); echo "<br><br>"; echo "<pre>"; print_r($all_works); echo "</pre>"; usort($all_works, function($work3, $work4){ if($work3["year"] == $work4["year"]){ return strnatcmp($work3["title"], $work4["title"]); }else{ return 0; } }); echo "<br><br>"; echo "<pre>"; print_r($all_works); echo "</pre>";
运行现象:PHP 8.2.0中结果完全符合预期;但在PHP 7.4.33中,第二次usort后的结果混乱,不仅title排序失效,year的顺序也被打乱。
需要解答的问题:
- PHP 8.2.0与7.4.33中的
usort()行为是否存在差异? - PHP 7.4.33中出现该异常的原因是什么?
- 如何修改代码使其在PHP 7.4.33中也能达到PHP 8.2.0中的效果?
问题分析与解答
1. PHP版本间usort的行为差异
是的,两者存在关键差异:PHP 7.4及更早版本使用的排序算法不保证稳定排序,而PHP 8.0及以后版本默认启用了稳定排序(底层切换为Timsort算法)。稳定排序会保留相等元素的原有相对位置,不稳定排序则不会。
2. PHP 7.4出现异常的原因
你的代码逻辑是先按year降序排序,再对同year的元素按title排序。但PHP 7.4的usort是不稳定排序,第二次调用时,当两个元素year不同时返回0(表示两者相等),排序算法会随意调整这些元素的相对位置,直接破坏了第一次排序好的year顺序,同时也无法正确完成同year内的title排序——因为不稳定排序会打乱原有分组的结构。
3. 修复方案
不要分两次调用usort,而是在一次排序中同时处理两个排序条件,这样无论PHP版本是否支持稳定排序,都能得到正确结果:
usort($all_works, function($a, $b) { // 先按year降序比较 $yearCompare = $b["year"] <=> $a["year"]; // 如果year相同,再按title自然排序升序比较 if ($yearCompare === 0) { return strnatcmp($a["title"], $b["title"]); } return $yearCompare; }); echo "<br><br>"; echo "<pre>"; print_r($all_works); echo "</pre>";
代码说明
- 用太空船运算符
<=>先比较year,通过$b["year"] <=> $a["year"]实现降序逻辑。 - 当
year相等时,调用strnatcmp对title进行自然字母数字排序,确保同年份内的标题按预期顺序排列。 - 一次排序完成所有逻辑,彻底避免了不稳定排序带来的问题,兼容所有PHP版本。
内容的提问来源于stack exchange,提问作者Coding_with_a_Tractor
相关产品推荐
相关产品推荐

