composer update --dry-run -v 是确认循环依赖的唯一可靠入口,重点观察末尾是否反复出现同一路径如 vendor/a → vendor/b → vendor/a,出现即闭环实锤;需配合 composer depends --tree 定位链路,且必须满足已执行过 composer install 和带 --tree 参数两个前提。

composer update --dry-run -v 是确认死循环的唯一可靠入口
报错卡在 Resolving dependencies 超过 30 秒、CPU 拉满、终端无响应,大概率不是网络慢,而是 SAT 求解器陷入无限回溯。这时候别等,直接运行:composer update --dry-run -v,重点看最后几行是否反复出现同一组包路径,比如:vendor/a → vendor/b → vendor/a —— 出现两次就是闭环实锤。
注意:composer show --tree 默认包含 require-dev,容易掩盖真实生产依赖链;composer validate 只校验 JSON 格式,对依赖逻辑完全无效。
- 必须加
-v(verbose)才能看到回溯细节,不加就只显示“正在解析”,毫无诊断价值 - 如果输出里出现
Package not found,说明该包根本没进入解析阶段——先composer clear-cache,再删vendor/和composer.lock重试 - Windows 下 cmd 可能截断输出,建议用 PowerShell 或加
| more分页查看
composer depends --tree 是定位闭环路径的唯一有效命令
这个命令不是可选项,是必须项。但它有两个硬前提:项目必须已成功执行过 composer install(保证 composer.lock 有完整快照),且必须带 --tree 参数。不加就只显示一级依赖,根本看不到间接环。
查可疑包时,别只盯着 require,重点检查 require-dev 引入的包是否悄悄污染了主依赖图。例如:phpunit/phpunit 在自己的 composer.json 里写了 "autoload": {"psr-4": {"App\": "../src/"}},就把你的 src/ 加进了它的自动加载路径;而你的代码又用了它的测试基类,运行时就形成隐式闭环。
- 运行
composer depends vendor/package-name --tree,输出中若出现vendor/package ← vendor/other ← vendor/package,箭头方向是“被谁依赖”,自身出现两次即闭环 - 所有
require-dev包的composer.json都要搜../、../../、src/这类路径 - 检查你自己的
autoload-dev是否误把vendor/下的路径写进去了——这等于告诉 Composer:“我依赖我自己”
删 composer.lock 不是解法,是引入新问题的开关
composer.lock 不是缓存,是契约:它明确记录每个包的精确版本、哈希值、下载源和依赖拓扑。直接删掉再跑 composer install,会让 Composer 重算整个依赖树,极大概率导致生产环境行为漂移。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
比如原来锁的是 guzzlehttp/guzzle v7.5.0,删 lock 后可能拉到 v8.x,运行时报 Class GuzzleHttp\Client not found;或者某个间接依赖的 require-dev 里混进了 "phpunit/phpunit": "dev-main",新解析会把它拉进来。
- 真正该用的是
composer update --lock:不升级任何包,只按当前composer.json重新生成composer.lock,刷新结构、补全字段、重排顺序、校验哈希 - 适用场景包括:改了
minimum-stability或platform配置但不想触发包升级;composer.lock被手动编辑或 Git 合并冲突残留脏内容 - 执行后
git diff composer.lock应只显示元数据变动(如content-hash、packages数组重排),不应出现版本号变更
破环只有三种重构方式,没有“绕过”选项
所有临时手段——删 composer.lock、加 --ignore-platform-reqs、调 minimum-stability——都只是掩盖症状。真正解法必须让依赖方向变成单向。
常见隐式循环来源是 autoload + require-dev:你没在 require 里写互相依赖,但项目照样爆红。这时候不能靠参数压制,得动结构。
- 抽离公共契约:把共用接口、DTO、异常类拎出来建新包(如
myorg/contracts),其composer.json中"require": {}必须为空;core和api都只require "myorg/contracts": "^1.0" - 运行时解耦:A 不再
new B()或use BClass,而是定义LoggerInterface(放契约包),B 实现它,A 通过容器获取实例;此时 A 的composer.json里必须删掉对 B 的require - 降级为可选依赖:如果 B 只在 A 的测试中用?移到
require-dev;是增强功能(如导出 Excel)?改用suggest字段提示用户手动安装
别用 replace 或 provide 去伪装兼容——这会让 autoload 错乱,运行时报 Class not found,比原问题更难排查。

















