Skip to content
全部文档

Artisan 控制台

简介

Artisan 是 Laravel 自带的命令行界面。Artisan 位于应用根目录,以 artisan 脚本的形式存在,并提供许多有用的命令,可在构建应用时为你提供帮助。要查看所有可用 Artisan 命令的列表,可使用 list 命令:

shell
php artisan list

每个命令还包含一个「帮助」界面,用于显示并描述该命令可用的参数与选项。要查看帮助界面,在命令名称前加上 help

shell
php artisan help migrate

Laravel Sail

若使用 Laravel Sail 作为本地开发环境,请记住使用 sail 命令行来调用 Artisan 命令。Sail 会在应用的 Docker 容器中执行 Artisan 命令:

shell
./vendor/bin/sail artisan list

Tinker(REPL)

Laravel Tinker 是 Laravel 框架强大的 REPL,由 PsySH 包提供支持。

安装

所有 Laravel 应用默认都包含 Tinker。不过,若你之前从应用中移除了它,可通过 Composer 安装 Tinker:

shell
composer require laravel/tinker

INFO

在与 Laravel 应用交互时,需要热重载、多行代码编辑和自动补全?请查看 Tinkerwell

用法

Tinker 允许你在命令行上与整个 Laravel 应用交互,包括 Eloquent 模型、任务、事件等。要进入 Tinker 环境,运行 tinker Artisan 命令:

shell
php artisan tinker

可使用 vendor:publish 命令发布 Tinker 的配置文件:

shell
php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider"

WARNING

dispatch 辅助函数以及 Dispatchable 类上的 dispatch 方法依赖垃圾回收来将任务放入队列。因此,在使用 Tinker 时,应使用 Bus::dispatchQueue::push 来派发任务。

命令允许列表

Tinker 使用「允许」列表来确定哪些 Artisan 命令允许在其 shell 中运行。默认情况下,你可以运行 clear-compileddownenvinspiremigratemigrate:installupoptimize 命令。若希望允许更多命令,可将它们添加到 tinker.php 配置文件的 commands 数组中:

php
'commands' => [
    // App\Console\Commands\ExampleCommand::class,
],

不应别名的类

通常,当你在 Tinker 中与类交互时,Tinker 会自动为它们创建别名。不过,你可能希望永远不为某些类别名。可通过在 tinker.php 配置文件的 dont_alias 数组中列出这些类来实现:

php
'dont_alias' => [
    App\Models\User::class,
],

编写命令

除了 Artisan 提供的命令外,你还可构建自己的自定义命令。命令通常存储在 app/Console/Commands 目录中;不过,只要指示 Laravel 扫描其他目录以查找 Artisan 命令,你可自由选择自己的存储位置。

生成命令

要创建新命令,可使用 make:command Artisan 命令。该命令会在 app/Console/Commands 目录中创建新的命令类。不必担心应用中是否存在该目录——首次运行 make:command Artisan 命令时会创建它:

shell
php artisan make:command SendEmails

命令结构

生成命令后,应为类的 signaturedescription 属性定义合适的值。这些属性会在 list 屏幕上显示命令时使用。signature 属性还允许你定义命令的输入期望。执行命令时会调用 handle 方法。可将命令逻辑放在此方法中。

我们来看一个示例命令。注意,我们可通过命令的 handle 方法请求所需的任何依赖。Laravel 服务容器会自动注入在该方法签名中类型提示的所有依赖:

php
<?php

namespace App\Console\Commands;

use App\Models\User;
use App\Support\DripEmailer;
use Illuminate\Console\Command;

class SendEmails extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'mail:send {user}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send a marketing email to a user';

    /**
     * Execute the console command.
     */
    public function handle(DripEmailer $drip): void
    {
        $drip->send(User::find($this->argument('user')));
    }
}

INFO

为提高代码复用性,较好的做法是保持控制台命令轻量,并让它们将任务委托给应用服务来完成。在上例中,注意我们注入了一个服务类来完成发送电子邮件的「繁重工作」。

退出码

handle 方法未返回任何内容且命令成功执行,命令将以表示成功的 0 退出码退出。不过,handle 方法可选择返回一个整数以手动指定命令的退出码:

php
$this->error('Something went wrong.');

return 1;

若希望从命令内的任何方法「使命令失败」,可使用 fail 方法。fail 方法会立即终止命令执行并返回退出码 1

php
$this->fail('Something went wrong.');

闭包命令

基于闭包的命令提供了将控制台命令定义为类的替代方案。正如路由闭包是控制器的替代方案一样,可将命令闭包视为命令类的替代方案。

