插件激活时应在activate()中仅做轻量注册,重操作须延迟至具体事件;应实现EventSubscriberInterface而非手动addListener;避免在post-autoload-dump中修改autoload结构;远程调用必须设超时并降级。

插件激活时不要在activate()里做重操作
Composer 插件的 activate() 方法在每次 Composer 命令启动时都会执行(哪怕只是 composer -V),如果这里触发文件扫描、远程请求或 autoload 生成,会直接拖慢所有命令响应。实际项目中见过插件在 activate() 里调用 $composer->getPackage()->getAutoload() + 递归解析 PSR-4 映射,导致 composer list 从 0.1s 拉长到 1.8s。
建议只做轻量注册:绑定事件监听器、检查依赖是否存在、读取 extra 配置。重逻辑必须延迟到具体事件触发后,比如 post-install-cmd 或 post-autoload-dump。
用EventSubscriberInterface替代手动addListener()
手动在 activate() 中调用 $eventDispatcher->addListener() 容易遗漏优先级控制,且无法声明“仅对特定命令生效”。而实现 EventSubscriberInterface 并返回带权重的数组,能明确约束执行时机:
-
'pre-install-cmd' => ['onPreInstall', 10]:数字越小优先级越高,可用于抢占式校验 -
'post-update-cmd' => 'onPostUpdate':不带权重则默认为 0,适合后置清理 - 若订阅
pre-file-download却没做并发限流,多个包并行下载时可能触发 API 频控
注意:Composer 2.x 的 EventDispatcher 不支持异步事件,所有回调都是同步阻塞执行,别在里面写 sleep() 或 file_get_contents() 远程地址。
避免在post-autoload-dump中修改 autoload 结构
这个事件常被误用于“动态追加 PSR-4 映射”,但 Composer 在 dump 阶段已锁定 autoload 生成器状态。强行调用 $generator->addPsr4() 可能导致:
- 类映射重复(同一命名空间被注册两次)
- 生成的
vendor/autoload.php包含无效require_once路径 - 后续
composer dump-autoload --optimize失败,报错Class not found in generated classmap
正确做法是:在 activate() 中通过 $composer->getPackage()->setAutoload() 提前注入配置,或改用 post-install-cmd + 外部脚本生成独立 autoloader 文件。
插件内远程调用必须加超时和降级
不少插件会在 pre-command-run 或 pre-file-download 中请求私有仓库元数据、校验 license、上报统计。这些操作一旦卡住,整个 Composer 流程就挂起——没有内置超时机制。
务必做到:
- 使用
stream_context_create(['http' => ['timeout' => 3]])控制 HTTP 超时 - 把远程调用包裹在
try/catch中,并 fallback 到本地缓存或默认值 - 禁用插件内任何
exec()或shell_exec()调用,CI 环境常禁用这些函数 - 别在插件里写日志到
/var/log,应统一走$io->writeError()输出到 stderr
最隐蔽的负载陷阱是插件悄悄启用了调试模式(如 define('DEBUG_PLUGIN', true)),导致每秒写数百行 debug 日志到磁盘,尤其在 composer update 解析几百个包时瞬间打满 I/O。











