composer install卡在resolving dependencies即为循环依赖,非版本冲突或网络问题;需用composer depends --tree定位闭环链路(要求composer.lock有效且带--tree参数),检查require-dev和autoload隐式依赖,破环仅能通过抽离契约包、拆分单体包或运行时解耦实现。

composer install卡在Resolving dependencies就是循环依赖
不是版本冲突,也不是网络或源的问题,是依赖图本身无解。Composer 的 SAT 求解器在闭环里无限回溯,表现为 CPU 持续 95%+、内存每秒涨几十 MB、终端卡住几十秒后突然退出,且 composer.lock 不更新、无网络请求、不报具体错误。这种状态不能靠 composer update 或删 vendor/ 解决——问题不在版本号,而在包之间“你依赖我、我依赖你”的引用关系。
用composer depends --tree定位真实闭环链路
这是唯一能看清间接环的命令,但有两个硬前提:项目必须有有效的 composer.lock,且必须加 --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 -v看卡在哪一步 - 别只查
composer depends myorg/core(无--tree),那只是上游快照,看不出链路折叠
require-dev 和 autoload 是隐式循环高发区
80% 的“循环”根本没写在 require 里,而是靠自动加载悄悄打通的。比如 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、phpstan/phpstan) - 再跑
composer update --dry-run -v,如果不再反复跳转,问题就在这里 - 逐个恢复
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 不反向依赖 - 运行时解耦:用工厂、容器或事件总线替代直接 new 或静态调用,把编译期依赖降为运行期协作——这对已上线项目最实用,但需配套测试覆盖
最容易被忽略的是 autoload 的跨目录映射和 require-dev 的“反向加载”行为,它们不会在 composer show -t 里显形,却能让求解器在毫秒级内陷入死循环。










