Composer无系统级扩展扫描路径,仅支持两种可控方式:一是项目级repositories声明path仓库,二是COMPOSER_HOME配合config.json配置vendor-dir;其他方案均无效或埋雷。

Composer 没有“系统级扩展扫描路径”这个概念——它不主动扫描目录找扩展,也不支持全局配置一个路径让所有项目自动加载其中的包。所谓“自定义扩展路径”,本质只有两条可控通路:一是通过 repositories 声明本地 path 类型仓库(项目级),二是通过 COMPOSER_HOME + 全局 config.json 控制共享安装位置(伪系统级)。其他思路基本失效或埋雷。
为什么 config.vendor-dir 写在项目 composer.json 里不生效
因为 Composer 2.x 已彻底移除该配置项的支持。你写:{"config": {"vendor-dir": "libs"}} 不仅不会改路径,还会触发弃用警告:The "config.vendor-dir" option is deprecated, use the "COMPOSER_VENDOR_DIR" environment variable instead. 但注意:这个环境变量在 2.x 中也已失效,它现在只影响极少数调试行为,对 vendor 目录写入完全无感。真正能改 vendor 位置的,只剩一种方式:在 COMPOSER_HOME 下的 config.json 中配 vendor-dir。
想让多个项目共用一个 vendor 目录,必须设 COMPOSER_HOME + config.json
这是唯一接近“系统级”的做法,但需两步严格配合:
- 先设置环境变量:
export COMPOSER_HOME="/opt/my-composer-home(Linux/macOS)或set COMPOSER_HOME=C:\my-composer-home(Windows) - 然后在
/opt/my-composer-home/config.json(或对应 Windows 路径)中写入:{"config": {"vendor-dir": "/opt/shared-vendor"}} - 确保该
config.json是合法 JSON,且vendor-dir值为绝对路径(相对路径在这里不被识别) - 之后所有项目执行
composer install,都会把包装进/opt/shared-vendor,而非各自vendor/
⚠️ 注意:COMPOSER_HOME 本身只控制 Composer 自身配置、缓存和插件位置;只有它配合其下的 config.json 才能接管 vendor-dir。单独设 COMPOSER_HOME 不会移动 vendor。
path 类型仓库不能“系统级启用”,必须每个项目自己声明
你无法在全局配置里写一条 repositories 让所有项目都认本地包。每个项目必须在自己的 composer.json 根级 repositories 数组中显式声明,例如:
[{"type": "path", "url": "./packages/my-utils"}]
否则 composer require mycompany/utils 一定报 Could not find package。常见错误包括:
- 把
repositories错塞进require或extra字段下 -
url用了绝对路径(如/home/user/pkg)或file://前缀——Composer 会静默跳过 - 本地包目录里没有合法
composer.json,或缺name字段、version字段没写(如没写"dev-main"且项目没设"minimum-stability": "dev")
Windows 下尤其容易因符号链接权限或路径分隔符导致 symlink 失败,建议显式加 "options": {"symlink": true} 并以管理员身份运行命令行。
别碰 extra.installer-paths,它不是你要的“扩展扫描路径”
extra.installer-paths 看似能按包名路由安装位置,但它只对声明了特定 type(如 wordpress-plugin、drupal-module)且项目已安装对应 installer(如 composer/installers)的包生效。普通 library 类型包,哪怕写进规则里,也照旧进 vendor/。它不是通用路径重定向机制,而是 installer 类型分发器。试图用它实现“系统级扩展路径”只会白忙活。
真正需要“统一管理本地扩展”的团队,应聚焦两点:一是规范每个项目的 repositories 配置(可用模板或 CI 脚本注入),二是用 COMPOSER_HOME + config.json 统一 vendor 存放位置。前者管“源在哪”,后者管“包装哪”——两者缺一不可,且都不能靠“扫描”自动发现。











