自定义 repository 的 type 必须严格匹配来源类型:vcs 用于 git/svn/mercurial,package 用于离线包,path 用于本地目录,composer 用于私有 packagist 服务;顺序和缓存也需注意。

自定义 repository 的 type 必须匹配实际来源
Composer 不会自动识别你填的 type 是否合理,填错就直接报错或静默失效。比如你想从 Git 仓库拉包,type 必须是 vcs;想用本地路径做私有包,得用 package 或 path;想代理 Packagist 风格的 HTTP 仓库,才用 composer。
常见错误现象:Could not find package xxx at version yyy,往往是因为 type 写成了 git(不存在)或 vcs 下漏写了 url。
-
vcs:适用于 Git / SVN / Mercurial 仓库,Composer 会自动探测分支和 tag -
package:适合单个离线包(比如你打包好的my/utils),需手动声明version、dist、source -
path:指向本地目录(如../my-private-lib),开发调试最方便,但不能用于生产部署 -
composer:对接私有 Packagist 兼容服务(如 Satis、Private Packagist),必须提供url且该地址要返回合法的packages.json
在 composer.json 中声明 repository 要注意顺序和作用域
Repository 是全局生效的,但 Composer 只会从第一个能解析到目标包的 repository 拉取——不是合并所有 source。所以如果你同时配了私有 composer 源和 Packagist 官方源,又没关掉 packagist.org,它可能优先走官方源,导致私有版本被忽略。
实操建议:
- 把私有
repository放在repositories数组最前面 - 显式禁用 Packagist:
"packagist.org": false(写在repositories里) - 避免用
https://repo.packagist.org手动重复添加官方源,会导致冲突 - 如果只给某个包指定源,用
repositories+packagetype + 精确name和version,别依赖模糊匹配
使用 vcs 类型时,branch-alias 和 stability 的坑最多
Git 仓库默认只暴露 dev-master 这类开发分支,如果你 require 的是 "my/private": "^1.0",而仓库里没有打 v1.0.0 tag,Composer 就找不到满足条件的版本。
解决方法不靠改 require,而是让仓库“说自己支持什么”:
- 打正式 tag:
git tag v1.0.0 && git push --tags - 在
composer.json根目录加branch-alias(仅对dev-分支有效):"dev-main": "1.0.x-dev" - require 时显式加
@dev后缀(临时方案):"my/private": "dev-main as 1.0.0" - 避免在
minimum-stability设为stable时 requiredev-分支,否则要配prefer-stable: true或降级 stability
path 类型看似简单,但 autoloading 和 symlink 行为容易误判
path 类型本质是软链接(Linux/macOS)或复制(Windows),不是实时同步。你改了源码,不 composer update 或 dump-autoload,autoload 文件不会更新。
更隐蔽的问题是 autoload 加载路径:如果被引用包的 composer.json 里 autoload 配置用了相对路径(如 "src/"),而你把它放在项目外某处,加载时就会报 Class not found。
- 确保 path 指向的目录里有完整、可独立运行的
composer.json - 不要在
path目录里再 require 其他path包,嵌套后 Composer 不保证解析顺序 - CI/CD 环境禁用
path,它只适合本地开发;上线前必须切回vcs或package - 用
composer show my/package确认显示的是Path repository而不是From source,后者说明没生效
最常被跳过的点:repository 配置完不 composer clear-cache,旧缓存会干扰新源的解析;还有人把 repositories 写在 vendor 里的 composer.json 里,那根本不会被读取。










