Composer插件必须实现PluginInterface接口并定义activate()和deactivate()方法,通过EventSubscriberInterface监听事件,从root package读取extra配置,本地测试需用path repository并及时dump-autoload。

Composer插件必须实现 Composer\Plugin\PluginInterface
不实现这个接口,Composer 根本不会识别你的类为插件。它不是靠命名或目录约定触发的,而是 Composer 在加载时明确反射并检查该接口。
常见错误是只写了个普通类、加了 composer.json 声明 type: composer-plugin,但没实现接口——结果插件静默失效,没有任何报错提示。
- 必须在类中实现
activate()和deactivate()两个方法(哪怕空着) -
activate()的第二个参数是Composer\Composer实例,这是你操作依赖、事件、IO 的唯一入口 - 不要在
activate()里做耗时操作,比如远程请求或文件扫描,否则会拖慢所有composer install/update
监听事件要用 Composer\EventDispatcher\EventSubscriberInterface
单纯激活插件没用,真正干活得靠事件。比如想在 post-install-cmd 后生成配置文件,就得订阅对应事件。
注意:事件名不是字符串随意写,必须严格匹配 Composer 内置事件常量,比如 ScriptEvents::POST_INSTALL_CMD,拼错或大小写不对都会失效。
- 在
activate()中调用$eventDispatcher->addSubscriber($this)才能注册监听 - 每个事件处理方法必须以
on开头,如onPostInstallCmd(),且接受一个CommandEvent参数 - 别在事件回调里 throw 异常来“中断流程”——这会让整个 Composer 命令失败,用户看到的是红字报错,不是你预期的友好提示
composer.json 的 extra 配置要手动读取
用户可能想通过 extra.my-plugin.option 控制行为,但这部分不会自动注入到插件类里,必须自己解析。
很多人以为声明了 extra 就能在插件里直接访问,结果拿到的是空数组——因为 Composer 实例的 getPackage() 返回的是 root package,而 extra 是它的属性,得链式取出来:
if ($extra = $composer->getPackage()->getExtra()) {
$option = $extra['my-plugin']['option'] ?? false;
}
- 务必做空值判断,
getExtra()可能返回null - 不要把敏感逻辑(如密钥、路径)硬编码进插件,优先走
extra或环境变量 -
extra只在 root package 中有效;如果插件被其他包依赖,子包里的extra不会被父项目插件读到
本地测试插件不能只靠 composer require
开发阶段直接 composer require 本地路径,容易因 autoload 冲突或版本约束失败。最稳的方式是用 path repository + dev-main 版本锁定。
典型翻车点:改了插件代码却没清 Composer 的 cache,或者没执行 composer dump-autoload,导致新方法不生效,还以为是逻辑问题。
- 在项目根目录
composer.json加:"repositories": [ { "type": "path", "url": "../my-composer-plugin" } ] - 然后运行
composer require "myvendor/my-composer-plugin:@dev",确保指向本地目录 - 每次改完插件 PHP 文件后,执行
composer dump-autoload -d ../my-composer-plugin,否则类找不到
echo 或 file_put_contents 比 xdebug 更快定位是否进入事件回调;但上线前务必删掉所有调试输出,否则会污染 composer 的 JSON 输出格式,导致 CI 工具解析失败。










