Composer update默认必须联网,因其强制刷新packages.json元数据以重算依赖图,无法跳过;彻底离线需设COMPOSER_DISABLE_NETWORK=1并配合--no-plugins --no-scripts --no-autoloader --no-install,且依赖已预置本地cache或path仓库。

Composer update 本身无法完全脱离网络下载,但可以大幅压缩其联网行为——核心是绕过元数据刷新、复用本地缓存、强制走 dist 包、并切断所有远程 fallback 路径。
为什么 update 默认必须联网
即使 vendor/ 和 composer.lock 都完整,composer update 仍会默认请求 https://repo.packagist.org/packages.json(或你配置的镜像源)来刷新整个包索引。这不是“检查更新”,而是依赖图重计算的前置步骤,无法跳过,除非显式禁用元数据加载机制。
常见错误现象:[Composer\Downloader\TransportException] Could not fetch https://repo.packagist.org/packages.json,哪怕你只是想升级一个已知存在的本地包。
- 它不看
vendor/是否存在,只认元数据是否新鲜 - 即使你只改了
composer.json中的一个版本号,update 仍会拉全量 packages.json -
--no-scripts或--no-autoloader对这一步完全无效
用 --lock 和 --no-install 替代 update(最轻量联网方案)
如果你只是想让 composer.lock 同步 composer.json 的版本变更(比如手动改了 "monolog/monolog": "^3.0"),但不想触发任何下载,就别用 update,改用:
composer update --lock --no-install
这个组合的效果是:
-
--lock:仅重写composer.lock,不碰vendor/ -
--no-install:跳过所有包下载和解压逻辑 - 仍然会联网拉取 packages.json —— 但只拉一次,且不校验每个包的 dist ZIP
注意:--lock 不会验证新版本是否存在,它只按你写的版本字符串生成 lock 条目;后续 install 时才真正校验。所以该命令适合 CI 中“预生成 lock 文件”的场景,而非日常开发。
彻底离线 update 的唯一可行路径:提前准备 + path 仓库
真要零网络执行 composer update,必须放弃从 packagist 拉包的模式,改用 path 类型仓库。这意味着:
- 你得提前把目标包(如
laravel/framework)的指定版本 ZIP 或源码目录,放在本地某路径下(例如./packages/laravel-framework) - 在
composer.json中声明仓库:"repositories": [{"type": "path", "url": "./packages/laravel-framework"}] - 该包在
require中必须带-dev或-x.y.z明确后缀,否则 Composer 会忽略 path 仓库 - 运行
COMPOSER_DISABLE_NETWORK=1 composer update laravel/framework --no-plugins --no-scripts
关键限制:path 仓库只对「你明确列出的包」生效,不能自动覆盖其传递依赖;如果 laravel/framework 依赖 symfony/console,而你没也给它配 path,update 仍会尝试联网找 symfony/console。
容易被忽略的坑:autoload 重建与插件干扰
即使你成功绕过了下载,composer update 默认仍会触发 autoload 重建和插件初始化,这两者都可能隐式联网:
-
phpstan/extension-installer等插件会在post-update-cmd中探测远程扩展注册表 - 某些自定义
autoload-dev规则(如扫描tests/下所有*Test.php)可能触发文件系统遍历失败,被误判为需要远程补全 -
--no-autoloader不能和--lock共存,但可与--no-install同时用;若你不需要立即运行代码,加它能跳过所有 autoload 相关风险
最稳的离线 update 命令其实是:COMPOSER_DISABLE_NETWORK=1 composer update --no-plugins --no-scripts --no-autoloader --no-install,前提是你的 composer.lock 已人工维护好、所有依赖包 ZIP 都在 cache-files-dir 里备妥、且没用任何 require-dev 里的动态脚本。











