Skip to content

Artisan 控制台

介绍

Artisan 是 Laravel 附带的命令行界面。Artisan 位于应用程序的根目录下,作为 artisan 脚本提供了许多有用的命令,可以在构建应用程序时为您提供帮助。要查看所有可用的 Artisan 命令列表,可以使用 list 命令:

php
php artisan list

每个命令还包括一个“帮助”屏幕,显示并描述命令的可用参数和选项。要查看帮助屏幕,请在命令名称前加上 help

php
php artisan help migrate

Laravel Sail

如果您使用 Laravel Sail 作为本地开发环境,请记得使用 sail 命令行来调用 Artisan 命令。Sail 将在应用程序的 Docker 容器中执行您的 Artisan 命令:

php
./sail artisan list

Tinker (REPL)

Laravel Tinker 是一个强大的 REPL,适用于 Laravel 框架,由 PsySH 包提供支持。

安装

所有 Laravel 应用程序默认包含 Tinker。但是,如果您之前从应用程序中删除了它,可以使用 Composer 安装 Tinker:

php
composer require laravel/tinker
lightbulb

想要一个与 Laravel 应用程序交互的图形用户界面?请查看 Tinkerwell

用法

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

php
php artisan tinker

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

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

dispatch 辅助函数和 Dispatchable 类上的 dispatch 方法依赖于垃圾回收将作业放入队列。因此,在使用 tinker 时,您应该使用 Bus::dispatchQueue::push 来调度作业。

命令允许列表

Tinker 使用“允许”列表来确定哪些 Artisan 命令可以在其 shell 中运行。默认情况下,您可以运行 clear-compileddownenvinspiremigrateoptimizeup 命令。如果您想允许更多命令,可以将它们添加到 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 目录中;不过,只要您的命令可以被 Composer 加载,您可以自由选择自己的存储位置。

生成命令

要创建新命令,可以使用 make:command Artisan 命令。此命令将在 app/Console/Commands 目录中创建一个新的命令类。如果您的应用程序中不存在此目录,请不要担心 - 第一次运行 make:command Artisan 命令时将创建它:

php
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
{
    /**
     * 控制台命令的名称和签名。
     *
     * @var string
     */
    protected $signature = 'mail:send {user}';

    /**
     * 控制台命令的描述。
     *
     * @var string
     */
    protected $description = '向用户发送营销电子邮件';

    /**
     * 创建新的命令实例。
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * 执行控制台命令。
     *
     * @param  \App\Support\DripEmailer  $drip
     * @return mixed
     */
    public function handle(DripEmailer $drip)
    {
        $drip->send(User::find($this->argument('user')));
    }
}
lightbulb

为了更好地重用代码,建议保持控制台命令轻量,并让它们委托给应用程序服务来完成任务。在上面的示例中,请注意我们注入了一个服务类来完成发送电子邮件的“繁重工作”。

闭包命令

基于闭包的命令提供了定义控制台命令作为类的替代方法。就像路由闭包是控制器的替代方案一样,可以将命令闭包视为命令类的替代方案。在 app/Console/Kernel.php 文件的 commands 方法中,Laravel 加载 routes/console.php 文件:

php
/**
 * 为应用程序注册基于闭包的命令。
 *
 * @return void
 */
protected function commands()
{
    require base_path('routes/console.php');
}

即使此文件未定义 HTTP 路由,它也定义了进入应用程序的基于控制台的入口点(路由)。在此文件中,您可以使用 Artisan::command 方法定义所有基于闭包的控制台命令。command 方法接受两个参数:命令签名和接收命令参数和选项的闭包:

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

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

类型提示依赖项

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

php
use App\Models\User;
use App\Support\DripEmailer;

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

闭包命令描述

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

php
Artisan::command('mail:send {user}', function ($user) {
    // ...
})->purpose('向用户发送营销电子邮件');

定义输入期望

编写控制台命令时,通常需要通过参数或选项从用户那里收集输入。Laravel 使您可以非常方便地使用命令上的 signature 属性定义您期望用户提供的输入。signature 属性允许您以一种简单、类似路由的语法定义命令的名称、参数和选项。

参数

所有用户提供的参数和选项都用大括号括起来。在以下示例中,命令定义了一个必需的参数:user

php
/**
 * 控制台命令的名称和签名。
 *
 * @var string
 */
protected $signature = 'mail:send {user}';

您还可以使参数可选或为参数定义默认值:

php
// 可选参数...
mail:send {user?}

// 带有默认值的可选参数...
mail:send {user=foo}

选项

选项与参数一样,是另一种形式的用户输入。选项在命令行中提供时以两个连字符(--)为前缀。有两种类型的选项:接收值的选项和不接收值的选项。不接收值的选项用作布尔“开关”。让我们看一个这种类型选项的示例:

php
/**
 * 控制台命令的名称和签名。
 *
 * @var string
 */
protected $signature = 'mail:send {user} {--queue}';

在此示例中,可以在调用 Artisan 命令时指定 --queue 开关。如果传递了 --queue 开关,选项的值将为 true。否则,值将为 false

php
php artisan mail:send 1 --queue

带值的选项

接下来,让我们看一个期望值的选项。如果用户必须为选项指定一个值,您应该在选项名称后加上 = 符号:

php
/**
 * 控制台命令的名称和签名。
 *
 * @var string
 */
protected $signature = 'mail:send {user} {--queue=}';

在此示例中,用户可以像这样传递选项的值。如果在调用命令时未指定选项,其值将为 null

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

您可以通过在选项名称后指定默认值来为选项分配默认值。如果用户未传递选项值,将使用默认值:

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

