Composer插件需实现PluginInterface并监听PRE_PACKAGE_INSTALL事件,通过setDistUrl()等方法修改包下载URL,同时同步更新dist.type和dist.shasum以确保校验通过;或通过Config::merge动态注入镜像仓库配置。

Composer插件必须实现 PluginInterface 并监听 PRE_PACKAGE_INSTALL 事件
Composer 插件不能直接“修改下载 URL”,因为包元数据(如 dist.url)在加载后是只读的。真正可行的路径是:在包安装前拦截请求,用自定义逻辑替换源地址。这需要监听 PRE_PACKAGE_INSTALL 事件,并通过修改 PackageEvent::getOperation()->getPackage() 的 dist 配置来生效——但注意,该对象是 CompletePackage 实例,其 setDistUrl() 方法存在且可用。
关键点:
- 插件类必须实现
Composer\Plugin\PluginInterface - 必须在
activate()中获取EventDispatcher并添加监听器 - 仅
PRE_PACKAGE_INSTALL和PRE_PACKAGE_UPDATE事件允许安全修改 package 对象 - 不能用
POST_*事件,此时下载流程已启动,修改无效
setDistUrl() 修改后需同步更新 dist.shasum 和 dist.type
单纯调用 $package->setDistUrl('https://...') 不足以让 Composer 正常下载。它会校验 dist.shasum,若不匹配则报错 Invalid archive signature;若未设 dist.type,可能 fallback 到 VCS 拉取,绕过你的 URL。
实操建议:
- 确保新 URL 返回的 ZIP 包与原始包内容一致(否则 shasum 校验失败)
- 显式设置
$package->setDistType('zip')(或'tar') - 从原包读取
$package->getDistShaSums()['sha256']或['sha1'],再用相同值调用$package->setDistShaSums([...]) - 如果新 URL 是镜像服务(如私有 Packagist),它应返回标准 Composer dist 响应头(
Content-MD5等非必需,但shasum必须一致)
插件中无法直接改写 repositories 配置,但可通过 Config 动态注入镜像
有些场景想全局把所有 packagist.org 请求转到内网镜像,这时不应在每个包上 patch URL,而应修改 Composer 运行时的仓库配置。插件可通过 $composer->getConfig() 获取 Config 实例,再调用 merge() 注入自定义 repositories。
示例逻辑:
public function activate(Composer $composer, IOInterface $io)
{
$config = $composer->getConfig();
$repos = $config->get('repositories') ?: [];
array_unshift($repos, [
'type' => 'composer',
'url' => 'https://my-mirror.example.com',
'canonical' => false,
]);
$config->merge(['repositories' => $repos], Config::SOURCE_PLUGIN);
}
注意:
- 必须设
'canonical' => false,否则 Composer 会忽略其他仓库 - 此方式优先级高于
composer.json中的repositories,但低于命令行--repository - 不会影响已 lock 的包来源,仅对后续 resolve 生效
调试时用 COMPOSER_MEMORY_LIMIT=-1 COMPOSER_VERBOSITY=4 查看真实 dist URL
URL 是否被成功替换,最可靠的方式不是看日志文字,而是观察 Composer 实际发起的 HTTP 请求。启用高 verbosity 后,你会在输出中看到类似:
Downloading https://my-mirror.example.com/packagist/phpunit/phpunit/9.6.0.0/dist.zip
常见踩坑点:
- 忘记在
composer.json的autoload中声明插件类的 PSR-4 映射,导致类找不到 - 插件未声明
"type": "composer-plugin",Composer 直接跳过加载 - 用了
POST_PACKAGE_INSTALL事件,代码执行了但无效果(URL 已下载完毕) - 新 URL 返回 302 重定向但未带
Content-Length,触发 Composer 的 chunked transfer bug(v2.5+ 已修复,旧版建议避免重定向)
实际部署前,务必在干净环境运行 composer update --no-plugins 对比一次,确认差异仅来自你的插件逻辑。










