在 Yii 3.0 中开发自定义控制台命令需基于 PSR-11 容器和显式依赖注入,类须继承 yii-console/Command、声明 strict_types、通过构造函数注入依赖、在 config/console.php 的 commands 数组中注册 FQCN,并支持 prompt/confirm 或 --force 参数实现交互控制。

要在 Yii 3.0 中开发一个可执行的自定义控制台命令,必须绕过 Yii 2.x 的静态调用习惯,直接基于 PSR-11 容器和显式依赖注入构建,否则命令注册失败、依赖无法解析、运行时报 Class not found 或 Container not initialized 错误。
创建命令类文件
在 src/Command 目录下新建 PHP 文件,例如 SendNewsletterCommand.php;该路径不是约定俗成的,而是由你配置的命名空间决定——但必须与 composer.json 中的 "autoload": {"psr-4": {"App\": "src/"}} 对齐,否则类根本不会被自动加载。
类需继承 yiisoft/yii-console/Command,并实现 execute() 方法;不要写 run() 或 actionXxx(),这些是 Yii 2.x 遗留写法,在 Yii 3.0 中无效。
文件开头必须声明严格类型:declare(strict_types=1);,这是 Yii 3.0 所有类的硬性前提,漏写会导致 DI 容器拒绝解析构造函数参数。
注入依赖并编写业务逻辑
把所需服务作为构造函数参数声明,例如:public function __construct(private MailerInterface $mailer, private UserRepository $users);Yii 3.0 的容器会自动解析这些接口绑定的具体实现,前提是已在 config/common.php 中完成注册。
在 execute() 方法中写实际逻辑,比如遍历用户、拼接邮件内容、调用 $this->mailer->send();【不要在 execute() 内部 new 任何类】,所有对象都应通过构造函数注入,否则单元测试无法 mock,CI 环境也无法替换为假实现。
这一步操作起来很简单,直接把业务代码搬进来就行,但要注意:如果用到了数据库查询,确保 $users 是从容器注入的仓储实例,而不是手动 new Query() 或硬编码 PDO 连接。
注册命令到控制台应用
打开 config/console.php,找到 'commands' => [] 配置项(若不存在则手动添加),将命令类完整命名空间写入数组:
'send-newsletter' => AppCommandSendNewsletterCommand::class
键名 send-newsletter 就是终端里要输入的命令别名,支持短横线分隔,但不能含空格或下划线;值必须是类的完整 FQCN,且该类必须已通过 autoload 正确注册。
注意:这个配置数组不支持闭包或工厂函数,Yii 3.0 控制台只接受字符串类名或已实例化的 Command 对象——前者更常用,后者适合需要动态构造参数的场景。
添加交互式输入支持
方法一:使用内置 prompt() 和 confirm()
在 execute() 方法内直接调用:$email = $this->prompt('Enter recipient email:');;$this->confirm('Send now?') 会返回布尔值,回车默认为 false。
方法二:带默认值和校验的交互
第一步:$count = (int) $this->prompt('How many users to notify?', '10'); —— 用户直接回车就用 '10',但要强转 int,否则可能传入字符串导致 SQL 错误。
第二步:if (!$this->isInteractive) { $this->stderr("Not in interactive mode. "); return self::EXIT_CODE_ERROR; } —— 【必须加此判断】,否则在 CI 或管道中运行时,prompt() 会卡死或返回空,导致命令异常退出。
方法三:跳过交互,用参数替代
在 execute() 开头检查:if ($this->option('force')) { /* 跳过 confirm */ };然后在命令行运行时加 --force 参数即可绕过确认步骤,适合定时任务场景。


















