死循环确认方法:卡在“Resolving dependencies”、CPU≥95%、内存飙升、无日志输出、无Downloading或lock写入;出现cycle提示或--dry-run中同一路径反复跳转(如a→b→a)即为SAT求解器死锁。

composer 下载卡在 “Resolving dependencies” 不是网络慢,是 SAT 求解器在版本约束里无限回溯——它找不到一组满足所有 require、require-dev 和 PHP 平台约束的包版本。此时换镜像、清缓存、重启终端全无效。
怎么确认是死循环,不是下载卡住
真正死循环时:composer update 或 composer install 会卡在 Resolving dependencies 阶段,终端无输出、CPU 持续 95%+、内存每秒涨几十 MB、几十秒后直接退出,且 composer.lock 不更新、vendor/ 不生成、日志里没有 Downloading 或 Writing lock file。
若看到 Downloading https://... 或反复 retry,那才是网络问题;若压根没走到这步,就是解析死锁。
- 错误信息里出现
cycle和闭环包名(如myapp/core → myapp/api → myapp/core),是铁证 -
composer update --dry-run -v的末尾几行若反复出现同一跳转路径(如a → b → a),或某个包名连续出现 ≥3 次且父级不同,基本可定性 - 运行
composer why-not vendor/package:version(例如composer why-not laravel/framework:11.0)能快速定位阻断链,比瞎猜快得多
composer depends --tree 才能看清隐式环
80% 的“循环”根本没写在 require 里,而是靠自动加载悄悄打通的——比如 phpunit/phpunit 的 autoload 里写了 "../src/",就把你的 src/ 加进了它的加载路径;而你的代码又继承了它的 TestCase,运行时就形成隐式闭环。
- 必须满足两个前提:
composer.lock存在且有效,且命令带--tree参数,否则只显示一级依赖,完全漏掉A→C→B→A这类多跳环 - 执行
composer depends myorg/core --tree,若输出含myorg/core ← myorg/api ← myorg/core(箭头方向是“被谁依赖”),自身出现两次即闭环成立 - 若提示
Package not found,说明该包没进composer.lock——先删vendor/和composer.lock,再跑composer update --dry-run -v看第一步失败点
require-dev 和 autoload 是隐式循环高发区
临时注释掉 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/下的路径加进来了(例如"vendor/myorg/core/src") - 某些包(如旧版
phpunit)会通过psr-4自动加载规则反向加载你的代码,这种双向耦合极易触发求解器死锁
别碰 process-timeout 或镜像源
这两个参数对 Resolving dependencies 阶段完全无效:process-timeout 控制的是 git clone、unzip 这类子进程,而镜像源只加速 Downloading。你调高它们,只会让死锁过程更久、更耗资源。
真正要做的,是缩小约束空间:降级 PHP 版本要求(如把 "php": "^8.2" 改成 "php": ">=8.0.0 )、锁定关键包版本(如 <code>"guzzlehttp/guzzle": "7.8.1" 而非 "^7.0")、删掉暂时不用的 require-dev,再重试。
最常被忽略的一点:Composer 3.x 默认启用 classmap-authoritative,每次 update 都强制重扫整个 vendor/ 目录——这不是死循环,但会极大拖慢响应,让你误判为卡住。用 composer update --no-autoloader 先跳过 autoload 重建,之后单独 composer dump-autoload --optimize。


















