要让 yii2 的 composer 插件支持自定义命令,必须实现 composerplugincapabilitycommandprovider 接口并注册 symfony console 命令类,而非 yii 控制器;需手动引导最小 yii 应用且注意工作目录与环境变量;installer 类需匹配包 type 并在 activate() 中注册;钩子应优先使用 post-autoload-dump 以确保类可加载;插件自身必须正确配置 psr-4 autoloading。

怎么让 Yii2 的 Composer 插件支持自定义命令
Yii2 本身不接管 composer 命令行,插件要加命令,得靠 Composer 自身的插件机制——不是写个 yii 命令,而是注册一个 ComposerPluginCapabilityCommandProvider 能力。
常见错误是直接在 console/controllers/ 下写控制器,然后指望 composer my-command 能调到它——完全走不通。Composer 运行时根本没加载 Yii 应用上下文。
- 必须实现
ComposerPluginCapabilityCommandProvider接口,并在composer.json的extra.installer-types或插件类的activate()中声明能力 - 命令类需继承
SymfonyComponentConsoleCommandCommand,不能依赖yiiconsoleController - 如果需要访问 Yii2 配置或 DB,得手动引导最小 Yii 应用(比如只加载
BaseYii和Application),但要注意 Composer 运行时工作目录可能不是项目根,YII_ENV也默认是prod
示例命令类开头通常长这样:
class MyCommand extends Command
{
protected function configure()
{
$this->setName('my-plugin:do-something')
->setDescription('Do something with Yii2-aware logic');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// 手动加载 Yii:require __DIR__.'/../../vendor/yiisoft/yii2/Yii.php';
// new yiiconsoleApplication([...]);
}
}
Installer 类为什么没被触发?路径和 type 对不上
Composer 的 Installer 是按包的 type 匹配的,不是按文件名或命名空间。Yii2 插件里写了个 MyInstaller extends BaseInstaller,但 composer install 就是不调它——八成是包的 type 没配对,或者没在根 composer.json 的 autoload-dev 或插件配置里声明。
- 你的插件包(即含
Installer类的包)自身composer.json必须有"type": "composer-plugin" - 被你插件处理的目标包(比如一个
yii2-extension),其composer.json的type必须和你在MyInstaller::supportsType()里返回的字符串一致 -
MyInstaller::getInstallPath()返回的路径必须是相对路径,且不能以/开头;若返回yii2/extensions/{$name},就得确保目标包type是yii2-extension,且已注册该 type 到ComposerInstallerLibraryInstaller链中 - 别漏掉在插件主类的
activate()里调用$composer->getInstallationManager()->addInstaller($installer)
为什么 post-install-cmd 里拿不到刚装好的扩展路径
因为 post-install-cmd 是 Composer 执行完所有安装逻辑后才触发的钩子,此时包文件已落盘,但 vendor/composer/installed.json 可能还没刷新,更关键的是:Yii2 的自动加载器(ComposerAutoloadClassLoader)此时未必重写了 vendor/autoload.php——导致你在钩子里 class_exists('SomeExtensionClass') 返回 false。
- 不要在
post-install-cmd中试图 require 或 new 任何刚安装的扩展类 - 如果真要生成配置、复制资源,改用
post-autoload-dump钩子,它在 autoload 文件写完后触发,此时类可被安全反射 - 路径计算别硬编码
vendor/myvendor/myext,用$event->getComposer()->getPackage()->getAutoload()['psr-4']或$composer->getInstallationManager()->getInstallPath($package)动态取
Yii2 插件里调 Yii::createObject() 报错 Class not found
这不是 Yii 的问题,是 Composer 插件运行时的 autoloader 隔离导致的。插件代码由 Composer 自己的 ClassLoader 加载,而它默认只管 vendor/composer/autoload_*.php,不会自动包含你插件里 src/ 下的类,除非你在插件包的 composer.json 中明确定义了 autoload。
- 插件包的
composer.json必须有"autoload": { "psr-4": { "MyPlugin\": "src/" } },且执行过composer dump-autoload(开发时建议加--dev) - 别在
src/外放类文件,比如commands/目录不被 PSR-4 覆盖就加载不到 - 如果用了
Yii::createObject(['class' => 'MyPluginSomeClass']),确保SomeClass的命名空间和文件路径严格匹配 PSR-4 规则,大小写一个字母都不能错(尤其在 Linux 上)
最常被忽略的一点:插件类本身(实现 PluginInterface 的那个类)如果不在 autoload 规则里,Composer 根本不会实例化它——整个插件就静默失效了。











