
Composer插件必须实现PluginInterface接口并定义activate()和deactivate()方法,通过EventSubscriberInterface监听事件,从root package读取extra配置,本地测试需用path repository并及时dump-autoload。
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 参数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",确保指向本地目录composer dump-autoload -d ../my-composer-plugin,否则类找不到echo 或 file_put_contents 比 xdebug 更快定位是否进入事件回调;但上线前务必删掉所有调试输出,否则会污染 composer 的 JSON 输出格式,导致 CI 工具解析失败。