根本原因是Composer默认强制联网校验远程元数据,必须设COMPOSER_DISABLE_NETWORK=1才能禁用;否则即使vendor和lock完整,仍会报Could not fetch,且所有--no-xxx参数均无效。

为什么 vendor 存在、lock 文件完整,composer install 还报 Could not fetch
根本原因不是文件缺失,而是 Composer 默认会主动请求远程元数据(如 packages.json),哪怕所有本地文件都齐备。这个行为无法跳过,除非显式切断网络通路并禁用触发点。COMPOSER_DISABLE_NETWORK=1 是硬性前提——不设它,--no-plugins、--no-scripts 等参数全无效。即使你已断网,只要没设该环境变量,Composer 仍会尝试发起 HTTP 请求并超时失败。
离线安装必须执行的命令组合
这组参数不是“可选优化”,而是防止意外联网的强制防护。前提是:vendor/ 已存在、composer.lock 未改动、PHP 版本和扩展(mbstring、openssl、zlib)完全一致:
COMPOSER_DISABLE_NETWORK=1 composer install --no-plugins --no-scripts --no-autoloader- 紧接着必须补一记:
composer dump-autoload -o,否则运行时大概率报Class not found
注意:--no-autoloader 是绕过 autoload 重建的关键,某些自定义 autoloader 实现会在初始化时触发远程类查找;--no-plugins 可防止 phpstan/extension-installer 等插件隐式联网。
缓存目录在离线场景下反而会坏事
离线时,Composer 的缓存目录(默认 ~/.composer/cache)如果残留了部分元数据或损坏的 archive,会导致 install 阶段误判并尝试回源校验。尤其当缓存中存在 packages.json 但缺失对应 dist 包时,它不会静默降级,而是直接报错退出。
- 清理缓存不能只靠
composer clear-cache:进缓存目录手动确认downloads/和files/子目录为空 - 若项目曾配置过全局镜像(如
repo.packagist),需运行composer config --global repo.packagist false彻底禁用 fallback 校验 - 若
composer.lock中有"source"类型条目(如 Git URL 或dev-master),离线时无法校验,install必然失败——这类条目必须提前转为"dist"类型或移除
本地 path 仓库是离线开发更可靠的替代方案
比起依赖 COMPOSER_DISABLE_NETWORK 走完整流程,对关键依赖改用 "type": "path" 更可控。例如在 composer.json 中配置:
"repositories": [
{
"type": "path",
"url": "../my-dependency"
}
],
"require": {
"my-vendor/my-dependency": "@dev"
}
这样 Composer 完全跳过 Packagist 查询,也不依赖 lock 文件中的 dist URL 或 source commit hash。适用于跨库联调、调试、贡献 PR 等场景,且天然适配离线环境——只要本地目录存在,就能加载。
真正容易被忽略的是:一旦用了 path 仓库,composer update 就不再更新该包(除非你删掉 vendor/my-vendor/my-dependency 并重新 install),它的版本状态由本地文件系统决定,而非 lock 文件。











