Composer自定义命令必须通过type:composer-plugin插件实现,核心是实现CommandProviderInterface::getCommands()返回BaseCommand实例,并在composer.json中声明类型、API版本、autoload及extra.command-provider全限定类名。

Composer 自定义命令不是“写个 PHP 文件就能用”,必须走插件机制——否则 composer list 永远看不到你的命令。
为什么 composer my:command 报错 “Command not defined”
最常见原因不是命令类写错了,而是插件根本没被 Composer 加载。排查顺序如下:
- 运行
composer show --plugins—— 如果你的包不在列表里,说明插件注册失败 - 检查
composer.json是否漏了"type": "composer-plugin";缺这个字段,Composer 直接跳过整个包 - 确认
extra.command-provider(或旧版extra.class)指向的类名拼写、命名空间、PSR-4 自动加载路径三者完全一致 - 执行
composer dumpautoload,避免 autoloader 缓存导致类找不到 - 不要在插件类的
__construct()里抛异常或访问未注入的$io,这会让插件静默失效
CommandProviderInterface::getCommands() 必须返回 BaseCommand 实例
你写的命令类不能直接继承 SymfonyComponentConsoleCommandCommand,必须继承 ComposerCommandBaseCommand。否则会丢失 $this->getComposer()、$this->getIO() 等关键能力,且无法正确接入 Composer 生命周期。
-
configure()中必须调用$this->setName('my:command'),名字只能含小写字母、冒号、短横线,不能有空格或大写 -
execute()的两个参数顺序固定:InputInterface $input和OutputInterface $output,写反会导致运行时错误 - 输出必须用
$output->writeln(),别用echo,否则颜色、格式、静音模式(--quiet)全失效 - 在全局插件中调用
$this->getComposer()可能返回null(比如当前无项目),需先判空
composer.json 插件配置的关键字段不能少
一个可工作的插件包,composer.json 至少要包含以下内容:
立即学习“PHP免费学习笔记(深入)”;
-
"type": "composer-plugin"—— 这是开关,缺了就不是插件 -
"require": { "composer-plugin-api": "^2.0" }—— 对应 Composer 2.x;用 1.x 就写"^1.0" -
"autoload": { "psr-4": { "MyPlugin\": "src/" } }—— 类路径必须可自动加载 -
"extra": { "command-provider": "MyPlugin\MyCommandProvider" }—— 全限定类名,拼错或路径不对,Composer 不报错但静默跳过
注意:extra.command-provider 是 Composer 2.2+ 推荐写法;老版本用 extra.class,但行为一致。
插件装好了,但命令仍不出现?重点查这三个点
即使所有代码都对,仍有三个隐蔽坑常被忽略:
-
getCommands()方法返回空数组,或返回了非BaseCommand实例(比如忘了new MyCommand()) - 插件类没实现
ComposerPluginCommandProviderInterface,只写了方法但没implements接口,Composer 完全无视 - 插件包被安装在项目本地(
require-dev),但你想在全局用;或者反过来——全局安装了,却想在某个项目里覆盖命令,这时得看 Composer 的插件激活策略(默认只激活已安装且满足 type + interface 的包)
真正难调试的从来不是命令逻辑本身,而是插件加载链路是否完整:从 composer.json 声明 → autoloader 可见 → extra.command-provider 解析 → 接口实现 → getCommands() 返回有效实例。漏掉任意一环,命令就消失。



















