composer创建本地仓库不是为替代packagist,而是实现完全离线安装:必须用artifact或path类型;artifact需预置命名规范的.tar.gz包并配置url为本地路径,path需本地源码目录且require时加@dev,同时必须全局及项目级禁用packagist.org。

composer 创建本地仓库不是为了“替代 Packagist”,而是让 composer install 完全不发 HTTP 请求——它必须用 artifact 或 path 类型,不能靠改 repo.packagist 地址假装离线。
用 artifact 仓库类型打包所有依赖(推荐用于部署)
artifact 是 Composer 原生支持的纯离线仓库类型:只读取本地 .tar.gz 文件,不做任何网络探测。
关键不在“怎么建”,而在“包怎么来”:
- 所有压缩包必须提前在联网机生成,命名严格为 vendor-name/package-name-version.tar.gz(例如 monolog/monolog-2.13.0.tar.gz)
- 每个包内必须含有效 composer.json,且 name 和 version 字段要与文件名完全一致
- 把这些包统一放在一个目录下(如 /opt/artifacts/),不要嵌套子目录
- 离线机执行:composer config repositories.offline '{"type":"artifact","url":"/opt/artifacts/"}'
- 后续 composer install 会按 composer.lock 中的 dist.shasum 查找对应文件,校验失败直接报错,不 fallback
用 path 仓库类型链接本地开发包(适合联调)
path 类型跳过元数据请求,直接读取本地目录里的 composer.json,但仅适用于你手头已有完整源码的包。
常见误操作是路径写成绝对路径或漏掉 options:
- 包目录必须放在项目内可移植位置(如 ./packages/my-utils),不能是 /home/user/my-utils
- composer.json 中配置必须带 "options": {"symlink": false},否则离线机上软链失效
- 要求包的 name 字段和 require 时写的名称完全一致(包括大小写)
- 执行 composer require my-utils/my-utils:@dev,注意必须加 @dev,否则 Composer 不认本地路径
为什么禁用 packagist.org 还是会连网?
即使配置了本地仓库,composer install 仍可能触发 DNS 查询,根本原因是未彻底关闭默认源的 fallback 行为:
- 必须运行 composer config --global repo.packagist false(全局关掉)
- 同时检查项目级配置:composer config repo.packagist 输出应为空,不能是 composer https://packagist.org
- 若项目 composer.json 中还保留着 "repositories" 里指向 https://packagist.org 的条目,删掉它
- composer install -v 可看到真实请求地址;如果仍有 Downloading https://repo.packagist.org/packages.json,说明某处配置没生效
离线环境下不能做的事
composer update 在离线时必然失败,它必须访问仓库元数据,COMPOSER_DISABLE_NETWORK=1 下直接退出。
composer create-project 也不能用,它内部强制加载远程仓库索引。
真正能离线执行的只有:composer install(有完整 composer.lock)、composer dump-autoload --classmap-authoritative、composer archive(需提前装好)。
最常被忽略的一点:composer.lock 里某个包若标记为 "source" 类型(比如用了 dev-main 或 Git URL),artifact 仓库就无法匹配——这种包必须先改成稳定版本约束,再重新生成 lock 文件。











