必须接管Installer、RepositoryManager和事件生命周期三环节:通过composer.json的type字段匹配自定义Installer,用setRepositoryManager替换源管理器,并在POST_PACKAGE_INSTALL等早期阶段修改autoload配置,避免被后续流程覆盖。

Composer 插件不是“加个钩子就行”,真要定制企业级包管理逻辑,必须接管 Installer、RepositoryManager 和事件生命周期三个核心环节——否则所有“私有源鉴权”“版本号自动升阶”“依赖图强制校验”都会在安装中途被默认逻辑覆盖。
怎么让 Composer 执行你的自定义安装器而不是默认 LibraryInstaller
关键不在插件注册,而在 composer.json 的 type 字段和插件中对 Installer 的显式绑定。Composer 会根据包的 type 查找匹配的 installer,如果你的企业包类型是 enterprise-module,就必须在插件里用 getInstallers() 返回对应实例:
public function getInstallers()
{
return [
new EnterpriseModuleInstaller($this->io, $this->composer),
];
}
常见错误是只监听 post-package-install 事件——此时包已解压完毕,无法干预文件落盘路径或跳过某些 autoload 生成;真正要改行为,必须提供 Installer 子类并重写 install() 和 update()。
-
Installer::install()中可调用$package->setInstallationSource('dist')强制走压缩包而非 Git clone - 若需拦截安装,抛出
RuntimeException即可中断流程(比如校验签名失败时) - 不要在
install()里直接操作vendor/目录——应通过$filesystem实例,它是 Composer 内部统一的文件操作入口
如何在不 fork Composer 的前提下替换默认 RepositoryManager
Composer 启动时会实例化 RepositoryManager 并注入到 Composer 对象中,插件无法直接替换它,但可以通过 PluginInterface::activate() 中的 $composer->setRepositoryManager() 覆盖。前提是你的自定义 manager 继承自原类,并重写 repositories 属性初始化逻辑:
class EnterpriseRepositoryManager extends RepositoryManager
{
public function __construct(IOInterface $io, Config $config)
{
parent::__construct($io, $config);
// 清空默认 repos,注入企业内网源 + 镜像 fallback
$this->repositories = [
new EnterpriseVcsRepository(['url' => 'https://git.internal/foo'], $io, $config),
new CompositeRepository([
new PackagistRepository($io, $config),
new ArtifactRepository('/var/cache/composer/artifacts', $io),
]),
];
}
}
注意:如果企业源需要动态 token(如 JWT),不能把 token 写死在 repo 配置里,而应在 EnterpriseVcsRepository::fetchPackage() 中调用内部认证服务获取临时凭证——否则多项目共用全局 Composer 实例时会因 token 过期导致并发失败。
为什么 pre-autoload-dump 事件里改 autoload 配置没生效
因为 AutoloadGenerator 在触发该事件前,已经从 Package 对象里读取了 autoload 配置并缓存。插件中修改 $package->setAutoload(...) 是无效的,必须在更早的 PRE_FILE_DOWNLOAD 或 POST_PACKAGE_INSTALL 阶段,用 $event->getOperation()->getPackage() 拿到原始 package 实例后,再调用 setAutoload() 并确保该 package 已被 RepositoryManager 缓存更新。
- 最稳妥的做法是在
POST_PACKAGE_INSTALL中,用$composer->getRepositoryManager()->findPackage()重新加载该包,再 setAutoload - 若涉及 PSR-4 映射重写(如把
App\替换为Ent\App\),必须同步调用$package->setAutoloadTypes(['psr-4']),否则 AutoloadGenerator 会忽略新配置 - 别在
pre-autoload-dump里写文件——AutoloadGenerator会清空vendor/composer/下所有 auto-generated 文件,手动写的会被删掉
企业级包管理真正的复杂点不在代码量,而在多个插件之间对同一对象(如 Package、RepositoryManager)的修改顺序和竞态。哪怕只是加一个私有源鉴权,也得确认它是否和团队正在用的 composer-unused 插件共享同一个 Package 实例缓存——否则会出现“本地测试正常,CI 环境 autoload 错乱”的问题。


