尽管 routes/console.php 文件不定义 HTTP 路由,但它定义了进入应用的基于控制台的入口点(路由)。在此文件中,可使用 Artisan::command 方法定义所有基于闭包的控制台命令。command 方法接受两个参数:命令签名以及接收命令参数与选项的闭包:

php
Artisan::command('mail:send {user}', function (string $user) {
    $this->info("Sending email to: {$user}!");
});

闭包绑定到底层命令实例,因此你可完全访问通常在完整命令类上能够访问的所有辅助方法。

类型提示依赖

除了接收命令的参数与选项外,命令闭包还可类型提示希望从服务容器解析的额外依赖:

php
use App\Models\User;
use App\Support\DripEmailer;
use Illuminate\Support\Facades\Artisan;

Artisan::command('mail:send {user}', function (DripEmailer $drip, string $user) {
    $drip->send(User::find($user));
});

闭包命令描述

定义基于闭包的命令时,可使用 purpose 方法为命令添加描述。运行 php artisan listphp artisan help 命令时会显示此描述:

php
Artisan::command('mail:send {user}', function (string $user) {
    // ...
})->purpose('Send a marketing email to a user');

可隔离命令

WARNING

要使用此功能,应用必须将 memcachedredisdynamodbdatabasefilearray 缓存驱动用作默认缓存驱动。此外,所有服务器必须与同一中央缓存服务器通信。

有时你可能希望确保同一时间只能运行一个命令实例。为此,可在命令类上实现 Illuminate\Contracts\Console\Isolatable 接口:

php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Isolatable;

class SendEmails extends Command implements Isolatable
{
    // ...
}

将命令标记为 Isolatable 时,Laravel 会自动为该命令提供 --isolated 选项,而无需在命令选项中显式定义。使用该选项调用命令时,Laravel 会确保没有该命令的其他实例正在运行。Laravel 通过尝试使用应用的默认缓存驱动获取原子锁来实现这一点。若该命令的其他实例正在运行,命令将不会执行;不过,命令仍会以成功的退出状态码退出:

shell
php artisan mail:send 1 --isolated

若希望指定命令在无法执行时应返回的退出状态码,可通过 isolated 选项提供所需的状态码:

shell
php artisan mail:send 1 --isolated=12

锁 ID

默认情况下,Laravel 会使用命令名称生成用于在应用缓存中获取原子锁的字符串键。不过,你可通过在 Artisan 命令类上定义 isolatableId 方法来自定义此键,从而将命令的参数或选项集成到键中:

php
/**
 * Get the isolatable ID for the command.
 */
public function isolatableId(): string
{
    return $this->argument('user');
}

锁过期时间

默认情况下,隔离锁在命令完成后过期。或者,若命令被中断且无法完成,锁将在一小时后过期。不过,你可通过在命令上定义 isolationLockExpiresAt 方法来调整锁的过期时间:

php
use DateTimeInterface;
use DateInterval;

/**
 * Determine when an isolation lock expires for the command.
 */
public function isolationLockExpiresAt(): DateTimeInterface|DateInterval
{
    return now()->plus(minutes: 5);
}

定义输入期望

编写控制台命令时,通常通过参数或选项从用户收集输入。Laravel 使用命令上的 signature 属性,使定义期望从用户获得的输入变得非常方便。signature 属性允许你用单一、富有表现力、类似路由的语法定义命令的名称、参数与选项。

参数

所有用户提供的参数与选项都包在大括号中。在下例中,命令定义了一个必填参数:user

php
/**
 * The name and signature of the console command.
 *
 * @var string
 */
protected $signature = 'mail:send {user}';

也可使参数可选,或为参数定义默认值:

php
// Optional argument...
'mail:send {user?}'

// Optional argument with default value...
'mail:send {user=foo}'

选项

选项与参数一样,是另一种用户输入形式。通过命令行提供时,选项以两个连字符(--)为前缀。选项有两种类型:接收值的与不接收值的。不接收值的选项充当布尔「开关」。我们来看这类选项的示例:

php
/**
 * The name and signature of the console command.
 *
 * @var string
 */
protected $signature = 'mail:send {user} {--queue}';

在本例中,调用 Artisan 命令时可指定 --queue 开关。若传入 --queue 开关,选项值为 true;否则为 false

shell
php artisan mail:send 1 --queue

带值的选项

接下来,我们来看期望接收值的选项。若用户必须为选项指定值,应在选项名后加 = 号:

