Composer查包严格按repositories数组从上到下顺序匹配,首个声明支持目标包的仓库即生效,后续仓库被跳过;必须显式设"packagist": false并把私有源置于数组最前,否则默认兜底官方源。

Composer查包时到底按什么顺序找仓库
它不“排序”,也不“智能选源”,而是从 repositories 数组第一个元素开始,逐个检查是否「声明支持」你要装的包——只要某个仓库的 packages.json 里有该 vendor/package 的完整元数据(哪怕只有一个 dev-main),就立刻采用,后面所有仓库全跳过。
常见错误现象:composer require myorg/utils 报 Could not find package myorg/utils,但你明明在私有源里发布了;原因往往是这个私有源没写进 repositories,或写在了数组靠后位置,而前面某个镜像源(比如阿里云)虽然不包含该包,却因未显式禁用 packagist.org,导致 Composer 直接绕过私有源去查镜像,结果镜像没数据就报错。
- 必须用索引数组:
"repositories": [{...}, {...}],不能写成对象键值对 - 私有源要放最前面,且确保其
packages.json真的包含你要的包名和版本 - 国内镜像(如
https://mirrors.aliyun.com/composer/)只是 packagist.org 的只读代理,不托管私有包,混配时极易静默失效
为什么禁用 packagist.org 是硬性前提
不设 "packagist": false,Composer 就永远把官方源当兜底,无论你把私有源写在 repositories 第几行——它都会先查 packagist.org 或其镜像,查不到才 fallback。这不是优先级问题,是默认行为逻辑。
真实调试表现:加 -vvv 运行 composer require,日志里会显示请求了 https://packagist.org/packages/list.json 或镜像地址,完全没碰你的私有 URL。
-
"packagist": false必须作为repositories数组里的一个独立元素,且放在私有源之后(不是之前) - 别用
composer config --global repos.packagist false,这是全局开关,会污染其他项目 - 验证是否生效:
composer config repositories输出中应看到你的私有 URL,且"packagist": false明确存在
同名包出现在多个仓库时会发生什么
Composer 不合并版本列表,也不提示冲突。它只取第一个“能返回元数据”的仓库的结果——哪怕第二个仓库有更新的 dev-main,只要第一个仓库没声明,composer require vendor/package:dev-main 就直接失败。
典型踩坑场景:两个内部 GitLab 仓库都托管了 acme/logger,A 仓只有 1.0.0,B 仓有 2.0.0 和 dev-main。如果你把 A 放在 repositories 前面,那 composer update acme/logger 永远卡在 1.0.0,连 dev-main 都不会出现在可选版本里。
- 不要指望 Composer 自动选“最新版”,它只认“最先命中的那个”
- 若需临时切源,可用
composer config repositories.0.url https://new-url.com动态改第一项 - package 类型仓库(
"type": "package")可精准锁定单个包来源,适合灰度发布
配置写错后最隐蔽的失效方式
最常被忽略的是:私有源的 packages.json 文件本身没正确声明包。比如你用 Satis 或 Private Packagist,但生成的 packages.json 里漏了 "myorg/utils" 这个 key,或者用了错误的命名空间格式(如写成 myorg-utils 而非 myorg/utils),那 Composer 查到这个源时会认为“不支持”,继续往后找——此时即使顺序和开关都对,也等于白配。
调试建议:手动 curl -s https://your-private-repo.com/packages.json | jq '.packages."myorg/utils"',确认输出非空。
-
repositories顺序只是表象,真正决定是否命中的,是每个源的packages.json内容是否匹配 - 运行
composer update --lock后,务必检查生成的composer.lock里dist.url是否指向你的私有源域名 - CI 环境下容易因缓存导致旧 lock 文件残留,记得清掉
vendor/和composer.lock重试


















