最稳离线方案是完整复制vendor目录+composer.lock,前提为php版本、扩展、架构完全一致,并在源机执行composer install --no-dev --optimize-autoloader验证后打包;或用artifact仓库+composer_disable_network=1读本地zip包,文件名须为vendor-name-package-name-version.zip且版本严格匹配。

composer install 本身不支持“纯离线自动解析安装”,但可以离线执行,前提是所有依赖已提前固化、元数据完整、网络路径被彻底切断。靠参数跳过网络(比如 --no-install-recommends)是无效的——这个参数根本不存在,Composer 没有 apt 那套逻辑。
为什么 composer install 在没网时直接报错?
它不是“连不上就放弃”,而是根本没走本地路径:默认行为是先请求 https://repo.packagist.org/packages.json 获取元数据,再逐个校验 composer.lock 里每个包的 dist.url 和 dist.shasum。只要其中任一环节缺本地依据(比如某个包是 "type": "source"),就会中断并报 Could not fetch 或 Package not found。
-
composer.lock缺dist.shasum字段?→ 直接退出,不尝试用 vendor 已有文件 - 某个包写的是
"dev-main"或 Git URL?→ 离线时无法解析版本,install 中断 - PHP 版本或
platform.config不匹配?→ 即使 vendor 存在,也会因约束拒绝加载
最稳的离线部署方案:完整 vendor/ + composer.lock + 强制隔离
别折腾缓存导出或 composer archive —— 它不处理子依赖,生成的 ZIP 也只覆盖当前目录。真正能落地的只有完整 vendor/ 目录打包。
- 在线机执行:
composer install --no-dev --prefer-dist --optimize-autoloader,确保所有子依赖进vendor/,且无 dev-only 文件 - 打包前删掉:
vendor/bin/(Windows 软链在 Linux 失效)、vendor/composer/installed.json(含绝对路径哈希,引发 autoload 异常) - 目标机解压后,必须运行:
COMPOSER_DISABLE_NETWORK=1 composer install --no-plugins --no-scripts --no-autoloader - 最后补一记:
composer dump-autoload -o,强制刷新 autoload,避免路径变更或旧缓存导致类找不到
想后续增删包?只能用 type: "path" 仓库
composer require 默认必须联网查包名和版本,离线不可用。唯一可靠方式是把本地包目录当仓库用:
- 把目标包(含完整
composer.json)放到项目内,如./packages/my-utils - 在项目根目录
composer.json中添加:"repositories": [{ "type": "path", "url": "./packages/my-utils" }] - 运行:
composer require vendor/my-utils:dev-main(注意必须用分支名,path仓库不认^1.0这类约束) - 装完立刻执行:
composer dump-autoload,否则autoload.php不会包含该包
容易踩的坑:url 必须是相对路径;包名必须和其 composer.json 中的 "name" 完全一致;子依赖也得用同样方式提供,否则安装中断。
Bundle 打包的本质是“绕过 Composer 的网络阶段”
所谓 Bundle,并不是 Composer 内置功能,而是人为构建一个可移植的依赖快照。它的可靠性不取决于命令多炫酷,而取决于三件事是否对齐:
- 源与目标机器的 PHP 版本、关键扩展(
openssl、zlib、mbstring)完全一致 -
composer.lock未被修改,且content-hash与当前composer.json严格匹配(换行符、空格都影响) - 所有包均为
dist类型(即"type": "library"),不含"type": "source"或post-install-cmd编译逻辑
一旦其中任何一项不满足,你看到的可能不是报错,而是“看起来成功了,运行时报 Class not found”。这类问题最难排查,因为 autoload 层面的失败往往静默发生。











