离线安装失败主因是未禁用网络请求,必须设COMPOSER_DISABLE_NETWORK=1并配合--prefer-dist、完整dist包及本地仓库配置,否则Composer默认跳过缓存直接联网校验元数据。

离线安装失败,八成是因为没关网络请求
Composer 默认行为是“宁可报错也不用缓存”,哪怕 ~/.composer/cache/files/ 里明明有 monolog/monolog-2.12.0.zip,它也会先去请求 https://repo.packagist.org/packages.json。不显式切断网络,缓存根本不会被读取。
必须在离线机执行前设置环境变量:COMPOSER_DISABLE_NETWORK=1。Linux/macOS 直接前缀运行,Windows 需先 set COMPOSER_DISABLE_NETWORK=1(CMD)或 $env:COMPOSER_DISABLE_NETWORK="1"(PowerShell)。
-
--prefer-dist是硬性要求:只有 dist 包(ZIP/TAR)才走缓存路径;source 类型(如"type": "git")会跳过缓存直接报错 -
--no-scripts --no-plugins必须带上:某些插件(如hirak/prestissimo)或包的post-install-cmd会绕过COMPOSER_DISABLE_NETWORK自行发起 HTTP 请求 - 命令示例:
COMPOSER_DISABLE_NETWORK=1 composer install --prefer-dist --no-scripts --no-plugins --no-dev
只拷 cache/files/ 是最常见错误
缓存目录不是“ZIP 包仓库”,而是三部分协同工作的元数据系统:files/(包本体)、repo/(JSON 映射表,告诉 Composer “symfony/console v6.4.0 对应哪个 ZIP 的 sha256”)、archived/(归档缓存,用于 composer archive 场景)。缺任一,Composer 就 fallback 到联网。
迁移前用 composer config --global cache-dir 查真实路径(Linux/macOS 默认 ~/.composer/cache,Windows 是 %LOCALAPPDATA%\Composer\cache),然后打包整个根目录——包括隐藏文件和子目录,不要过滤。
- 验证是否完整:解压后检查
repo/https---packagist.org/packages.json是否存在且非空(内容应为 JSON 格式,含大量包名与版本映射) - 离线机上需同步执行
composer config --global cache-dir /path/to/restored/cache,否则 Composer 仍会用默认路径,忽略你拷来的缓存 - 若用的是 Composer 2.9.6,注意其缓存结构未变,但校验逻辑更严格,旧版缓存可能因哈希算法差异被拒绝
用 --repository-url=file:// 比依赖全局缓存更可控
当项目依赖私有包、或需要精确控制 ZIP 文件来源时,全局缓存容易因路径/权限/用户问题失效。此时把所有 dist ZIP 按 vendor/name/version-hash.zip 结构整理进一个本地目录,再用 --repository-url=file:///path/to/dist 直接指向,能绕过缓存解析逻辑,强制走本地文件。
操作要点:
- 联网机上提取 ZIP:从
~/.composer/cache/files/中按composer.lock里的dist.shasum和dist.url找对应文件,或直接全量复制该目录下所有 ZIP - 确保 ZIP 命名规范:例如
monolog/monolog/7c8a3e7b5d8a3e7b5d8a3e7b5d8a3e7b5d8a3e7b5d8a3e7b5d8a3e7b5d8a3e7b.zip(hash 长度需匹配 lock 文件记录) - 离线机命令:
composer install --prefer-dist --repository-url=file:///opt/composer-dist/—— 注意 URL 开头是file://,且路径必须绝对、可读 - 失败时优先检查:
composer.lock中某个包的dist.shasum是否能在本地目录找到同名 ZIP;文件权限是否允许 Composer 读取
composer install --no-install 不是“预下载”,而是缓存准备指令
很多人误以为 composer install --no-install 是个无害的预热命令,其实它是离线方案中唯一能触发“仅下载 dist ZIP、不解压不执行脚本”的核心动作。它依赖 composer.lock 解析所有包的 dist URL,并把 ZIP 写入 cache/files/,同时更新 cache/repo/ 中的元数据映射。
执行前必须确认:
-
composer.lock已存在且未被修改(不能用composer update临时生成,否则 hash 不一致) - 已设
COMPOSER_PREFERS_DIST=1或composer.json中配置"prefer-dist": true - 私有包源已注册为 dist 类型(如 GitLab Releases 提供 ZIP 下载地址),否则该命令会跳过或报错
- 命令示例:
COMPOSER_PREFERS_DIST=1 composer install --no-install --prefer-dist --no-scripts --no-plugins
真正容易被忽略的是:这个命令本身不生成 vendor/,也不写任何 autoload 文件,它只做一件事——把锁文件里每一项对应的 dist ZIP 安静地塞进缓存。离线机后续的 composer install 能否成功,就取决于这一步有没有跑完、跑对、跑全。


















