composer install卡在resolving dependencies超30秒、cpu拉满、无网络请求,即为sat求解器陷入循环依赖死循环;需立即运行composer update --dry-run -v观察末尾重复路径(如vendor/a → vendor/b → vendor/a),或用composer depends --tree定位闭环,且须满足已成功install和带--tree两个前提;隐式循环多源于autoload或require-dev越界加载,破环仅三种方式:抽离契约包、运行时解耦、降级为可选依赖。

Composer install卡在Resolving dependencies就是循环依赖
不是网络慢、不是镜像挂了,是 Composer 的 SAT 求解器正在死循环回溯。CPU 拉满、无网络请求、超过 30 秒没反应,基本可以实锤。它不会报“检测到循环依赖”,而是直接卡住——这不是临时故障,是依赖图逻辑无解。
用composer update --dry-run -v和depends --tree定位闭环
别等它自己退出,立刻执行 composer update --dry-run -v,重点看最后几行是否反复出现同一路径,比如 vendor/a → vendor/b → vendor/a,出现两次即闭环确认。
composer depends vendor/a --tree 是唯一能看清间接环的命令,但有两个硬前提:
- 项目必须已成功运行过
composer install(保证composer.lock有完整快照) - 必须加
--tree参数,否则只显示一级依赖,根本看不到环
如果提示 Package not found,说明该包没进 composer.lock,先删掉 vendor/ 和 composer.lock,再跑 composer update --dry-run 观察第一步失败点。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
autoload 和 require-dev 是隐式循环高发区
很多“循环”根本没写在 require 里,而是靠自动加载悄悄打通的:
-
autoload中声明了"psr-4": {"App\": "src/"},而src/里用了phpunit/phpunit的TestCase -
phpstan/phpstan的composer.json里写了"autoload": {"psr-4": {"App\": "../src/"}},把你的代码路径反向加进了它的加载范围 -
autoload-dev错误地把vendor/下的路径也写进去了——等于告诉 Composer:“我依赖我自己”
验证方法:临时注释掉 require-dev 下所有非核心工具(phpunit、phpstan、infection),再试 composer update;如果恢复成功,问题就在这里。
破环只有三种有效方式,没有“跳过”或“强制安装”
所有靠谱解法都指向一个目标:让依赖方向变成单向。
- 抽离契约包:把双方共用的接口、DTO、异常类拎出来建新包(如
myorg/contracts),然后myorg/core和myorg/api都只require "myorg/contracts": "^1.0",删掉彼此的require - 运行时解耦:
A不再new B()或use BSomeClass,而是定义interface ServiceInterface(放契约包里),B实现它,A通过容器或工厂获取实例——此时A的composer.json里彻底删掉对B的require - 降级为可选依赖:如果
B只在A的测试里用(比如 Mock 数据),就移到require-dev;如果是增强功能(如导出 Excel),改用suggest字段提示用户手动安装
最容易被忽略的是 autoload 路径越界和 require-dev 的反向加载——它们不报错,却让 composer install 在静默中失败。重构前务必先验证是不是这类隐式循环。