php
/**
 * The name and signature of the console command.
 *
 * @var string
 */
protected $signature = 'mail:send {user} {--queue=}';

在本例中,用户可像这样为选项传值。若调用命令时未指定该选项,其值将为 null

shell
php artisan mail:send 1 --queue=default

可通过在选项名后指定默认值来为选项分配默认值。若用户未传入选项值,将使用默认值:

php
'mail:send {user} {--queue=default}'

选项快捷方式

定义选项时要分配快捷方式,可在选项名之前指定它,并使用 | 字符作为分隔符来分隔快捷方式与完整选项名:

php
'mail:send {user} {--Q|queue=}'

在终端调用命令时,选项快捷方式应以单个连字符为前缀,且在为选项指定值时不应包含 = 字符:

shell
php artisan mail:send 1 -Qdefault

输入数组

若希望定义期望多个输入值的参数或选项,可使用 * 字符。首先,我们来看指定此类参数的示例:

php
'mail:send {user*}'

运行此命令时,user 参数可按顺序传入命令行。例如,以下命令会将 user 的值设为包含 12 的数组:

shell
php artisan mail:send 1 2

* 字符可与可选参数定义结合,以允许零个或多个参数实例:

php
'mail:send {user?*}'

选项数组

定义期望多个输入值的选项时,传给命令的每个选项值都应加上选项名前缀:

php
'mail:send {--id=*}'

可通过传入多个 --id 参数来调用此类命令:

shell
php artisan mail:send --id=1 --id=2

输入描述

可通过用冒号将参数名与描述分开,为输入参数与选项分配描述。若定义命令时需要更多空间,可随意将定义拆成多行:

php
/**
 * The name and signature of the console command.
 *
 * @var string
 */
protected $signature = 'mail:send
                        {user : The ID of the user}
                        {--queue : Whether the job should be queued}';

提示缺失的输入

若命令包含必填参数,用户未提供时会收到错误消息。或者,可通过实现 PromptsForMissingInput 接口,将命令配置为在缺少必填参数时自动提示用户:

php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;

class SendEmails extends Command implements PromptsForMissingInput
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'mail:send {user}';

    // ...
}

若 Laravel 需要从用户收集必填参数,它会使用参数名或描述智能措辞问题,自动向用户询问该参数。若希望自定义用于收集必填参数的问题,可实现 promptForMissingArgumentsUsing 方法,返回以参数名为键的问题数组:

php
/**
 * Prompt for missing input arguments using the returned questions.
 *
 * @return array<string, string>
 */
protected function promptForMissingArgumentsUsing(): array
{
    return [
        'user' => 'Which user ID should receive the mail?',
    ];
}

也可通过使用包含问题与占位符的元组来提供占位符文本:

php
return [
    'user' => ['Which user ID should receive the mail?', 'E.g. 123'],
];

若希望完全控制提示,可提供一个应提示用户并返回其答案的闭包:

php
use App\Models\User;
use function Laravel\Prompts\search;

// ...

return [
    'user' => fn () => search(
        label: 'Search for a user:',
        placeholder: 'E.g. Taylor Otwell',
        options: fn ($value) => strlen($value) > 0
            ? User::whereLike('name', "%{$value}%")->pluck('name', 'id')->all()
            : []
    ),
];

INFO

全面的 Laravel Prompts 文档包含有关可用提示及其用法的更多信息。

若希望提示用户选择或输入选项,可在命令的 handle 方法中包含提示。不过,若仅希望在用户也因缺失参数而被自动提示时才提示用户,则可实现 afterPromptingForMissingArguments 方法:

php
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use function Laravel\Prompts\confirm;

// ...

/**
 * Perform actions after the user was prompted for missing arguments.
 */
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output): void
{
    $input->setOption('queue', confirm(
        label: 'Would you like to queue the mail?',
        default: $this->option('queue')
    ));
}

命令 I/O

检索输入

在命令执行期间,你很可能需要访问命令接受的参数与选项的值。为此,可使用 argumentoption 方法。若参数或选项不存在,将返回 null

php
/**
 * Execute the console command.
 */
public function handle(): void
{
    $userId = $this->argument('user');
}

若需要将所有参数检索为 array,调用 arguments 方法:

php
$arguments = $this->arguments();

使用 option 方法检索选项与检索参数一样容易。要将所有选项检索为数组,调用 options 方法:

php
// Retrieve a specific option...
$queueName = $this->option('queue');

// Retrieve all options as an array...
$options = $this->options();

提示输入

INFO

