Siam博客

PHP中SORT_LOCALE_STRING的地区排序差异

2025-11-12

写在前面

在使用 SORT_LOCALE_STRING 进行排序时,遇到了一个奇怪的现象:本地环境和 GitHub Actions 环境的排序结果不一致。

本地运行结果:

[a, A, b, B, c, C]  // 大小写混合排序

GitHub Actions 运行结果:

[A, B, C, a, b, c]  // 大写在前,小写在后

这是因为 SORT_LOCALE_STRING 会根据系统的 locale(地区设置)使用不同的排序规则。

问题原因

SORT_LOCALE_STRING 排序时,会根据 setlocale() 设置的地区规则来处理字符串。不同地区对字符(特别是大小写、特殊字符)的排序规则不同。

C/POSIX Locale(传统模式)

严格按 ASCII 码排序,大写字母(65-90)排在小写字母(97-122)前面。

setlocale(LC_COLLATE, 'C');
$array = ['a', 'C', 'b', 'A', 'B', 'c'];
sort($array, SORT_LOCALE_STRING);
print_r($array);

// 输出: [A, B, C, a, b, c]

UTF-8 Locale(现代模式)

大小写不敏感,按字母本身分组排序。

setlocale(LC_COLLATE, 'en_US.UTF-8');
$array = ['a', 'C', 'b', 'A', 'B', 'c'];
sort($array, SORT_LOCALE_STRING);
print_r($array);

// 输出: [a, A, b, B, c, C]

对比示例

读取当前地区

echo setlocale(LC_COLLATE, 0);

示例代码

<?php
declare(strict_types=1);

$values = ['a', 'b', 'A', 'B', 'c', 'C'];
$locales = ['C', 'en_US.UTF-8'];

foreach ($locales as $locale) {
    $currentLocale = setlocale(LC_COLLATE, $locale);
    
    echo "Current LC_COLLATE locale: ";
    echo ($currentLocale === false ? '[not set]' : $currentLocale) . PHP_EOL;
    
    $arr = $values;
    sort($arr, SORT_LOCALE_STRING);
    
    echo "Sorted: ";
    print_r($arr);
    echo str_repeat('=', 50) . "\n";
}

输出结果

Current LC_COLLATE locale: C
Sorted: Array
(
    [0] => A
    [1] => B
    [2] => C
    [3] => a
    [4] => b
    [5] => c
)
==================================================
Current LC_COLLATE locale: en_US.UTF-8
Sorted: Array
(
    [0] => a
    [1] => A
    [2] => b
    [3] => B
    [4] => c
    [5] => C
)
==================================================

CI/CD环境的问题

在 GitHub Actions 中,默认的 locale 可能是 CPOSIX,而不是 en_US.UTF-8,导致排序结果与本地不同。

# 本地环境(en_US.UTF-8)
-[{"id":4,"name":"a"},{"id":2,"name":"b"},{"id":5,"name":"A"},{"id":3,"name":"B"}]

# GitHub Actions(C/POSIX)
+[{"id":4,"name":"a"},{"id":5,"name":"A"},{"id":2,"name":"b"},{"id":3,"name":"B"}]

相同的地区编码 不同机器表现不同

详见> https://github.com/hyperf/hyperf/pull/7612

所以使用相同地区编码来控制排序结果也是不一定可靠的,取决于当前操作系统平台的实现(猜测)

总结

  • SORT_LOCALE_STRING 会受系统 locale 影响,不同环境可能产生不同结果
  • 使用 setlocale() 时要检查返回值,确保设置成功
  • CI/CD 环境需要明确安装和设置所需的 locale
  • 跨平台应用建议使用自定义排序函数(如 strcasecmp)保证一致性
  • 对于用户可见的数据,应该根据用户地区设置合适的 locale 进行排序
本文链接:
版权声明: 本文由 Siam原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权
Tags: PHP

扫描二维码,分享此文章