Composer Command是必须实现CommandProvider接口、通过composer-plugin机制注册的类实例,能访问$input、$output、$composer和$io;普通PHP脚本无法获取这些对象,也不能被composer list识别。

什么是Composer Command,它和普通PHP脚本有什么区别
Composer Command不是独立运行的PHP脚本,而是被composer进程加载并执行的类实例——它必须实现ComposerPluginCapabilityCommandProvider接口,并通过插件机制注册到Composer生命周期中。直接写个bin/my-command然后chmod +x是没用的,Composer根本不会识别它。
关键区别在于执行上下文:Command类能拿到$input(SymfonyComponentConsoleInputInputInterface)、$output(SymfonyComponentConsoleOutputOutputInterface),还能访问$composer对象(当前项目依赖图)和$io(交互式IO封装)。普通脚本做不到这点。
- 必须以Composer插件形式发布(
type: composer-plugin) - 入口类需声明
getCommands()方法返回命令实例数组 - 不能依赖全局
autoload,必须通过插件的activate()触发加载
如何编写一个最小可用的自定义Command类
假设你要写一个composer dump-env命令,把.env里所有APP_*变量输出为JSON格式。核心类结构如下:
namespace MyVendorComposerCommand;
<p>use ComposerPluginCapabilityCommandProvider as CommandProviderCapability;
use ComposerConsoleApplication;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
use SymfonyComponentConsoleInputInputOption;</p><p>class DumpEnvCommand extends Command implements CommandProviderCapability
{
protected function configure()
{
$this->setName('dump-env')
->setDescription('Dump APP_* env vars as JSON')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'Output format', 'json');
}</p><pre class="brush:php;toolbar:false;">protected function execute(InputInterface $input, OutputInterface $output)
{
$format = $input->getOption('format');
$env = [];
if (file_exists('.env')) {
foreach (file('.env') as $line) {
if (str_starts_with(trim($line), 'APP_')) {
[$key, $val] = array_pad(explode('=', trim($line), 2), 2, '');
$env[trim($key)] = trim($val);
}
}
}
if ($format === 'json') {
$output->writeln(json_encode($env, JSON_UNESCAPED_UNICODE));
}
return 0;
}
public function getCommands()
{
return [$this];
}}
注意:getCommands()必须返回$this或新实例,不能返回static::class字符串;configure()里不要调用parent::configure(),这个基类没有父实现。
composer.json怎么配置才能让命令生效
你的包必须声明为插件,并指定extra.command-provider指向实现类(不是命名空间,是完整类名):
{
"name": "myvendor/composer-dump-env",
"type": "composer-plugin",
"autoload": {
"psr-4": {
"MyVendor\ComposerCommand\": "src/"
}
},
"require": {
"composer-plugin-api": "^2.0"
},
"extra": {
"class": "MyVendor\ComposerCommand\DumpEnvCommand"
}
}常见错误:
- 漏掉
"type": "composer-plugin"→ Composer直接忽略该包 -
extra.class写成MyVendor\ComposerCommand(命名空间)→ 报错Class not found - PHP版本约束太松(如
"php": "^7.4 || ^8.0")→ 在Composer 2.5+环境下可能因反射限制失败,建议锁死"php": "^8.1" - 没加
composer-plugin-api依赖 → Composer 2.x会拒绝加载
本地开发调试时为什么composer命令不出现
不是改完代码就能立刻看到composer dump-env,必须完成三步:
- 在项目根目录执行
composer require myvendor/composer-dump-env:dev-main --dev(假设你用main分支) - 确保
myvendor/composer-dump-env已出现在vendor/composer/installed.json里且type为composer-plugin - 删掉
vendor/composer/autoload_psr4.php和vendor/composer/autoload_classmap.php,再跑一次composer dump-autoload(否则旧类映射残留)
如果仍不显示,运行composer list | grep dump确认是否加载成功;若报Plugin installation failed,检查composer diagnose输出,重点看Plugin API version是否匹配——Composer 2.5要求插件声明composer-plugin-api: ^2.5,旧版声明^2.0会被跳过。
最隐蔽的坑:类文件路径和命名空间不一致(比如src/DumpEnvCommand.php里写了class DumpEnv),这种错误不会报Fatal,但会导致getCommands()返回空数组,命令彻底消失。


















