Composer插件是干预其自身行为的钩子系统,需同时满足type为composer-plugin、require composer-plugin-api版本匹配、extra.class指向正确类;事件监听必须在activate()中注册且参数类型严格匹配;路径配置须通过$composer->getConfig()->get()获取,避免依赖getcwd()或相对路径;deactivate()需清理静态状态以防污染。

Composer 插件机制不是“给项目加功能”的库,而是让代码能干预 Composer 自身行为的钩子系统——它在 composer install、composer update 等命令执行过程中,允许你监听事件、修改依赖解析、注册新命令,甚至重写包安装逻辑。
插件被识别的前提:type + interface + extra.class 缺一不可
Composer 不靠目录名或文件位置判断是否为插件,只认 composer.json 里的三个硬性字段:
-
"type": "composer-plugin"—— 没这行,整个包会被当普通库加载,activate()根本不会调用 -
"require": { "composer-plugin-api": "^2.0" }—— 必须匹配当前 Composer 主版本(2.x 项目不能写^1.1),否则安装时报class not found或方法不存在 -
"extra": { "class": "MyVendor\MyPlugin" }—— 类名必须和autoload路径下实际存在的类完全一致,大小写敏感,PSR-4 映射错一个字符就静默失败
事件监听必须在 activate() 里注册,且签名要严格匹配
插件逻辑不能写在构造函数里,也不能靠 __invoke 自动触发。所有动作必须在 activate(Composer $composer, IOInterface $io) 中显式挂载:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 监听
post-install-cmd时,回调函数参数类型必须是CommandEvent,写成Event或空参数会导致运行时报ArgumentCountError - 想改自动加载逻辑?得监听
post-autoload-dump,而不是post-install-cmd—— 后者触发时 autoload 文件还没生成,改了也白改 - 监听器内禁止抛出未捕获异常,否则整个
composer install会中断;建议用try/catch包裹业务逻辑,并用$io->writeError()输出提示
路径和配置读取别依赖 getcwd(),要用 $composer->getConfig()->get()
插件可能在任意工作目录下被加载(比如 CI 中从 /tmp 运行),getcwd() 和相对路径 ./composer.json 都不可靠:
- 获取 vendor 目录路径,应调用
$composer->getConfig()->get('vendor-dir'),而不是拼接__DIR__ . '/../../vendor' - 读取当前项目
composer.json内容,应通过$composer->getPackage()获取已解析并校验过的对象,而非file_get_contents('composer.json') - 修改配置或写文件前,先确认是否在项目根目录上下文 ——
$composer->getPackage()->getName()不为空才代表已成功加载项目元数据
最常被忽略的一点:插件的 deactivate() 方法虽可空实现,但若你在 activate() 中注册了全局静态状态(如自定义安装器、临时替换类映射),就必须在 deactivate() 中清理,否则多项目共存或测试时会出现状态污染。

