Laravel Prompts 是一个 PHP 包,用于为命令行应用添加美观且用户友好的表单,并具有类似浏览器的功能,包括占位符文本与验证。

除了显示输出外,你还可在命令执行期间要求用户提供输入。ask 方法会用给定问题提示用户,接受其输入,然后将用户的输入返回给你的命令:

php
/**
 * Execute the console command.
 */
public function handle(): void
{
    $name = $this->ask('What is your name?');

    // ...
}

ask 方法还接受可选的第二个参数,用于指定未提供用户输入时应返回的默认值:

php
$name = $this->ask('What is your name?', 'Taylor');

secret 方法与 ask 类似,但用户在控制台中输入时看不到其输入。当询问密码等敏感信息时,此方法很有用:

php
$password = $this->secret('What is the password?');

请求确认

若需要向用户询问简单的「是或否」确认,可使用 confirm 方法。默认情况下,此方法返回 false。不过,若用户输入 yyes 作为提示的响应,方法将返回 true

php
if ($this->confirm('Do you wish to continue?')) {
    // ...
}

如有必要,可通过将 true 作为第二个参数传给 confirm 方法,指定确认提示默认返回 true

php
if ($this->confirm('Do you wish to continue?', true)) {
    // ...
}

自动补全

anticipate 方法可用于为可能的选择提供自动补全。无论自动补全提示如何,用户仍可提供任意答案:

php
$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);

或者,可将闭包作为第二个参数传给 anticipate 方法。每次用户输入一个字符时都会调用该闭包。闭包应接受包含用户迄今输入的字符串参数,并返回用于自动补全的选项数组:

php
use App\Models\Address;

$name = $this->anticipate('What is your address?', function (string $input) {
    return Address::whereLike('name', "{$input}%")
        ->limit(5)
        ->pluck('name')
        ->all();
});

多选题

若在提问时需要给用户一组预定义的选择,可使用 choice 方法。可通过将索引作为方法的第三个参数传入,设置未选择任何选项时返回的默认值的数组索引:

php
$name = $this->choice(
    'What is your name?',
    ['Taylor', 'Dayle'],
    $defaultIndex
);

此外,choice 方法接受可选的第四、第五个参数,用于确定选择有效响应的最大尝试次数,以及是否允许多选:

php
$name = $this->choice(
    'What is your name?',
    ['Taylor', 'Dayle'],
    $defaultIndex,
    $maxAttempts = null,
    $allowMultipleSelections = false
);

写入输出

要向控制台发送输出,可使用 linenewLineinfocommentquestionwarnalerterror 方法。这些方法都会为其用途使用适当的 ANSI 颜色。例如,让我们向用户显示一些一般信息。通常,info 方法会在控制台中显示为绿色文本:

php
/**
 * Execute the console command.
 */
public function handle(): void
{
    // ...

    $this->info('The command was successful!');
}

要显示错误消息,使用 error 方法。错误消息文本通常显示为红色:

php
$this->error('Something went wrong!');

可使用 line 方法显示纯文本、无颜色的文本:

php
$this->line('Display this on the screen');

可使用 newLine 方法显示空行:

php
// Write a single blank line...
$this->newLine();

// Write three blank lines...
$this->newLine(3);

表格

table 方法可轻松正确格式化多行 / 多列数据。你只需提供列名与表格数据,Laravel 就会自动为你计算表格的适当宽度与高度:

php
use App\Models\User;

$this->table(
    ['Name', 'Email'],
    User::all(['name', 'email'])->toArray()
);

进度条

对于长时间运行的任务,显示进度条以告知用户任务完成程度会很有帮助。使用 withProgressBar 方法,Laravel 会显示进度条,并在对给定可迭代值的每次迭代中推进其进度:

php
use App\Models\User;

$users = $this->withProgressBar(User::all(), function (User $user) {
    $this->performTask($user);
});

有时,你可能需要对进度条的推进有更多手动控制。首先,定义进程将迭代的总步数。然后,在处理每个条目后推进进度条:

php
$users = App\Models\User::all();

$bar = $this->output->createProgressBar(count($users));

$bar->start();

foreach ($users as $user) {
    $this->performTask($user);

    $bar->advance();
}

$bar->finish();

INFO

有关更高级的选项,请查看 Symfony Progress Bar 组件文档

注册命令

默认情况下,Laravel 会自动注册 app/Console/Commands 目录中的所有命令。不过,可在应用的 bootstrap/app.php 文件中使用 withCommands 方法,指示 Laravel 扫描其他目录以查找 Artisan 命令:

