Artisan 控制台
介绍
Artisan 是 Laravel 附带的命令行界面。Artisan 位于应用程序根目录下,作为 artisan
脚本提供了许多有用的命令,可以在构建应用程序时为您提供帮助。要查看所有可用的 Artisan 命令列表,可以使用 list
命令:
php artisan list
每个命令还包括一个“帮助”屏幕,显示并描述命令的可用参数和选项。要查看帮助屏幕,请在命令名称前加上 help
:
php artisan help migrate
Laravel Sail
如果您使用 Laravel Sail 作为本地开发环境,请记得使用 sail
命令行来调用 Artisan 命令。Sail 将在应用程序的 Docker 容器中执行您的 Artisan 命令:
./vendor/bin/sail artisan list
Tinker (REPL)
Laravel Tinker 是一个强大的 Laravel 框架 REPL,由 PsySH 包提供支持。
安装
所有 Laravel 应用程序默认包含 Tinker。但是,如果您之前从应用程序中删除了它,可以使用 Composer 安装 Tinker:
composer require laravel/tinker
想要一个用于与 Laravel 应用程序交互的图形用户界面吗?请查看 Tinkerwell!
使用
Tinker 允许您在命令行上与整个 Laravel 应用程序交互,包括您的 Eloquent 模型、作业、事件等。要进入 Tinker 环境,请运行 tinker
Artisan 命令:
php artisan tinker
您可以使用 vendor:publish
命令发布 Tinker 的配置文件:
php artisan vendor:publish --provider="Laravel\Tinker\TinkerServiceProvider"
dispatch
辅助函数和 Dispatchable
类上的 dispatch
方法依赖于垃圾回收将作业放入队列。因此,在使用 tinker 时,您应该使用 Bus::dispatch
或 Queue::push
来调度作业。
命令允许列表
Tinker 使用“允许”列表来确定哪些 Artisan 命令可以在其 shell 中运行。默认情况下,您可以运行 clear-compiled
、down
、env
、inspire
、migrate
、optimize
和 up
命令。如果您想允许更多命令,可以将它们添加到 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 命令时将创建它:
php artisan make:command SendEmails
命令结构
生成命令后,您应为类的 signature
和 description
属性定义适当的值。这些属性将在 list
屏幕上显示您的命令时使用。signature
属性还允许您定义命令的输入期望。当执行命令时,将调用 handle
方法。您可以在此方法中放置命令逻辑。
让我们看一个示例命令。请注意,我们可以通过命令的 handle
方法请求所需的任何依赖项。Laravel 服务容器 将自动注入此方法签名中类型提示的所有依赖项:
<?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 = '向用户发送营销电子邮件';
/**
* 执行控制台命令。
*
* @param \App\Support\DripEmailer $drip
* @return mixed
*/
public function handle(DripEmailer $drip)
{
$drip->send(User::find($this->argument('user')));
}
}
为了更好地重用代码,建议保持控制台命令轻量,并让它们委托给应用程序服务来完成任务。在上面的示例中,请Note我们注入了一个服务类来完成发送电子邮件的“繁重工作”。
闭包命令
基于闭包的命令提供了定义控制台命令的类的替代方法。就像路由闭包是控制器的替代方案一样,可以将命令闭包视为命令类的替代方案。在 app/Console/Kernel.php
文件的 commands
方法中,Laravel 加载 routes/console.php
文件:
/**
* 为应用程序注册基于闭包的命令。
*
* @return void
*/
protected function commands()
{
require base_path('routes/console.php');
}
即使此文件未定义 HTTP 路由,它也定义了进入应用程序的基于控制台的入口点(路由)。在此文件中,您可以使用 Artisan::command
方法定义所有基于闭包的控制台命令。command
方法接受两个参数:命令签名和接收命令参数和选项的闭包:
Artisan::command('mail:send {user}', function ($user) {
$this->info("Sending email to: {$user}!");
});
闭包绑定到底层命令实例,因此您可以完全访问通常可以在完整命令类上访问的所有辅助方法。
类型提示依赖项
除了接收命令的参数和选项外,命令闭包还可以类型提示您希望从服务容器中解析的其他依赖项:
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 list
或 php artisan help
命令时,将显示此描述:
Artisan::command('mail:send {user}', function ($user) {
// ...
})->purpose('向用户发送营销电子邮件');
可隔离命令
要使用此功能,您的应用程序必须使用 memcached
、redis
、dynamodb
、database
、file
或 array
缓存驱动程序作为应用程序的默认缓存驱动程序。此外,所有服务器必须与同一个中央缓存服务器通信。
有时您可能希望确保一次只能运行一个命令实例。为此,您可以在命令类上实现 Illuminate\Contracts\Console\Isolatable
接口:
<?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 通过尝试使用应用程序的默认缓存驱动程序获取原子锁来实现这一点。如果其他命令实例正在运行,命令将不会执行;但是,命令仍将以成功的退出状态代码退出:
php artisan mail:send 1 --isolated
如果您希望指定命令无法执行时应返回的退出状态代码,可以通过 isolated
选项提供所需的状态代码:
php artisan mail:send 1 --isolated=12
锁定过期时间
默认情况下,隔离锁在命令完成后过期。或者,如果命令被中断且无法完成,锁将在一小时后过期。但是,您可以通过在命令上定义 isolationLockExpiresAt
方法来调整锁定过期时间:
/**
* 确定命令的隔离锁何时过期。
*
* @return \DateTimeInterface|\DateInterval
*/
public function isolationLockExpiresAt()
{
return now()->addMinutes(5);
}
定义输入期望
编写控制台命令时,通常需要通过参数或选项从用户那里收集输入。Laravel 使定义您期望用户输入的内容变得非常方便,您可以在命令的 signature
属性中定义。signature
属性允许您以单一、富有表现力的路由样式语法定义命令的名称、参数和选项。
参数
所有用户提供的参数和选项都用大括号括起来。在以下示例中,命令定义了一个必需参数:user
:
/**
* 控制台命令的名称和签名。
*
* @var string
*/
protected $signature = 'mail:send {user}';
您还可以使参数可选或为参数定义默认值:
// 可选参数...
'mail:send {user?}'
// 带默认值的可选参数...
'mail:send {user=foo}'
选项
选项与参数一样,是另一种形式的用户输入。选项在命令行中提供时以两个连字符(--
)为前缀。有两种类型的选项:接收值的选项和不接收值的选项。不接收值的选项用作布尔“开关”。让我们看一个这种类型选项的示例:
/**
* 控制台命令的名称和签名。
*
* @var string
*/
protected $signature = 'mail:send {user} {--queue}';
在此示例中,可以在调用 Artisan 命令时指定 --queue
开关。如果传递了 --queue
开关,选项的值将为 true
。否则,值将为 false
:
php artisan mail:send 1 --queue
带值的选项
接下来,让我们看一个期望值的选项。如果用户必须为选项指定一个值,您应该在选项名称后加上 =
符号:
/**
* 控制台命令的名称和签名。
*
* @var string
*/
protected $signature = 'mail:send {user} {--queue=}';
在此示例中,用户可以像这样为选项传递一个值。如果在调用命令时未指定选项,其值将为 null
:
php artisan mail:send 1 --queue=default
您可以通过在选项名称后指定默认值来为选项分配默认值。如果用户未传递选项值,将使用默认值:
'mail:send {user} {--queue=default}'
选项快捷方式
要在定义选项时分配快捷方式,可以在选项名称之前指定它,并使用 |
字符作为分隔符将快捷方式与完整选项名称分开:
'mail:send {user} {--Q|queue}'
在终端上调用命令时,选项快捷方式应以单个连字符为前缀:
php artisan mail:send 1 -Q
输入数组
如果您希望定义参数或选项以期望多个输入值,可以使用 *
字符。首先,让我们看一个指定此类参数的示例:
'mail:send {user*}'
调用此方法时,可以按顺序将 user
参数传递给命令行。例如,以下命令将 user
的值设置为一个数组,其中包含 1
和 2
作为其值:
php artisan mail:send 1 2
此 *
字符可以与可选参数定义结合使用,以允许零个或多个参数实例:
'mail:send {user?*}'
选项数组
定义期望多个输入值的选项时,每个传递给命令的选项值都应以选项名称为前缀:
'mail:send {--id=*}'
可以通过传递多个 --id
参数来调用此类命令:
php artisan mail:send --id=1 --id=2
输入描述
您可以通过使用冒号将参数名称与描述分开来为输入参数和选项分配描述。如果您需要更多空间来定义命令,请随意将定义分布在多行上:
/**
* 控制台命令的名称和签名。
*
* @var string
*/
protected $signature = 'mail:send
{user : 用户的 ID}
{--queue : 是否应将作业排队}';
命令 I/O
检索输入
在命令执行期间,您可能需要访问命令接受的参数和选项的值。为此,您可以使用 argument
和 option
方法。如果参数或选项不存在,将返回 null
:
/**
* 执行控制台命令。
*
* @return int
*/
public function handle()
{
$userId = $this->argument('user');
//
}
如果需要将所有参数作为 array
检索,请调用 arguments
方法:
$arguments = $this->arguments();
选项可以像参数一样轻松检索,使用 option
方法。要将所有选项作为数组检索,请调用 options
方法:
// 检索特定选项...
$queueName = $this->option('queue');
// 将所有选项作为数组检索...
$options = $this->options();
提示输入
除了显示输出外,您还可以在命令执行期间要求用户提供输入。ask
方法将使用给定的问题提示用户,接受他们的输入,然后将用户的输入返回给您的命令:
/**
* 执行控制台命令。
*
* @return mixed
*/
public function handle()
{
$name = $this->ask('What is your name?');
}
secret
方法类似于 ask
,但用户的输入在控制台中输入时对他们不可见。此方法在询问密码等敏感信息时很有用:
$password = $this->secret('What is the password?');
询问确认
如果需要询问用户简单的“是或否”确认,可以使用 confirm
方法。默认情况下,此方法将返回 false
。但是,如果用户在提示中输入 y
或 yes
,该方法将返回 true
。
if ($this->confirm('Do you wish to continue?')) {
//
}
如果需要,可以通过将 true
作为 confirm
方法的第二个参数传递,指定确认提示应默认返回 true
:
if ($this->confirm('Do you wish to continue?', true)) {
//
}
自动完成
anticipate
方法可用于为可能的选择提供自动完成。用户仍然可以提供任何答案,无论自动完成提示如何:
$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);
或者,您可以将闭包作为 anticipate
方法的第二个参数传递。每次用户输入字符时,都会调用闭包。闭包应接受一个包含用户输入的字符串参数,并返回一个用于自动完成的选项数组:
$name = $this->anticipate('What is your address?', function ($input) {
// 返回自动完成选项...
});
多选问题
如果需要在询问问题时为用户提供预定义的选择集,可以使用 choice
方法。您可以通过将索引作为方法的第三个参数传递,设置未选择选项时要返回的默认值的数组索引:
$name = $this->choice(
'What is your name?',
['Taylor', 'Dayle'],
$defaultIndex
);
此外,choice
方法接受可选的第四个和第五个参数,用于确定选择有效响应的最大尝试次数以及是否允许多选:
$name = $this->choice(
'What is your name?',
['Taylor', 'Dayle'],
$defaultIndex,
$maxAttempts = null,
$allowMultipleSelections = false
);
写入输出
要将输出发送到控制台,可以使用 line
、info
、comment
、question
、warn
和 error
方法。每种方法都将使用适当的 ANSI 颜色来实现其目的。例如,让我们向用户显示一些常规信息。通常,info
方法将在控制台中显示为绿色文本:
/**
* 执行控制台命令。
*
* @return mixed
*/
public function handle()
{
// ...
$this->info('The command was successful!');
}
要显示错误消息,请使用 error
方法。错误消息文本通常以红色显示:
$this->error('Something went wrong!');
您可以使用 line
方法显示普通的、未着色的文本:
$this->line('Display this on the screen');
您可以使用 newLine
方法显示空行:
// 写入单个空行...
$this->newLine();
// 写入三个空行...
$this->newLine(3);
表格
table
方法使正确格式化多行/列数据变得容易。您只需提供列名和表格数据,Laravel 将自动计算表格的适当宽度和高度:
use App\Models\User;
$this->table(
['Name', 'Email'],
User::all(['name', 'email'])->toArray()
);
进度条
对于长时间运行的任务,显示进度条以告知用户任务完成的程度可能会有所帮助。使用 withProgressBar
方法,Laravel 将显示进度条,并在每次迭代给定的可迭代值时推进其进度:
use App\Models\User;
$users = $this->withProgressBar(User::all(), function ($user) {
$this->performTask($user);
});
有时,您可能需要更手动地控制进度条的推进。首先,定义进程将迭代的步骤总数。然后,在处理每个项目后推进进度条:
$users = App\Models\User::all();
$bar = $this->output->createProgressBar(count($users));
$bar->start();
foreach ($users as $user) {
$this->performTask($user);
$bar->advance();
}
$bar->finish();
有关更高级的选项,请查看 Symfony Progress Bar 组件文档。
注册命令
所有控制台命令都在应用程序的 App\Console\Kernel
类中注册,这是应用程序的“控制台内核”。在此类的 commands
方法中,您将看到对内核的 load
方法的调用。load
方法将扫描 app/Console/Commands
目录,并自动注册其中包含的每个命令到 Artisan。您甚至可以自由地对 load
方法进行额外的调用,以扫描其他目录中的 Artisan 命令:
/**
* 为应用程序注册命令。
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
$this->load(__DIR__.'/../Domain/Orders/Commands');
// ...
}
如果需要,您可以通过将命令的类名添加到 App\Console\Kernel
类中的 $commands
属性来手动注册命令。如果此属性尚未在内核上定义,您应手动定义它。当 Artisan 启动时,所有列在此属性中的命令将由服务容器解析并注册到 Artisan:
protected $commands = [
Commands\SendEmails::class
];
以编程方式执行命令
有时您可能希望在 CLI 之外执行 Artisan 命令。例如,您可能希望从路由或控制器执行 Artisan 命令。您可以使用 Artisan
facade 上的 call
方法来实现这一点。call
方法接受命令的签名名称或类名作为第一个参数,以及命令参数数组作为第二个参数。将返回退出代码:
use Illuminate\Support\Facades\Artisan;
Route::post('/user/{user}/mail', function ($user) {
$exitCode = Artisan::call('mail:send', [
'user' => $user, '--queue' => 'default'
]);
//
});
或者,您可以将整个 Artisan 命令作为字符串传递给 call
方法:
Artisan::call('mail:send 1 --queue=default');
传递数组值
如果命令定义了一个接受数组的选项,可以将值数组传递给该选项:
use Illuminate\Support\Facades\Artisan;
Route::post('/mail', function () {
$exitCode = Artisan::call('mail:send', [
'--id' => [5, 13]
]);
});
传递布尔值
如果需要指定不接受字符串值的选项的值,例如 migrate:refresh
命令上的 --force
标志,应将 true
或 false
作为选项的值传递:
$exitCode = Artisan::call('migrate:refresh', [
'--force' => true,
]);
队列化 Artisan 命令
使用 Artisan
facade 上的 queue
方法,您甚至可以将 Artisan 命令排队,以便它们由队列工作者在后台处理。在使用此方法之前,请确保已配置队列并正在运行队列监听器:
use Illuminate\Support\Facades\Artisan;
Route::post('/user/{user}/mail', function ($user) {
Artisan::queue('mail:send', [
'user' => $user, '--queue' => 'default'
]);
//
});
使用 onConnection
和 onQueue
方法,您可以指定 Artisan 命令应调度到的连接或队列:
Artisan::queue('mail:send', [
'user' => 1, '--queue' => 'default'
])->onConnection('redis')->onQueue('commands');
从其他命令调用命令
有时您可能希望从现有的 Artisan 命令调用其他命令。您可以使用 call
方法来实现这一点。此 call
方法接受命令名称和命令参数/选项数组:
/**
* 执行控制台命令。
*
* @return mixed
*/
public function handle()
{
$this->call('mail:send', [
'user' => 1, '--queue' => 'default'
]);
//
}
如果您希望调用另一个控制台命令并抑制其所有输出,可以使用 callSilently
方法。callSilently
方法的签名与 call
方法相同:
$this->callSilently('mail:send', [
'user' => 1, '--queue' => 'default'
]);
信号处理
如您所知,操作系统允许信号发送到正在运行的进程。例如,SIGTERM
信号是操作系统请求程序终止的方式。如果您希望在 Artisan 控制台命令中监听信号并在它们发生时执行代码,可以使用 trap
方法:
/**
* 执行控制台命令。
*
* @return mixed
*/
public function handle()
{
$this->trap(SIGTERM, fn () => $this->shouldKeepRunning = false);
while ($this->shouldKeepRunning) {
// ...
}
}
要同时监听多个信号,可以将信号数组提供给 trap
方法:
$this->trap([SIGTERM, SIGQUIT], function ($signal) {
$this->shouldKeepRunning = false;
dump($signal); // SIGTERM / SIGQUIT
});
存根自定义
Artisan 控制台的 make
命令用于创建各种类,例如控制器、作业、迁移和测试。这些类是使用“存根”文件生成的,这些文件根据您的输入填充值。但是,您可能希望对 Artisan 生成的文件进行小的更改。为此,您可以使用 stub:publish
命令将最常用的存根发布到您的应用程序,以便您可以自定义它们:
php artisan stub:publish
发布的存根将位于应用程序根目录下的 stubs
目录中。您对这些存根所做的任何更改将在使用 Artisan 的 make
命令生成其对应的类时反映出来。
事件
Artisan 在运行命令时会调度三个事件:Illuminate\Console\Events\ArtisanStarting
、Illuminate\Console\Events\CommandStarting
和 Illuminate\Console\Events\CommandFinished
。ArtisanStarting
事件在 Artisan 开始运行时立即调度。接下来,CommandStarting
事件在命令运行之前立即调度。最后,CommandFinished
事件在命令执行完成后调度。