自定义 Artisan 命令必须满足路径(app/Console/Commands/)、命名空间(App\Console\Commands)、类名以 Command 结尾、执行 config:clear 和 composer dump-autoload 四重条件,缺一不可。

自定义 Artisan 命令不是“写个类就能用”,而是必须满足路径、继承、签名、缓存四重条件,缺一不可;否则 php artisan list 里永远看不到它。
命令没出现在 php artisan list 里?先查这三处硬性条件
自动发现(Laravel 8+ 默认启用)只认三个东西:文件必须在 app/Console/Commands/ 目录下、类名必须以 Command 结尾(如 SendReportCommand)、命名空间必须是 App\Console\Commands。漏掉任一,命令就“隐身”。
- 手动生成文件时容易把类名写成
SendReport而非SendReportCommand,导致类不被识别 - 改过命名空间(比如改成
App\Commands)后,必须手动在app/Console/Kernel.php的$commands数组里加全限定类名,不能依赖自动发现 - 改完代码后不执行
php artisan config:clear,缓存里的旧注册表不会更新,新命令始终不生效 - 部署到新环境或启用了
composer install --optimize-autoloader后,还得补跑composer dump-autoload,否则 PSR-4 自动加载找不到新类
$signature 写错一个符号,$this->argument() 就拿不到值
$signature 是 Laravel 解析命令行的 DSL,不是普通字符串。它控制参数怎么传、选项怎么写、默认值怎么设——写错格式,php artisan cmd --help 可能直接抛 Symfony\Component\Console\Exception\LogicException。
- 必填参数写成
{user_id},调用时必须带值:php artisan report:daily 123 - 可选参数必须写
{user_id?}(问号紧贴花括号),写成{user_id ?}或{user_id}?都会解析失败 - 带默认值的可选参数只能用字符串字面量:
{user_id?=123}合法,{user_id?=$default}非法 - 布尔选项写
{--force},用户传--force时$this->option('force')返回true;但若写成{--force=true},传--force反而返回字符串"true" - 接收值的选项必须等号紧贴名:
{--path=}合法,{--path =}会报错
handle() 里别用 echo 或 dd(),交互和输出有专用方法
Artisan 运行在 CLI 环境,echo、var_dump 会绕过颜色、换行、进度条等控制逻辑,dd() 更会中断整个命令流程,导致无法捕获错误或清理资源。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 要提问,用
$this->ask('请输入邮箱')、$this->confirm('确定删除?')、$this->choice('选哪个', ['A','B']) - 要输出,用
$this->info('✅ 成功')、$this->warn('⚠️ 注意')、$this->error('❌ 失败')——它们自动加颜色和换行 - 要调试变量,用
$this->line(var_export($data, true)),别用dd() - 要输出表格或进度条,用
$this->table($headers, $rows)或$bar = $this->output->createProgressBar(100),别自己拼字符串
调外部命令(如 mysqldump)必须用 proc_open() 捕获 stderr
直接 shell_exec("mysqldump -u{$user} -p{$pass}") 是高危操作:密码暴露在进程列表、错误信息被吞掉、失败时只返回 null,根本分不清是连不上数据库还是磁盘满了。
- 从
config/database.php读配置,别硬拼.env值;敏感字段(如密码)不要出现在命令字符串中 - 用
proc_open()显式捕获stderr,例如:$descriptors = [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']] - 导出路径强制限定在
storage_path('app/backups'),并提前用Filesystem确保目录存在且权限正确 - 执行前校验扩展是否启用:
if (!extension_loaded('mysqli')) { $this->error('mysqldump 扩展未启用'); return 1; }
最易被忽略的是缓存与 autoload 的联动:改完命令类,不 config:clear 就运行,等于没改;换了环境或优化了 autoloader,不 dump-autoload,命令就永远加载不出来。










