写在前面
在使用 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 可能是 C 或 POSIX,而不是 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 进行排序
扫描二维码,分享此文章