离线可复现依赖的核心是:在有网机器上执行 composer update --with-all-dependencies 确保 composer.lock 含完整 dist 信息和校验和,并将 vendor/ 与 lock 文件一同打包交付,禁用缓存与自动回退,必要时用 --ignore-platform-reqs 跳过平台检查。

用 composer install --no-install 生成离线可复现的锁文件
空气间隙(air-gapped)环境无法联网,所以关键不是“怎么装”,而是“怎么让每个项目在无网时也能还原完全一致的依赖”。核心动作是:所有依赖解析和版本锁定必须在有网机器上做完,且确保 composer.lock 包含完整包元数据(包括 dist 信息和校验和)。
常见错误是只拷贝 composer.json 过去,结果离线运行 composer install 直接报错 Could not find package ... —— 因为没锁版本,也没提前下载包。
- 有网机器上,对每个项目执行
composer update --with-all-dependencies(强制刷新全树),再确认composer.lock中每个dist条目都有url和shasum - 禁用 packagist 自动回退:在
composer.json加"repositories": [{"type": "composer", "url": "https://packagist.org"}],再删掉~/.composer/cache避免本地缓存干扰判断 - 不要用
--no-scripts或--no-plugins生成锁文件,否则某些插件依赖(如hirak/prestissimo)可能被跳过,导致离线行为不一致
把 vendor 和 lock 文件一起打包进离线介质
仅靠 composer.lock 不够。Composer 离线安装时仍会尝试访问网络获取包 zip/tar,除非你提供完整 vendor/ 或预下载的包存档。最稳妥的做法是:把整个 vendor/ 目录 + composer.lock 打包,直接复制过去。
有人想省空间只传 .zip 包,但容易踩坑:Composer 默认不读取任意路径的 zip,除非配置 artifact 仓库 —— 而这个配置本身要写进 composer.json,且只对 require 的顶层包生效,对递归依赖无效。
- 打包前运行
composer install --no-dev --optimize-autoloader,减少体积并排除 dev-only 包 - 确认
vendor/autoload.php可正常 require,避免因路径或权限问题导致 autoload 失败 - 如果项目多、vendor 共享率高,可用
rsync -a --delete增量同步,比每次全量压缩更快
用 composer install --ignore-platform-reqs 绕过 PHP 扩展缺失报错
离线系统常存在 PHP 版本或扩展与开发机不一致,比如开发机有 ext-redis,而目标机没有,composer install 就会卡在平台需求检查阶段,报错 Your requirements could not be resolved to an installable set of packages.
这不是依赖冲突,而是 Composer 在做预检。此时不能改 composer.json,因为会破坏锁文件语义;也不能硬删 platform 配置,因为有些扩展是运行时必需的。
- 加
--ignore-platform-reqs让安装跳过检查,但需确保运行时环境实际满足要求(比如代码真用了Redis::connect(),没扩展就会 fatal error) - 更安全的做法是:在有网机器上用目标环境的 PHP 运行
composer install --dry-run验证兼容性,提前暴露问题 - 若必须支持多 PHP 版本,可在
composer.json中用config.platform.php锁定虚拟平台版本,避免锁文件记录真实环境信息
为什么不用 composer archive 或私有 Packagist?
这两个方案在 air-gapped 场景下反而增加复杂度,不是“不能用”,而是容易忽略部署链路断点。
composer archive 生成的是单个项目源码包,不含依赖,也不带 autoloader 映射;私有 Packagist(如 Satis)需要额外维护 Web 服务、HTTP 路由、HTTPS 证书、用户鉴权 —— 而 air-gapped 系统通常连基础 Web 服务都不允许启用。
- 私有 Packagist 的
packages.json必须定期 rebuild,一旦漏掉某个子依赖的更新,离线install就会失败 -
composer archive输出的 zip 解压后仍是源码,仍需执行composer install,又回到网络问题 - 真正轻量可控的方式,是把「已验证可运行」的
vendor/当作构建产物交付 —— 类似 Go 的静态编译,不追求过程优雅,只保结果确定
最易被忽略的一点:不同项目的 vendor/ 目录不能混用,哪怕包名版本相同。Composer 的 autoloader 是基于当前项目路径生成的,跨项目挪用会导致类加载路径错乱,报 Class not found 却查不出原因。










