
preferred-install 是策略,不是开关
Composer 的包安装策略核心就落在 preferred-install 配置上,但它常被误读为“强制走 dist 或 source”。实际它只是**优先级提示**,最终行为由多层条件叠加决定。
常见错误是改完 preferred-install 后发现 vendor 里有的包有 .git、有的没有,以为配置失效。其实是因为:
–prefer-dist 或 –prefer-source 命令行参数优先级最高,会直接覆盖配置包自身 composer.json 若没提供 dist 字段(比如私有 GitLab 仓库未开启 archive),即使你设了 "*": "dist",也会静默 fallback 到 sourcerequire-dev 下的包在 –no-dev 场景下可能被跳过,或触发不同安装逻辑
推荐用对象形式写死映射:{"myorg/*": "source", "*": "dist"}。注意 * 是前缀匹配,monolog/monolog 不会匹配 "monolog/*",也不能写成 "*/monolog"。
platform 配置影响的是依赖解析阶段
config.platform 不是运行时检测,而是告诉 Composer:“我最终要部署到这个环境”,从而在 安装前 就过滤掉不兼容的包。它只在依赖解析阶段起作用,装完后不会做任何检查。
必须写对路径和结构,否则完全不生效:
错:写成 "platform": {"php": "8.1.0"} —— 这个字段名位置错了,必须嵌套在 config 下对:写成 "config": {"platform": {"php": "8.1.10", "ext-redis": "5.3.7"}}上游包没声明 "ext-pcntl": "*",你再配 platform 也拦不住 —— 它只筛显式声明了扩展依赖的包
PHP 版本模拟最可靠;扩展模拟效果取决于生态规范程度。2026 年主流包基本都已补全 ext-xxx 声明,但小众封装器仍可能漏掉。
vendor-dir 和 installer-paths 是两类不同粒度的路径控制
想改整个 vendor 目录?只能靠 vendor-dir。但它不是热更新配置:必须删掉现有 vendor 目录,再跑 composer install 才生效;composer update 不会移动已有文件。
想只动某个包的位置?得靠 composer/installers 插件 + extra.installer-paths。但有两个硬前提:
目标包的 type 必须是插件支持的类型(如 drupal-module、wordpress-plugin),普通 library 类型默认不触发插件自身必须通过 composer require composer/installers 装进当前项目,且其类被 autoload 正确加载
installer-paths 的路径值是相对于项目根目录的,支持 {$name} 占位符,但不能以 / 开头,也不能含 .. 向上跳出项目范围。
安装器插件的 supports() 匹配是严格字符串相等
自定义安装路径最易踩坑的地方,就是以为 supports() 支持模糊匹配或前缀识别。实际上它只做 === 判断:包的 type 字段必须和插件里写的字符串**逐字一致**。
比如插件写 return $packageType === ‘theme’;,那只有 "type": "theme" 的包才会被接管;"type": "my-theme" 或 "type": "theme-v2" 全部忽略。
另一个关键点:getInstallPath() 只决定「装到哪」,不负责「把什么放进去」。文件复制动作由 Composer 内部调用完成,默认只复制包根目录下的内容。如果包用了 source 方式安装(比如 Git clone),而你没重写 install() 方法,那 public/themes/my-theme 目录可能是空的 —— 因为源码实际在 vendor/my/theme 下,还没被手动拷过去。
插件修改后必须清缓存:composer clear-cache,删掉 vendor/ 和 composer.lock,再重装。否则 Composer 会沿用旧的路径缓存,导致路径变了但文件还在老地方。

评论(0)