在 composer.json 根级添加 "sort-packages": true 即可自动按 ASCII 顺序排序 require、require-dev 等区块内依赖项,Composer 1.0.0+ 原生支持,仅影响区块内键序,不改变字段位置或语义。

composer.json 里加一行就能自动排序
直接在 composer.json 的根级加 "sort-packages": true,保存后下次运行 composer install 或 composer update 就会自动按包名 ASCII 顺序重排 require、require-dev、conflict 等区块里的依赖项。
不需要插件、不依赖命令、不改脚本——Composer 1.0.0+ 原生支持。它只是在写入 JSON 前对数组做一次 ksort(),性能无感知。
- 排序规则是纯字符串比较:
"_package"(下划线 ASCII 95)排在"a"(97)前面;"Z"(90)排在"a"前面 - 大小写敏感:所以
"Monolog/monolog"和"monolog/monolog"会被当成不同名字处理(实际项目中应统一小写) -
"sort-packages"不影响字段顺序:require还是在require-dev上面,它只管每个区块内部的键值对顺序
为什么 Git diff 总是“全变了”?
团队协作时,如果有人关了 "sort-packages": true,或者本地没配,composer.json 就会来回变:一次提交是字母序,下一次又乱序——Git diff 显示全是“移动行”,真实修改被淹没。
这不是 bug,是预期行为。排序不是“格式化”,而是写入时的确定性输出逻辑。
- 必须全队统一配置:建议把
"sort-packages": true写进项目模板或 CI 检查项 - 别和 Prettier、jq 或 IDE 的 JSON 格式化混用:它们可能按缩进或键名重排,破坏字母序,导致下次
composer update又触发变更 - CI 中可加检查:
composer update --dry-run后比对git status -s composer.json是否有修改,有则报错
排序后 require 和 require-dev 还能分清吗?
能。排序只发生在每个区块内部,require、require-dev、conflict 等字段本身的位置完全由你原始 JSON 结构决定,"sort-packages" 不动它们。
也就是说,只要你在 composer.json 里把 "require" 写在 "require-dev" 上面,它就永远在上面;排序不会把它俩合并,也不会交换区块位置。
- 常见误操作:手写时把
"require-dev"放前面,以为“开发依赖优先”,结果排序后看着更乱——其实只是视觉干扰,功能不受影响 - 区块内排序不影响语义:无论
"laravel/framework"排第几,它仍只在require里生效,不会跑到require-dev中去 - 如果你用
composer require --dev添加包,它会自动塞进require-dev区块并按字母序插入,无需手动调位置
私有包、镜像、sort-packages 三者能共存吗?
能,但它们互不干涉:"sort-packages" 只管 JSON 键序,不管仓库来源;仓库优先级由 repositories 数组顺序决定;镜像是否生效取决于是否禁用 packagist.org。
换句话说:排序再整齐,也不能让私有源变“优先”;反过来,仓库顺序调得再好,也不会影响 composer.json 里依赖项的字母排列。
- 别指望靠排序控制加载顺序:类自动加载走的是
psr-4/classmap规则,跟依赖声明顺序无关 - 私有包名若含大写字母(如
"MyOrg/Utils"),会排在所有小写包前面,容易误判“是不是被顶到最前了”——其实只是 ASCII 规则使然 - 如果用了
"type": "package"手动定义包,它的名字也参与排序,但要注意"name"字段必须和require里写的完全一致(包括大小写、斜杠方向)
真正容易被忽略的点是:排序是静态的、单次的,而仓库匹配是动态的、每次请求都发生的。一个配置管结构,一个管行为,别试图用其中一个解决另一个的问题。


















