deactivate() 方法在 composer 插件中永远不会被自动调用,因其设计上无卸载机制;资源清理需改用事件钩子(如 post-install-cmd)、信号捕获或显式释放,且临时文件须带时间戳与超时约束。

deactivate 方法在 Composer 插件里根本不会被自动调用
Composer 官方插件机制(PluginInterface)只定义了 activate() 和 deactivate() 两个方法,但实际运行中——deactivate() 永远不会被 Composer 自己调用。这不是 bug,是设计如此:Composer 的生命周期极短,一次命令执行完进程就退出,没有“卸载插件”这个概念。
所以,如果你在插件里写了 deactivate() 并指望它清理临时文件、关闭连接或释放资源,那它大概率永远不会执行。
- 常见错误现象:
deactivate()里写的日志没输出、文件没删掉、socket 没 close,误以为是逻辑写错了 - 真实使用场景:只有在你手动测试时用
new Plugin()实例化再显式调用deactivate(),才可能触发 - 别把它当 PHP 的
__destruct()或 Laravel 的 service providerboot()/shutdown()—— 它不具备运行时生命周期管理能力
想做资源清理?得靠 CLI 命令钩子或信号捕获
真正能控制执行时机的,是 Composer 提供的事件钩子(EventSubscriberInterface),或者更底层的 PHP 进程控制手段。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
-
post-install-cmd和post-update-cmd是最常用的清理入口,适合删缓存、重写配置等“命令结束后”的事 - 如果需要在任意命令中途退出时也清理(比如用户按 Ctrl+C),可以用
pcntl_signal()捕获SIGINT,但要注意:Composer 默认不启用 PCNTL 扩展,且某些容器环境禁用信号处理 - 避免在
pre-*钩子里做释放操作——那是准备阶段,资源可能还没分配 - 示例:在
post-install-cmd中清理旧构建产物:if (is_dir($this->cacheDir . '/build_old')) { exec('rm -rf ' . escapeshellarg($this->cacheDir . '/build_old')); }
临时资源必须带超时或命名约束,不能依赖 deactivate
很多插件会生成临时目录、锁文件、HTTP 连接池之类的东西。既然 deactivate() 不可靠,就得从设计上规避“残留”问题。
- 所有临时路径必须包含时间戳或随机后缀(如
tmp_build_20240521_abc123),避免多实例冲突 - 关键临时文件加
.lock后缀,并在创建前检查是否已存在且超过 10 分钟未更新——直接跳过或强制清理 - 不要用
register_shutdown_function():Composer 命令可能通过exit()或异常提前终止,shutdown 函数不一定跑得完 - 数据库连接、cURL handle 等长生命周期资源,应在每个具体命令逻辑内显式
unset()或curl_close(),别攒到“最后”
Composer 2.5+ 的 plugin-api-version 兼容性陷阱
新版本 Composer 对插件接口更严格,尤其在事件订阅和返回值类型上,容易让本想“兜底清理”的代码直接报错退出,反而导致清理逻辑彻底失效。
- 如果声明了
"plugin-api-version": "2.4"却用了Event::POST_INSTALL_CMD(它是 2.5+ 引入的常量),运行时报Undefined class constant 'POST_INSTALL_CMD' - 返回非
null或非void的activate()方法,在 Composer 2.5+ 会警告并可能中断后续事件派发 - 建议:插件
composer.json中明确写"plugin-api-version": "2.5",并用class_exists(Event::class)做运行时降级判断
deactivate(),而是接受它只是个空壳——所有清理逻辑都得自己找时机、设约束、加兜底,否则上线后某天磁盘突然爆满,翻日志才发现那个方法三年没跑过。










