Composer自定义命令需通过type:composer-plugin包实现,主类实现CommandProviderInterface::getCommands()返回BaseCommand实例,并在composer.json中声明插件类型及兼容API版本。

Composer 本身不支持“插件式命令行扩展”,所谓“自定义命令插件”实际是通过 composer-plugin 类型包 + CommandProviderInterface 实现的——它不是写个脚本扔进 bin 就能用,而是要注册为 Composer 插件并被正确加载。
怎么让 Composer 认出你的自定义命令
关键不在命令本身,而在插件声明和接口实现。Composer 只会从已安装的 type: composer-plugin 包中查找实现了 Composer\Plugin\CommandProviderInterface 的类。
- 必须在
composer.json中声明"type": "composer-plugin" -
require里得有"composer-plugin-api": "^2.0"(对应 Composer 2.x)或"^1.0"(Composer 1.x) - 主类需实现
Composer\Plugin\CommandProviderInterface::getCommands(),返回一个Command实例数组 - 该类需在
autoload中可被自动加载(推荐"psr-4")
为什么你的命令没出现在 composer list 里
最常见原因是插件未被激活:Composer 不会自动加载本地开发中的插件,必须先 composer global require 或 composer require --dev 到目标项目,且插件包已发布(或通过 repositories 指向本地路径)。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 用
composer global show确认插件是否已全局安装 - 用
composer show --plugin查看当前项目启用的插件列表(Composer 2.2+ 支持) - 如果插件类抛出异常(比如构造函数失败),Composer 会静默跳过该插件,不会报错也不会注册命令
- 确保
getCommands()返回的是Composer\Command\BaseCommand子类实例,不是字符串或未实例化的类名
Command 类怎么写才真正可用
不能直接 extends Symfony\Component\Console\Command\Command——必须继承 Composer\Command\BaseCommand,否则无法获取 $this->getComposer() 和 $this->getIO()。
- 重写
configure()设置setName("myplugin:do-something"),名字会出现在composer list -
execute()中通过$this->getComposer()->getPackage()获取当前项目信息 - 输入参数建议用
$input->getOption("force")而非$_SERVER,保证与 Composer 原生命令行为一致 - 避免在
execute()里调用exec()或shell_exec()执行composer install——可能引发嵌套加载、锁文件冲突
protected function configure(): void
{
$this->setName('myplugin:dump-config')
->setDescription('Dump current composer config as JSON');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = $this->getIO();
$composer = $this->getComposer();
$config = $composer->getConfig()->all();
$io->write(json_encode($config, JSON_PRETTY_PRINT));
return 0;
}
调试时最容易忽略的加载时机问题
Composer 插件在 Composer\Autoload\ClassLoader 初始化前就尝试加载,所以不能依赖 vendor/autoload.php 中尚未注册的其他第三方库——哪怕你已在 require 中写了它们。
- 插件类自身应尽量轻量,避免在构造函数中做 heavy work
- 所有外部依赖(如 Guzzle、Symfony Console)必须声明在插件包的
require中,且版本与当前 Composer 兼容(例如 Symfony 6 组件可能不兼容 Composer 2.2 内置的旧 Console 版本) - 如果用
repositories指向本地插件路径,记得每次改代码后运行composer update myvendor/myplugin,否则缓存可能让你以为修改没生效

















