离线环境下 composer 跑不起来,根本原因是默认依赖 packagist.org 元数据校验,即使 vendor 存在或 cache 有 zip,只要未禁用网络且未固化本地元数据源(如 artifact 或 satis),就会因 dist.url 为 https 或无法加载 packages.json 而报 could not fetch;composer_disable_network=1 仅跳过请求,不激活缓存,必须配合 artifact 仓库(zip 命名严格为 vendor-name-version.zip 并配置 repositories)或 satis 静态镜像(含 packages.json + dist zip 并禁用 packagist.org)才能真正离线运行。

离线环境下用 Composer 跑不起来,不是因为没包,而是 Composer 根本不信你本地有的东西——它默认只认 packagist.org 返回的元数据,composer.lock 里写的 dist.url 是 HTTPS 地址,断网就直接报 Could not fetch,连 ~/.composer/cache/files/ 里的 ZIP 都不看一眼。
为什么 COMPOSER_DISABLE_NETWORK=1 单独用没用
这个环境变量只是“跳过网络请求”,但前提是 Composer 已经能从本地读到完整元数据(比如 packages.json)和对应 ZIP。它不负责帮你把缓存“激活”成可信源。常见错误现象:
- 设了
COMPOSER_DISABLE_NETWORK=1,但composer install卡在Loading composer repositories with package information - 报错
Could not fetch https://repo.packagist.org/packages.json,哪怕你刚复制了整个~/.composer/cache/ - 运行
composer config -g -l | grep cache发现cache-files-dir没生效,或者cache-dir和cache-files-dir冲突
根本原因:Composer 的缓存目录是混合结构,只有 files/ 子目录下的 ZIP 文件是离线安装时真正被 --prefer-dist 直接解压的;而 repo/ 下的 packages.json 在离线时无法复用,除非你用 Satis 或 artifact 方式固化元数据。
用 artifact 类型仓库最轻量、最可控
适合已知全部依赖版本、能提前打包、且不想搭 HTTP 服务的场景。它绕过所有远程元数据解析,只读本地 ZIP 并校验文件名和 composer.json。
- ZIP 文件必须严格按
vendor/name-version.zip命名,例如monolog/monolog-2.13.0.zip - 每个 ZIP 内必须包含合法的
composer.json(不能是空或格式错误) -
composer.json中加配置:"repositories": [ { "type": "artifact", "url": "./packages/archives" } ] - 执行
composer install --no-dev --prefer-dist,它会逐个匹配 ZIP 名称,找到就解压,找不到就报Package xxx could not be found - 不支持
dev-main、dev-feature/x等开发分支,锁版本必须是稳定 tag(如2.13.0)
用 satis 构建静态镜像才是真离线
Satis 输出的是完整可替代 Packagist 的静态结构:含 packages.json、provider-*.json、dist/*.zip,离线机只需一个 HTTP 服务就能当源用。
- 联网机构建命令:
php bin/satis build satis.json ./mirror,输出./mirror目录 - 离线机启动服务:
python3 -m http.server 8000(当前目录为./mirror) - 项目中禁用默认源并指向本地:
"repositories": [ { "type": "composer", "url": "http://127.0.0.1:8000" } ], "packagist.org": false - 必须加
"packagist.org": false,否则 Composer 仍会 fallback 回源查缺失版本 - 检查是否生效:
composer config -g -l | grep repos应该看到你配的 URL,且没有packagist.org行
cache-files-dir 是唯一值得配的缓存路径
别碰 cache-dir,它混杂元数据与归档,离线无效;cache-files-dir 只管 ZIP 存放位置,是离线迁移的最小可行单元。
- 联网机先设路径:
composer config --global cache-files-dir /path/to/offline-cache - 再跑:
composer install --no-autoloader --no-scripts --prefer-dist(触发下载并落盘) - 确认路径下有类似
monolog/monolog/2.13.0.0/monolog-monolog-abc123.zip的结构 - 离线机同样执行
composer config --global cache-files-dir /your/copied/path,然后composer install --prefer-dist - 如果之前设过
cache-dir,务必先composer config --global --unset cache-dir,避免冲突
真正麻烦的从来不是“怎么打包”,而是“让 Composer 主动信任你给它的每一条路径”。artifact 和 satis 的区别不在技术难度,而在你愿不愿意为元数据的确定性多走一步:前者靠文件名硬匹配,后者靠 JSON 索引做语义化解析——选哪个,取决于你的发布流程能不能接受手动维护 ZIP 命名,还是必须支持 ^2.0 这类版本约束。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











