Composer 自定义 Installer 是用于处理非标准包类型(如 wordpress-plugin)安装路径的机制,解决其默认被错误放入 vendor/ 的问题;它通过继承 LibraryInstaller 并实现 supports()、getInstallPath()、isInstalled() 三个方法,接管安装逻辑,再以 composer-plugin 类型注册到主项目中生效。

什么是 Composer 自定义 Installer,它解决什么问题
Composer 默认只认 library、project、metapackage 等几类 type,遇到像 wordpress-plugin、typo3-cms-extension 或私有框架的 module 这类非标 type,就会直接丢进 vendor/ 下,不按预期路径安装——这是自定义 Installer 的核心出发点。
它不是“扩展 Composer 功能”,而是告诉 Composer:“当遇到 type 为 my-framework-bundle 的包时,请把它解压到 app/bundles/ 而不是 vendor/”。本质是接管安装路径逻辑。
编写 Installer 类必须实现的三个方法
自定义 Installer 必须继承 Composer\Installer\LibraryInstaller(或其子类),并至少覆盖以下三个方法:
-
supports():返回true仅当$package->getType()匹配你支持的 type,例如return $package->getType() === 'laravel-package'; -
getInstallPath():返回目标路径,相对项目根目录,**不能以/开头**。例如return 'packages/'.$package->getPrettyName(); -
isInstalled():判断是否已安装(用于composer update检测)。通常检查目标路径是否存在且含composer.json即可
漏掉 isInstalled() 会导致 composer update 反复重装;getInstallPath() 返回绝对路径会触发 InvalidArgumentException。
如何注册 Installer 到 composer.json 并确保生效
Installer 插件本身是一个独立的 Composer 包(type = composer-plugin),需在它的 composer.json 中声明:
{
"name": "acme/my-installer",
"type": "composer-plugin",
"require": {
"composer-plugin-api": "^2.0"
},
"autoload": {
"psr-4": { "Acme\\Installer\\": "src/" }
},
"extra": {
"class": "Acme\\Installer\\MyInstaller"
}
}
关键点:
-
extra.class必须指向完整命名空间类名,且该类必须实现Composer\Plugin\PluginInterface - 插件包必须被 require 进主项目(
composer require acme/my-installer --dev),否则不会加载 - Composer v2 要求插件 PHP 类必须同时实现
activate()方法,并在其中注册你的 Installer 实例到$installerManager
常见失败:插件已安装但 Installer 不生效——大概率是 activate() 里没调用 $installerManager->addInstaller()。
路径安全与跨平台兼容性容易被忽略
自定义路径拼接时,别直接用 . 或 / 拼字符串。Windows 下 getInstallPath() 返回带反斜杠会出错。
- 始终用
Composer\Util\Filesystem::normalizePath()处理路径 - 避免硬编码
../或../../,改用$composer->getConfig()->get('vendor-dir')等接口获取配置值 - 若需写入非 vendor 目录(如
public/),务必检查该目录是否在composer.json的config.notify或其他约束之外,否则可能被composer install --no-plugins跳过
最隐蔽的坑:某些共享主机禁用 symlink,而你的 Installer 若依赖符号链接(比如想 link 到 public),得提前检测并 fallback 到 copy。