php
->withCommands([
    __DIR__.'/../app/Domain/Orders/Commands',
])

如有必要,也可通过将命令的类名提供给 withCommands 方法来手动注册命令:

php
use App\Domain\Orders\Commands\SendEmails;

->withCommands([
    SendEmails::class,
])

当 Artisan 启动时,应用中的所有命令都会由服务容器解析并注册到 Artisan。

以编程方式执行命令

有时你可能希望在 CLI 之外执行 Artisan 命令。例如,你可能希望从路由或控制器执行 Artisan 命令。可使用 Artisan facade 上的 call 方法来完成。call 方法的第一个参数接受命令的签名名称或类名,第二个参数为命令参数数组。将返回退出码:

php
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Route;

Route::post('/user/{user}/mail', function (string $user) {
    $exitCode = Artisan::call('mail:send', [
        'user' => $user, '--queue' => 'default'
    ]);

    // ...
});

或者,可将整个 Artisan 命令作为字符串传给 call 方法:

php
Artisan::call('mail:send 1 --queue=default');

传递数组值

若命令定义了接受数组的选项,可将值数组传给该选项:

php
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Route;

Route::post('/mail', function () {
    $exitCode = Artisan::call('mail:send', [
        '--id' => [5, 13]
    ]);
});

传递布尔值

若需要指定不接受字符串值的选项的值,例如 migrate:refresh 命令上的 --force 标志,应将 truefalse 作为选项的值传入:

php
$exitCode = Artisan::call('migrate:refresh', [
    '--force' => true,
]);

将 Artisan 命令加入队列

使用 Artisan facade 上的 queue 方法,甚至可将 Artisan 命令加入队列,以便由队列工作者在后台处理。使用此方法之前,请确保已配置队列并正在运行队列监听器:

php
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Route;

Route::post('/user/{user}/mail', function (string $user) {
    Artisan::queue('mail:send', [
        'user' => $user, '--queue' => 'default'
    ]);

    // ...
});

使用 onConnectiononQueue 方法,可指定应向其派发 Artisan 命令的连接或队列:

php
Artisan::queue('mail:send', [
    'user' => 1, '--queue' => 'default'
])->onConnection('redis')->onQueue('commands');

从其他命令调用命令

有时你可能希望从现有 Artisan 命令调用其他命令。可使用 call 方法。此 call 方法接受命令名称以及命令参数 / 选项数组:

php
/**
 * Execute the console command.
 */
public function handle(): void
{
    $this->call('mail:send', [
        'user' => 1, '--queue' => 'default'
    ]);

    // ...
}

若希望调用另一个控制台命令并抑制其所有输出,可使用 callSilently 方法。callSilently 方法与 call 方法具有相同的签名:

php
$this->callSilently('mail:send', [
    'user' => 1, '--queue' => 'default'
]);

信号处理

如你所知,操作系统允许向正在运行的进程发送信号。例如,SIGTERM 信号是操作系统请求程序优雅终止的方式。若希望在 Artisan 控制台命令中监听信号并在信号发生时执行代码,可使用 trap 方法:

php
/**
 * Execute the console command.
 */
public function handle(): void
{
    $this->trap(SIGTERM, fn () => $this->shouldKeepRunning = false);

    while ($this->shouldKeepRunning) {
        // ...
    }
}

要同时监听多个信号,可将信号数组传给 trap 方法:

php
$this->trap([SIGTERM, SIGQUIT], function (int $signal) {
    $this->shouldKeepRunning = false;

    dump($signal); // SIGTERM / SIGQUIT
});

Stub 自定义

Artisan 控制台的 make 命令用于创建各种类,例如控制器、任务、迁移与测试。这些类使用根据你的输入填充值的「stub」文件生成。不过,你可能希望对 Artisan 生成的文件做小改动。为此,可使用 stub:publish 命令将最常见的 stub 发布到应用中以便自定义:

shell
php artisan stub:publish

已发布的 stub 将位于应用根目录的 stubs 目录中。你对这些 stub 所做的任何更改,都会在使用 Artisan 的 make 命令生成对应类时反映出来。

事件

运行命令时,Artisan 会派发三个事件:Illuminate\Console\Events\ArtisanStartingIlluminate\Console\Events\CommandStartingIlluminate\Console\Events\CommandFinished。Artisan 开始运行时立即派发 ArtisanStarting 事件。接下来,在命令运行前立即派发 CommandStarting 事件。最后,命令执行完毕后派发 CommandFinished 事件。