仅设"type": "package"需手动维护版本等信息,无法自动更新;必须用自定义VCS驱动:通过Composer插件注册MyHttpDriver,实现getTags()、getBranches()等方法,返回合规版本映射,并在repositories中使用myhttp://协议调用。

为什么composer.json里写"type": "package"还不够?
因为这种方式要手动维护版本号、dist/source地址、sha256校验值,一旦上游更新就得同步改,根本没法自动更新。真正需要的是让 Composer 像拉 Git 仓库一样,自动识别 tag、解析分支、下载 zip 包——但目标平台不是 GitHub/GitLab,而是内部 HTTP 文件服务器、NAS 共享目录甚至某个带 JSON API 的私有包管理页。
这时候必须用自定义 VCS 驱动,否则 composer install 根本不知道怎么“checkout”一个非 Git 地址。
如何注册自定义 VCS 驱动类并让它被 Composer 加载?
Composer 在初始化时会扫描所有已安装的包,查找实现了 ComposerRepositoryVcsDriverInterface 的类,并按 url 协议前缀匹配驱动。关键不是改 Composer 源码,而是通过 Composer 插件机制注入。
- 新建一个 Composer 插件包(比如
myorg/composer-vcs-driver),在composer.json中声明"type": "composer-plugin" - 插件主类实现
ComposerPluginPluginInterface,在activate()方法里调用$composer->getPackage()->getRepositories()获取仓库管理器 - 用
$io->writeError('Registering MyHttpDriver...')调试是否生效(writeError才能在composer install过程中看到输出) - 注册驱动:调用
$repositoryManager->setVcsDriver('myhttp', 'MyOrg\Composer\Driver\MyHttpDriver'),其中'myhttp'是协议名,后面会在repositories的url里用到
MyHttpDriver 必须实现哪些方法?重点看 getTags() 和 getBranches()
Composer 在 resolve 阶段会调用这两个方法来构建可用版本列表。返回格式必须严格匹配原生驱动:键是版本名(如 'v1.2.0'),值是 commit hash(哪怕你根本不用 git,也得伪造一个唯一字符串,比如 md5($zipUrl))。
常见错误是返回空数组或键名含非法字符(如空格、斜杠),导致 composer update 报错 Could not parse version constraint 或直接跳过该仓库。
-
hasComposerFile()返回true,否则 Composer 认为这个仓库没composer.json,不会尝试读取 -
getComposerInformation($commitRef)必须能根据传入的 hash(即你getTags()返回的 value)反查到对应 zip 包 URL,并下载解析其composer.json - 所有网络请求必须用
$this->getLoop()->addStream()或$this->getHttpClient(),不能直接file_get_contents(),否则不支持异步和超时控制
在项目中使用自定义驱动:repositories 的 url 写法很关键
协议名必须和插件里 setVcsDriver() 注册的一致,且 url 要以该协议开头,否则 Composer 根本不会触发你的驱动。
示例配置:
{
"repositories": [
{
"type": "vcs",
"url": "myhttp://packages.internal/releases/mylib/"
}
],
"require": {
"myorg/mylib": "^1.2"
}
}
注意:myhttp:// 不是真实协议,只是 Composer 内部路由标识;实际请求仍走 HTTP,但会交由你的 MyHttpDriver 处理。
容易踩的坑:url 末尾加不加 / 可能影响路径拼接逻辑;如果返回的 zip 包里没有 composer.json,Composer 会静默跳过该 tag,而不是报错——建议在 getComposerInformation() 里加 throw new RuntimeException("No composer.json in $url") 显式失败。
最麻烦的其实是缓存:Composer 会把 getTags() 结果缓存数小时,改了远程 JSON 却不生效,得手动删 vendor/composer/installed.json 或加 --no-cache 调试。



















