COMPOSER_DEBUG=1 对调试自定义插件基本没用,它仅控制Composer加载插件时的极简提示,不透出插件代码执行日志,也不影响事件回调运行;插件中echo、var_dump等输出会被Composer捕获丢弃,必须使用$io->writeError()才能被-vvv捕获。

COMPOSER_DEBUG 对调试自定义插件基本没用——它只控制 Composer 自身加载插件时的极简提示(比如“Loading plugin X”),不透出你的插件代码执行日志,也不影响事件回调是否运行。
为什么 COMPOSER_DEBUG=1 看不到插件里的 echo 或 var_dump
Composer 会捕获并丢弃插件中所有直接输出(echo、print、var_dump、error_log),这些内容不会出现在终端,也不会被 -vvv 捕获。它只接管自己框架层的日志通道,而插件代码运行在独立上下文中。
- 现象:加了
COMPOSER_DEBUG=1后仍看不到插件activate()里的调试语句 - 原因:该环境变量不改变输出重定向行为,也不启用插件内部日志转发
- 替代方案:必须改用
Composer\Util\OutputHandler::getOutput()->writeError(),才能确保日志被-vvv捕获并强制刷出
真正起作用的日志方式:用 writeError() 而不是 echo
只有通过 Composer 提供的 IO 实例输出,才能和 -vvv 级别对齐,并绕过缓冲干扰。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 在
activate()开头写:$io->writeError('[my-plugin] activated'); - 监听事件时,用
$event->getIO()->writeError('[my-plugin] post-install triggered'); - 避免用
write()(会被--no-ansi或静默模式吞掉),writeError()始终走 stderr,最可靠 - 加上
[my-plugin]前缀,后续可用composer install -vvv 2>&1 | grep '\[my-plugin\]'快速过滤
验证插件是否真被加载的三步检查法
光看 -vvv 输出不够,得主动确认生命周期是否走到关键节点。
- 运行
composer show --plugins:如果插件不在列表里,说明注册失败,立刻检查composer.json的type是否为composer-plugin,以及autoload路径是否正确 - 检查
vendor/composer/autoload_plugins.php:打开这个文件,搜索你的插件类名,若无条目,Composer 根本没识别到它 - 在
activate()第一行抛异常:throw new \Exception('PLUGIN LOADED');—— 如果命令报错且堆栈含这行,证明插件已加载;若完全没反应,说明activate()根本没被调用
调试时别踩的三个硬坑
很多插件卡在“看起来没执行”,其实败在细节上。
-
extra.plugin-class配置错误或缺失:新版 Composer 推荐自动发现,但如果你显式写了这个字段,类名拼错、命名空间没 autoload、或类不存在,都会导致静默跳过 - 事件监听名写成
ScriptEvents::POST_INSTALL_CMD:这是脚本事件常量,插件监听要用字符串'post-install-cmd',写错就完全不触发 - 在
deactivate()里做清理但抛了异常:Composer 不会记录,也不会报错,只会让插件下次无法重新激活,且-vvv里没有任何提示
插件调试真正的分水岭不在日志开关,而在你是否把输出交给了 Composer 的 IO 实例——其他所有方式,包括 COMPOSER_DEBUG、echo、甚至 file_put_contents(除非你确定路径可写且权限对),都只是临时补救。一旦依赖逻辑变复杂,只有 writeError() + -vvv + composer show --plugins 这套组合能稳定复现和定位问题。

















