composer解析死循环是sat求解器无限回溯,表现为cpu持续95%+、内存每秒涨50mb+、composer update --dry-run -v末尾反复出现同一包路径(如vendor/a→vendor/b→vendor/a),并可用composer depends --tree实锤闭环链。

Composer 解析死循环不是“慢”,是 SAT 求解器在无限回溯——CPU 持续 95%+、内存每秒涨 50MB+、composer update --dry-run -v末尾反复出现同一组包名(如 vendor/a → vendor/b → vendor/a),基本可以确定是闭环卡死了。
看 composer update --dry-run -v 的“临终遗言”
这个命令不改任何文件,但完整走一遍 SAT 求解流程,失败前会打印它最后尝试的组合和立即 reject 的原因。这不是日志,是求解器的决策痕迹。
- 重点盯最后 5–10 行:如果反复出现
vendor/a→vendor/b→vendor/a这类嵌套,就是强循环信号 - 留意
Rejecting vendor/b because it requires vendor/a ^2.0这类语句——而当前锁的是vendor/a:1.9,说明版本约束在闭环里互相拉扯 - 某包名连续出现 ≥3 次(比如
myorg/core被myorg/api、myorg/utils、phpunit/phpunit分别引入),大概率是间接环
用 composer depends --tree 实锤闭环链
这是目前唯一能直接看到闭环路径的命令,但有两个硬前提:项目必须已有有效的 composer.lock,且必须加 --tree 参数。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 运行
composer depends myorg/core --tree,如果输出含myorg/core ← myorg/api ← myorg/core,就是闭环实锤 - 如果报
Package not found,说明该包根本没进composer.lock——删掉vendor/和composer.lock,再跑composer update --dry-run -v看第一步失败点 - 别只查一级依赖:
composer depends myorg/core(无--tree)只会显示直接上游,漏掉A→C→B→A这类多跳环
警惕 autoload + require-dev 构成的隐式循环
最常被忽略的“循环”根本不在 require 字段里,而是测试工具通过自动加载把你的代码反向拉进了它的运行时上下文。
- 检查所有
require-dev包的composer.json,重点搜../、../../、src/、tests/——比如phpunit/phpunit的"autoload": {"psr-4": {"App\": "../src/"}}就会让它加载你的src/ - 确认你自己的
autoload-dev没把vendor/下的路径写进去,否则 Composer 会认为“我依赖我自己” - 临时注释掉非核心
require-dev条目(尤其是infection/infection、phpstan/phpstan插件),再试composer update;很多“死循环”只是测试包在作祟
破环只有三种靠谱方式,没有“跳过”选项
所有临时手段(删 composer.lock、加 --ignore-platform-reqs、改 minimum-stability)都只是掩盖症状。真正解法只有重构依赖方向:
- 抽离契约包:新建
myorg/contracts,里面只放接口、DTO、异常,"require": {}为空;core和api都只require "myorg/contracts": "^1.0" - 运行时解耦:
A不再new B()或use BClass,而是定义LoggerInterface(放契约包),由容器注入具体实现 - 调整
require-dev:把本该只在 CI 中运行的工具(如特定 PHP 版本的分析器)移到platform配置或独立脚本中,避免污染主依赖图
闭环路径越深(比如四跳以上)、涉及的 autoload 越多,手动追踪就越容易漏掉中间节点。别指望 conflict 自动修复——它只提前报错,不改变图结构;真正要动的,是你代码里的 use 和包之间的 require 方向。