选项快捷方式

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

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

在终端上调用命令时,选项快捷方式应以单个连字符为前缀:

php
php artisan mail:send 1 -Q

输入数组

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

php
mail:send {user*}

调用此方法时,可以按顺序将 user 参数传递给命令行。例如,以下命令将 user 的值设置为一个数组,其中包含 foobar 作为其值:

php
php artisan mail:send foo bar

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

php
mail:send {user?*}

选项数组

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

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

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

输入描述

您可以通过使用冒号将参数名称与描述分开来为输入参数和选项分配描述。如果您需要更多空间来定义命令,请随意将定义分布在多行上:

php
/**
 * 控制台命令的名称和签名。
 *
 * @var string
 */
protected $signature = 'mail:send
                        {user : 用户的 ID}
                        {--queue : 是否应将作业排队}';

命令 I/O

检索输入

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

php
/**
 * 执行控制台命令。
 *
 * @return int
 */
public function handle()
{
    $userId = $this->argument('user');

    //
}

如果需要将所有参数作为 array 检索,请调用 arguments 方法:

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

选项可以像参数一样轻松检索,使用 option 方法。要将所有选项作为数组检索,请调用 options 方法:

php
// 检索特定选项...
$queueName = $this->option('queue');

// 将所有选项作为数组检索...
$options = $this->options();

提示输入

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

php
/**
 * 执行控制台命令。
 *
 * @return mixed
 */
public function handle()
{
    $name = $this->ask('What is your name?');
}

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 ($input) {
    // 返回自动完成选项...
});

多选问题

如果需要在询问问题时为用户提供预定义的选择集,可以使用 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
/**
 * 执行控制台命令。
 *
 * @return mixed
 */
public function handle()
{
    // ...

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

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

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

您可以使用 line 方法显示普通的、无色的文本:

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

您可以使用 newLine 方法显示空行:

php
// 写入单个空行...
$this->newLine();

// 写入三个空行...
$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) {
    $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();
lightbulb

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

注册命令

所有控制台命令都在应用程序的 App\Console\Kernel 类中注册,这是应用程序的“控制台内核”。在此类的 commands 方法中,您将看到对内核的 load 方法的调用。load 方法将扫描 app/Console/Commands 目录,并自动注册其中包含的每个命令到 Artisan。您甚至可以自由地对 load 方法进行额外的调用,以扫描其他目录中的 Artisan 命令:

php
/**
 * 为应用程序注册命令。
 *
 * @return void
 */
protected function commands()
{
    $this->load(__DIR__.'/Commands');
    $this->load(__DIR__.'/../Domain/Orders/Commands');

    // ...
}

如果需要,您可以通过将命令的类名添加到 App\Console\Kernel 类中的 $commands 属性来手动注册命令。如果此属性尚未在内核上定义,您应手动定义它。当 Artisan 启动时,服务容器将解析此属性中列出的所有命令,并将其注册到 Artisan:

php
protected $commands = [
    Commands\SendEmails::class
];

以编程方式执行命令

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

php
use Illuminate\Support\Facades\Artisan;

Route::post('/user/{user}/mail', function ($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]
    ]);
});

传递布尔值

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

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

队列化 Artisan 命令

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

php
use Illuminate\Support\Facades\Artisan;

Route::post('/user/{user}/mail', function ($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
/**
 * 执行控制台命令。
 *
 * @return mixed
 */
public function handle()
{
    $this->call('mail:send', [
        'user' => 1, '--queue' => 'default'
    ]);

    //
}

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

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

信号处理

Symfony Console 组件(为 Artisan 控制台提供支持)允许您指示命令处理哪些进程信号(如果有)。例如,您可以指示命令处理 SIGINTSIGTERM 信号。

要开始,您应在 Artisan 命令类上实现 Symfony\Component\Console\Command\SignalableCommandInterface 接口。此接口要求您定义两个方法:getSubscribedSignalshandleSignal

php
<?php

use Symfony\Component\Console\Command\SignalableCommandInterface;

class StartServer extends Command implements SignalableCommandInterface
{
    // ...

    /**
     * 获取命令处理的信号列表。
     *
     * @return array
     */
    public function getSubscribedSignals(): array
    {
        return [SIGINT, SIGTERM];
    }

    /**
     * 处理传入的信号。
     *
     * @param  int  $signal
     * @return void
     */
    public function handleSignal(int $signal): void
    {
        if ($signal === SIGINT) {
            $this->stopServer();

            return;
        }
    }
}

正如您所料,getSubscribedSignals 方法应返回命令可以处理的信号数组,而 handleSignal 方法接收信号并可以相应地响应。

存根自定义

Artisan 控制台的 make 命令用于创建各种类,例如控制器、作业、迁移和测试。这些类是使用“存根”文件生成的,这些文件根据您的输入填充值。但是,您可能希望对 Artisan 生成的文件进行一些小的更改。为此,您可以使用 stub:publish 命令将最常用的存根发布到您的应用程序,以便您可以自定义它们:

php
php artisan stub:publish

发布的存根将位于应用程序根目录下的 stubs 目录中。您对这些存根所做的任何更改将在使用 Artisan 的 make 命令生成其对应的类时反映出来。

事件

运行命令时,Artisan 会调度三个事件:Illuminate\Console\Events\ArtisanStartingIlluminate\Console\Events\CommandStartingIlluminate\Console\Events\CommandFinishedArtisanStarting 事件在 Artisan 开始运行时立即调度。接下来,CommandStarting 事件在命令运行之前立即调度。最后,CommandFinished 事件在命令执行完成后调度。