今天来看看经常使用的数组排序函数如 sort, rsort, asort, arsort, ksort, krsort 。话不多说直接找 sort 函数吧。
在 php7.3 源码中搜索 PHP_FUNCTION(sort) 可以搜到如下

其中 .h 文件是C语言的头文件,直接打开 .c 文件。sort 函数如下,其中我加了一点注释。
1 | |
不但 rsort, asort, arsort, ksort, krsort 这些函数在 array.c 文件中,PHP数组相关的也都在其中。
先说下 rsort, asort, arsort, ksort, krsort 函数内容与 sort 只有细微的差别。ksort、krsort 是根据键排序所以排序规则获取排序函数用的是 php_get_key_compare_func 参数与 php_get_data_compare_func 是一样的。php_get_data_compare_func、php_get_key_compare_func 函数第二个参数意思是是否降序排列,rsort、arsort、krsort 第二个参数都是1。
进行排序时 zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) 第三个参数意思是是否重新排列索引, sort、rsort 传的都是1。
做个表格看下
| 获取排序函数 | 调用排序 | |
|---|---|---|
| sort | php_get_data_compare_func(sort_type, 0) | zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) |
| rsort | php_get_data_compare_func(sort_type, 1) | zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) |
| asort | php_get_data_compare_func(sort_type, 0) | zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) |
| arsort | php_get_data_compare_func(sort_type, 1) | zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) |
| ksort | php_get_key_compare_func(sort_type, 0) | zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) |
| krsort | php_get_key_compare_func(sort_type, 1) | zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) |
其中调用 php_get_data_compare_func 与 php_get_key_compare_func 获取的 cmp 后面再说明。
继续找 zend_hash_sort ,在 zend_hash.h 中。
1 | |
看来 zend_hash_sort 中调用了 zend_hash_sort_ex 。 zend_hash_sort_ex 在 zend_hash.c 中。
1 | |
在 zend_sort.c 中找到 zend_sort ,通过备注发现这个排序是源于 LLVM 的 libc++ 中的 std::sort 实现的。算是快排的优化版,当元素数小于等于16时有特殊的优化,当元素数小于等于5时直接通过 if else 嵌套判断排序,真是优化的极致。zend_sort_2 、 zend_sort_3 、 zend_sort_4 、 zend_sort_5 中是 if else 嵌套的判断排序就不贴出来了。其中基准点(pivot)计算方式也进行了优化。相比 PHP5 时代的标配快排实现要稳定多了。
1 | |
最后来说说 cmp 这个函数,当 sort_flags 为 SORT_REGULAR 时 sort 函数的 cmp 调用的是 array.c 中的下面这个函数,返回值分成 小于0(b>1), 0(b==a), 大于0(a>b)对比失败也是0。
1 | |
再往下追就是 compare_function 很长我就不贴了,简单说下其中先判断 first 和 second 类型,再进行各种分支比较。比较好奇其中的都是字符串时对比方法,追了下发现底层使用的是C的 memcmp 比较这两个串的前N个字节,这个N是这两个串中较小的那个。
最后总结下 PHP7 对比 PHP5 时代数组排序调用逻辑相差不大,但是排序算法优化了很多,更不用说底层的hash table了。
最后的最后文中如有理解错误的点也请指教。