Composer插件必须实现PluginInterface并在composer.json中声明"type": "composer-plugin",否则不会加载;需通过PSR-4自动加载,activate()中注册事件监听器,Composer 2.x需兼容EventSubscriberInterface,监听PostInstallEvent而非PackageEvents::POST_INSTALL,全局安装不触发生命周期事件,读取已安装包信息应避免缓存陷阱。

Composer插件必须实现 PluginInterface 才能被识别
Composer 不会自动加载任意 PHP 类作为插件,必须显式声明类实现了 PluginInterface,且在 composer.json 中通过 "type": "composer-plugin" 标识。漏掉任一条件,插件完全不会被加载——连构造函数都不会执行。
实操要点:
-
composer.json中需包含"autoload": { "psr-4": { "MyVendor\MyPlugin\": "src/" } },确保类可被自动加载 - 插件主类必须实现
ComposerPluginPluginInterface,且activate()方法里注册事件监听器(不能只写构造函数) - 若使用 Composer 2.x,还需兼容
ComposerEventDispatcherEventSubscriberInterface,否则install钩子可能静默失效
监听 PostInstallEvent 而非 PackageEvents::POST_INSTALL
官方文档里混用常量和事件类名,容易踩坑:PackageEvents::POST_INSTALL 是旧版(Composer 1.x)的字符串常量,而 Composer 2+ 推荐直接监听具体事件类 PostInstallEvent。用错会导致钩子不触发,且无报错提示。
正确做法:
- 在
activate()中调用$eventDispatcher->addSubscriber($this) - 插件类实现
EventSubscriberInterface,并返回['post-install-cmd' => ['onPostInstall']]或更稳妥的[PostInstallEvent::class => ['onPostInstall']] -
onPostInstall(PostInstallEvent $event)的参数类型必须严格匹配,PHP 8+ 下类型错误会直接跳过回调
post-install-cmd 钩子在全局安装时无效
很多人测试时用 composer global require xxx,却发现自定义插件的 post-install-cmd 完全没执行——这是设计使然。Composer 的生命周期事件(如 PostInstallEvent)只在项目级 composer install 或 composer update 时触发,global require 走的是另一套逻辑,不派发这些事件。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
替代方案:
- 若需干预全局安装,只能监听
CommandEvent并检查$event->getCommandName() === 'global',再手动解析参数 - 更可靠的做法是放弃钩子,改用
scripts配合composer exec,或让使用者显式运行你的 CLI 命令 - 注意:插件自身被全局安装时,它的
activate()会被调用,但后续项目里的install才真正触发生命周期事件
插件中访问已安装包信息需绕过 InstalledRepository 缓存
在 onPostInstall() 里直接调用 $composer->getRepositoryManager()->getLocalRepository(),大概率拿到的是“安装前”的状态——因为 Composer 在事件触发时尚未刷新本地仓库缓存。你看到的仍是旧的 installed.json 内容。
安全读取方式:
- 用
$event->getComposer()->getInstallationManager()->getInstaller('library')->getInstallPath($package)获取路径,再读composer.lock或vendor/composer/installed.json - 或直接解析
$composer->getConfig()->get('vendor-dir') . '/composer/installed.json'(该文件在install结束后才写入) - 避免依赖
$composer->getRepositoryManager()->getLocalRepository()->findPackage(),它返回的是内存中未更新的快照
这件事很容易被忽略:你以为拿到了新装的包信息,其实只是上一轮残留的缓存。

















