Composer install卡在Resolving dependencies且CPU跑满,基本是循环依赖而非版本冲突;需用composer depends --tree定位闭环链路,检查require-dev和autoload中的隐式依赖,破环只能通过抽离契约包、拆分单体包或移除反向autoload映射实现。

Composer install卡在Resolving dependencies就是循环依赖
看到命令行长时间停在Resolving dependencies、CPU跑满、无网络请求,基本可以断定是循环依赖——不是版本冲突,是依赖图本身逻辑无解。Composer不会尝试“绕过”,它直接退出并报错,比如Package a depends on b which depends on a。这种错误不能靠composer update或删vendor解决,因为问题不在版本号,而在包之间的引用关系。
用composer depends --tree定位闭环链路
这个命令是唯一能看清间接环的工具,但前提是composer.lock存在且完整(即项目之前成功执行过composer install):
- 必须加
--tree参数,否则只显示一级依赖,根本看不到闭环 - 运行
composer depends myorg/core --tree,如果输出里出现myorg/core ← myorg/api ← myorg/core,箭头形成闭环,就是真实循环点 - 若提示
Package not found,说明该包没进composer.lock——先composer clear-cache,再删vendor/和composer.lock,重跑composer update --dry-run看卡在哪一步
require-dev是隐藏循环高发区
很多“循环”根本没写在require里,而是靠autoload悄悄打通的。比如phpunit/phpunit的composer.json里写了"autoload": {"psr-4": {"App": "../src/"}},就把你的src/加进了它的加载路径;而你的代码又继承了它的TestCase,运行时就形成隐式闭环。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 临时注释掉
composer.json中所有非核心的require-dev条目(如phpunit/phpunit、infection/infection) - 再跑
composer update --dry-run,如果不再卡住,问题就出在这里 - 逐个恢复
require-dev包,配合composer show package-name查它的autoload配置,特别留意含../、../../、src/的路径 - 检查你自己的
autoload-dev是否误把vendor/下的路径写进去了——这等于告诉 Composer:“我依赖我自己”
破环只有三种有效方式
没有--force、--skip-cycle这类开关。Composer不提供“忽略循环”的选项,只有结构重构:
- 抽离契约包:把双方共用的接口、DTO、异常类拎出来,建新包
myorg/contracts,然后myorg/core和myorg/api都只require "myorg/contracts": "^1.0" - 拆分单体包:把原本打包在一起的
myorg/core按职责切成myorg/core-api和myorg/core-domain,让api层依赖domain,但domain不反向依赖 - 移除 autoload 反向映射:如果你的
autoload-dev里写了"psr-4": {"Tests\": "vendor/myorg/core/tests/"},立刻删掉——这是最常被忽略的破环入口
循环依赖的本质是设计耦合,不是配置问题。查到闭环后,重点不是改命令,而是改包结构或autoload路径。

















