插件类必须实现 PluginInterface,否则不会被加载;activate() 和 deactivate() 方法签名须严格匹配,且插件 type 必须为 "composer-plugin";getInstallPath() 仅声明相对路径,不执行复制操作。

插件类必须实现 PluginInterface,否则根本不会被加载
Composer 不会扫描或猜测哪个类是插件——它只认 Composer\Plugin\PluginInterface 这个契约。漏掉这个接口,哪怕类名、命名空间、autoload 全对,composer install 也完全无视你。
常见错误现象:composer require my/plugin-installer --dev 成功,但 type=theme 的包依然进 vendor/;用 composer diagnose 查看已启用插件列表,你的插件压根不在里面。
-
activate()和deactivate()方法必须存在,且签名严格匹配:activate(Composer\Composer $composer, Composer\IO\IOInterface $io) - 插件包的
composer.json中"type"必须是"composer-plugin",不是"library"或其他 - PHP 类不能是匿名类,必须可被 PSR-4 自动加载(例如
"autoload": {"psr-4": {"My\Installer\": "src/"}}) -
activate()里不调用$composer->getInstallationManager()->addInstaller($myInstaller),安装器就永远不会介入
getInstallPath() 返回路径必须是相对路径,且不能含 ..
这个方法决定“文件最终放哪”,但它的返回值不是终点——它只是告诉 Composer:“请把包内容复制到这个位置”。如果返回 /var/www/public/themes 或 ../public/themes,Composer 会直接抛出 InvalidArgumentException。
使用场景:你想让 acme/my-theme 安装到项目根目录下的 public/themes/acme-my-theme。
- 正确写法:
return 'public/themes/' . $package->getPrettyName(); - 错误写法:
return '/public/themes/...'(以/开头)、return '../public/themes'(跳出项目范围) - 路径变量如
{$name}在自定义 Installer 中不生效——那是composer/installers的语法,你的类要自己拼 - 别在
getInstallPath()里做mkdir或copy,那属于install()阶段;这里只管“路径声明”
supports() 是字符串严格相等,不是模糊匹配
Composer 判断是否启用你的 Installer,只看 $package->getType() === 'your-custom-type'。它不会做 str_starts_with(),也不会 fallback 到父类型。写错一个字符,就等于没写。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
常见错误现象:插件类已加载、activate() 执行成功,但目标包始终走默认 vendor 安装逻辑——十有八九是 type 对不上。
- 被安装的包(如
acme/my-theme)的composer.json中必须明确写"type": "theme" - 你的 Installer 中
supports()必须返回$packageType === 'theme',不能是strpos($packageType, 'theme') !== false - 插件自身必须在根项目中
require(比如composer require acme/theme-installer --dev),不能只放在被安装的 theme 包里 - 修改插件后没清缓存?运行
composer clear-cache && rm -rf vendor composer.lock && composer install再试
为什么文件没出现?大概率卡在生命周期和缓存上
Installer 逻辑写对了,getInstallPath() 返回也合法,但目标目录空空如也——这不是代码 bug,而是 Composer 缓存和安装阶段的典型陷阱。
关键点:getInstallPath() 只影响路径决策;实际复制动作由 Composer 内部的 Installer::installCode() 完成,它默认只复制包根目录下内容(即 source 目录结构),不会自动带 tests/ 或 resources/。
- 缓存不清理 = 路径信息旧,改了插件也白搭;务必
composer clear-cache后重装 -
composer install不报错 ≠ 插件生效;检查composer show --plugins确认插件在列表里 - 若需处理 Git source(非 dist zip),得重写
install()方法,手动调用$filesystem->relativeCopy()或类似操作 - 调试时加
-v参数看详细日志,重点找Installing acme/my-theme (dev-main):后面有没有My\ThemeInstaller字样
最常被忽略的是:插件类实现了 InstallerInterface,却忘了在 activate() 里把它注册进 InstallationManager;还有人把 getInstallPath() 当成拷贝入口,在里面写 copy(),结果 Windows 权限失败、符号链接丢失、路径分隔符出错……这些都该交给 Composer 自己干,你只管说“放哪儿”。

















