composer自定义安装器必须用composer/installers,因其提供可扩展的类型映射机制,而composer仅内置library、metapackage、plugin等类型;直接写任意installer-name无效。

Composer 自定义安装器为什么必须用 composer/installers 而不是直接写 installer-name
因为 Composer 本身不识别任意字符串的 installer-name;它只内置了 library、metapackage、plugin 这几种类型。想支持 wordpress-plugin 或 typo3-cms-extension 这类自定义类型,必须依赖 composer/installers 这个第三方 installer —— 它在 composer.json 的 extra.installer-paths 之外,提供了可扩展的类型映射机制。
常见错误是直接在包的 type 字段写 my-framework-bundle,却不声明 require: {"composer/installers": "^2.0"},结果安装时仍落到 vendor/ 下,而非预期的 src/Bundle/ 目录。
- 所有自定义 type 必须先被
composer/installers显式注册(通过其installers配置)或继承其基类 -
composer/installersv2+ 不再自动加载任意 type,需显式启用:例如在 rootcomposer.json中加"extra": {"installer-types": ["my-framework-bundle"]} - 若使用私有 installer 类,必须实现
ComposerInstallerInstallerInterface,且在autoload中能被 Composer 找到
如何让自己的包被正确识别为 my-framework-module 并安装到 modules/
分两步:一是在你的包(即要被安装的模块)中声明 type,二是在使用它的项目中配置 installer 行为。
你的模块 composer.json:
{
"name": "acme/my-module",
"type": "my-framework-module",
"autoload": { "psr-4": { "Acme\MyModule\": "src/" } }
}
主项目的 composer.json:
{
"require": {
"acme/my-module": "*",
"composer/installers": "^2.2"
},
"extra": {
"installer-types": ["my-framework-module"],
"installer-paths": {
"modules/{$name}/": ["type:my-framework-module"]
}
}
}
-
installer-types是告诉composer/installers:“这个 type 我要你管”,否则它会跳过该包 -
installer-paths中的type:xxx是匹配语法,不是变量插值;{$name}才是实际替换的占位符 - 路径值必须以
/结尾,否则 Composer 可能报Installation failed, reverting...错误
PackageInstallers 和 LibraryInstaller 的根本区别在哪
这是 composer/installers 内部的两个核心类:LibraryInstaller 处理标准 library 类型(默认进 vendor/),而 PackageInstallers 是一个“分发器”——它根据 package.type 查表,把安装请求路由给对应的具体 installer 实例(比如 WordPressPluginInstaller)。
如果你写自定义 installer 类,通常应继承 BaseInstaller(来自 composer/installers),而不是从头实现接口:
use ComposerInstallerLibraryInstaller;
use ComposerPackagePackageInterface;
class MyFrameworkModuleInstaller extends ComposerInstallersBaseInstaller
{
protected $locations = array('my-framework-module' => 'modules/{$name}/');
}
- 继承
BaseInstaller可复用路径变量解析({$name}、{$vendor}、{$version})和目录创建逻辑 - 不要重写
supports()方法来返回true给任意 type;应严格匹配$package->getType() === 'my-framework-module' - 自定义 installer 类必须在主项目的
autoload.files或autoload.psr-4中声明,否则 Composer 启动时无法加载
为什么 composer update 有时忽略 installer-paths 更新已安装包的位置
Composer 不会自动移动已安装的包 —— 它只在首次安装或版本变更触发重新安装时应用 installer-paths。如果改了路径规则,但包已存在 vendor/ 下,composer update 默认复用现有安装,不会挪动。
- 解决方法:先
composer remove acme/my-module,再composer require acme/my-module - 更稳妥的做法是加
--prefer-dist强制重装(但不保证清旧路径) - 真正可靠的清理方式是删掉
vendor/和modules/,再composer install—— 尤其当你同时调整了installer-types和installer-paths - 注意:某些 IDE 缓存或 opcache 可能导致 autoload.php 仍引用旧路径,修改后建议清空
vendor/composer/autoload_*.php
最常被忽略的是:自定义 installer 生效的前提,是 Composer 能完整加载整个依赖图;一旦某个中间包的 composer.json 格式非法或含语法错误,整个 installer 链就静默失效,包照旧落进 vendor/ —— 这时候得用 composer diagnose 和 composer install -v 看真实日志。











