Skip to content

Artisan 控制台

简介

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

shell
php artisan list

每个命令都包含一个 "help" 屏幕,显示命令的可用参数和选项。要查看帮助屏幕,请在命令名称前加上 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。但是,如果你之前从应用中删除了 Tinker,可以使用 Composer 安装它:

shell
composer require laravel/tinker

NOTE

在与 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 使用 "allow" 列表来确定哪些 Artisan 命令允许在其 shell 中运行。默认情况下,你可以运行 clear-compileddownenvinspiremigratemigrate:installupoptimize 命令。如果你想允许更多命令,可以将它们添加到 tinker.php 配置文件的 commands 数组中:

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

不应该别名的类

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

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

编写命令

除了 Artisan 提供的命令之外,你还可以构建自定义命令。命令通常存储在 app/Console/Commands 目录中;但是,只要你的命令可以被 Composer 加载,你可以选择任何存储位置。

生成命令

要创建新命令,可以使用 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')));
        }
    }

NOTE

为了实现更好的代码重用,最好保持命令轻量化,并让它们委托应用服务来完成任务。在上面的示例中,注意我们注入了一个服务类来执行发送电子邮件的 "繁重工作"。

退出码

如果 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;

    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()->addMinutes(5);
}

定义输入期望

编写控制台命令时,通常需要从用户那里获取输入,通过参数或选项。Laravel 使得定义你期望从用户那里获得的输入变得非常方便。你可以在命令的 signature 属性上使用 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::where('name', 'like', "%{$value}%")->pluck('name', 'id')->all()
                : []
        ),
    ];

NOTE

综合的 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');
    }

如果需要将所有参数作为数组获取,请调用 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();

提示输入

NOTE

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
    $name = $this->anticipate('What is your address?', function (string $input) {
        // Return auto-completion options...
    });

多选问题

如果你需要向用户提供一组预定义的选择,可以使用 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
    );

输出内容

要将输出发送到控制台,可以使用 lineinfocommentquestionwarnerror 方法。每个方法都会使用适合其目的的 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();

NOTE

有关更高级的选项,请查看 Symfony Progress Bar component documentation

注册命令

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

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;

    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;

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

传递布尔值

If you need to specify the value of an option that does not accept string values, such as the --force flag on the migrate:refresh command, you should pass true or false as the value of the option:

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

Queueing Artisan Commands

Using the queue method on the Artisan facade, you may even queue Artisan commands so they are processed in the background by your queue workers. Before using this method, make sure you have configured your queue and are running a queue listener:

php
    use Illuminate\Support\Facades\Artisan;

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

        // ...
    });

Using the onConnection and onQueue methods, you may specify the connection or queue the Artisan command should be dispatched to:

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

Calling Commands From Other Commands

Sometimes you may wish to call other commands from an existing Artisan command. You may do so using the call method. This call method accepts the command name and an array of command arguments / options:

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

        // ...
    }

If you would like to call another console command and suppress all of its output, you may use the callSilently method. The callSilently method has the same signature as the call method:

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

Signal Handling

As you may know, operating systems allow signals to be sent to running processes. For example, the SIGTERM signal is how operating systems ask a program to terminate. If you wish to listen for signals in your Artisan console commands and execute code when they occur, you may use the trap method:

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

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

To listen for multiple signals at once, you may provide an array of signals to the trap method:

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

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

Stub Customization

The Artisan console's make commands are used to create a variety of classes, such as controllers, jobs, migrations, and tests. These classes are generated using "stub" files that are populated with values based on your input. However, you may want to make small changes to files generated by Artisan. To accomplish this, you may use the stub:publish command to publish the most common stubs to your application so that you can customize them:

shell
php artisan stub:publish

The published stubs will be located within a stubs directory in the root of your application. Any changes you make to these stubs will be reflected when you generate their corresponding classes using Artisan's make commands.

Events

Artisan dispatches three events when running commands: Illuminate\Console\Events\ArtisanStarting, Illuminate\Console\Events\CommandStarting, and Illuminate\Console\Events\CommandFinished. The ArtisanStarting event is dispatched immediately when Artisan starts running. Next, the CommandStarting event is dispatched immediately before a command runs. Finally, the CommandFinished event is dispatched once a command finishes executing.