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 是一個功能強大的 REPL,適用於 Laravel 框架,由 PsySH 套件提供支援。

Installation

所有 Laravel 應用程式預設都包含 Tinker。但是,如果您之前已將其從應用程式中移除,則可以使用 Composer 安裝 Tinker:

shell
composer require laravel/tinker

📌 備註

正在尋找與您的 Laravel 應用程式互動時的熱重載、多行程式碼編輯與自動完成功能嗎?請查看 Tinkerwell

Usage

Tinker 允許您在命令列上與您的整個 Laravel 應用程式互動,包括您的 Eloquent 模型、任務、事件等等。若要進入 Tinker 環境,請執行 tinker Artisan 指令:

shell
php artisan tinker

您可以使用 vendor:publish 指令發佈 Tinker 的設定檔:

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

⚠️ 警告

dispatch 輔助函式與 Dispatchable 類別上的 dispatch 方法依賴於記憶體回收來將任務放入佇列。因此,當使用 tinker 時,您應該使用 Bus::dispatchQueue::push 來分派任務。

Command Allow List

Tinker 利用一個「允許」清單來決定哪些 Artisan 指令允許在其 shell 中執行。預設情況下,您可以執行 clear-compileddownenvinspiremigratemigrate:installupoptimize 指令。如果您想允許更多指令,可以將它們新增到 tinker.php 設定檔中的 commands 陣列:

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

Classes That Should Not Be Aliased

通常,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

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

📌 備註

為了提高程式碼重用性,最好讓您的主控台指令保持輕量,並讓它們委託給應用程式服務來完成任務。在上述範例中,請注意我們注入了一個服務類別來執行發送電子郵件的「繁重工作」。

離開代碼

如果 handle 方法沒有回傳任何內容,且指令執行成功,指令將以 0 的離開代碼退出,表示成功。然而,handle 方法可以選擇性地回傳一個整數來手動指定指令的離開代碼:

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

return 1;

如果您想從指令中的任何方法「失敗」該指令,您可以使用 fail 方法。fail 方法將立即終止指令的執行,並回傳離開代碼 1

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

閉包指令

基於閉包的指令提供了一種替代方案,用於將主控台指令定義為類別。就像路由閉包是控制器的一種替代方案一樣,您可以將指令閉包視為指令類別的替代方案。

儘管 routes/console.php 檔案沒有定義 HTTP 路由,但它定義了基於主控台的應用程式進入點 (路由)。在此檔案中,您可以使用 Artisan::command 方法定義所有基於閉包的主控台指令。command 方法接受兩個引數:指令簽章 和一個接收指令引數和選項的閉包:

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

閉包會綁定到底層的指令實例,因此您可以完整存取通常在完整指令類別上可以存取的所有輔助方法。

型別提示依賴

除了接收指令的引數和選項之外,指令閉包還可以型別提示您希望從 服務容器 中解析的其他依賴:

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 指令時,將會顯示此說明:

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

可隔離指令

⚠️ 警告

要使用此功能,您的應用程式必須使用 memcachedredisdynamodbdatabasefilearray 快取驅動程式作為應用程式的預設快取驅動程式。此外,所有伺服器都必須與相同的中央快取伺服器通訊。

有時候,您可能希望確保指令在任何時候都只有一個實例可以執行。為此,您可以在指令類別上實作 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 透過嘗試使用應用程式的預設快取驅動程式取得原子鎖來實現此功能。如果該指令的其他實例正在執行,指令將不會執行;然而,指令仍將以成功的離開狀態代碼退出:

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 屬性讓您可以使用單一、表達性強且類似路由的語法來定義指令的名稱、引數與選項。

引數

所有使用者提供的引數和選項都包含在大括號中。在以下範例中,指令定義了一個必要引數:user

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

您也可以將引數設為選用,或為引數定義預設值:

// 選用引數...
'mail:send {user?}'

// 帶有預設值的選用引數...
'mail:send {user=foo}'

選項

選項,與引數一樣,是另一種形式的使用者輸入。當選項透過命令列提供時,它們會以兩個連字號 (--) 作為前綴。選項有兩種:一種是接收值的,另一種是不接收值的。不接收值的選項充當布林值「開關」。讓我們看看這種選項的範例:

/**
 * 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

帶有值的選項

接下來,讓我們看看一個期望有值的選項。如果使用者必須為選項指定一個值,您應該在選項名稱後加上 = 符號:

/**
 * 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

您可以透過在選項名稱後指定預設值來為選項指派預設值。如果使用者沒有傳遞任何選項值,將會使用預設值:

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

選項快捷方式

為選項指派快捷方式時,您可以在選項名稱前指定它,並使用 | 字元作為分隔符,將快捷方式與完整選項名稱分隔開:

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

在終端機中呼叫指令時,選項快捷方式應以單個連字號作為前綴,並且在指定選項值時,不應包含 = 字元:

shell
php artisan mail:send 1 -Qdefault

輸入陣列

如果您想定義引數或選項以期望多個輸入值,您可以使用 * 字元。首先,讓我們看看一個指定此類引數的範例:

'mail:send {user*}'

呼叫此方法時,user 引數可以依序傳遞到命令列。例如,以下指令會將 user 的值設為一個包含 12 的陣列:

shell
php artisan mail:send 1 2

* 字元可以與選用引數定義結合,以允許零個或多個引數實例:

'mail:send {user?*}'

選項陣列

定義期望多個輸入值的選項時,傳遞給指令的每個選項值都應以選項名稱作為前綴:

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

此類指令可以透過傳遞多個 --id 引數來呼叫:

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

輸入說明

您可以透過使用冒號將引數名稱與說明分隔開來為輸入引數和選項指派說明。如果您需要多一點空間來定義指令,可以將定義分散在多行上:

/**
 * 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

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 方法,返回一個以引數名稱為鍵的問題陣列:

/**
 * 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?',
    ];
}

您也可以透過使用包含問題和佔位符的元組來提供佔位符文字:

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

如果您希望完全控制提示,您可以提供一個閉包,該閉包應該提示使用者並返回其答案:

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()
            : []
    ),
];

📌 備註

全面的 Laravel Prompts 文件包含了有關可用提示及其用法的更多資訊。

如果您希望提示使用者選擇或輸入 選項,您可以在指令的 handle 方法中包含提示。但是,如果您只想在使用者也自動提示遺漏的引數時才提示使用者,那麼您可以實作 afterPromptingForMissingArguments 方法:

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

擷取輸入

在指令執行期間,您可能需要存取指令所接受的引數 (arguments) 和選項 (options) 的值。為此,您可以使用 argumentoption 方法。如果引數或選項不存在,將會回傳 null

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

如果您需要以 array 形式擷取所有引數,請呼叫 arguments 方法:

$arguments = $this->arguments();

選項的擷取方式與引數一樣簡單,使用 option 方法即可。若要以陣列形式擷取所有選項,請呼叫 options 方法:

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

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

提示輸入

📌 備註

Laravel Prompts 是一個 PHP 套件,用於為您的命令列應用程式增加美觀且使用者友善的表單,具有類似瀏覽器的功能,包括佔位符文字和驗證。

除了顯示輸出之外,您還可以在指令執行期間要求使用者提供輸入。 ask 方法會向使用者提示給定的問題,接受他們的輸入,然後將使用者的輸入回傳給您的指令:

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

    // ...
}

ask 方法也接受一個可選的第二個引數,該引數指定如果未提供使用者輸入時應回傳的預設值:

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

secret 方法類似於 ask,但使用者在主控台輸入時,其輸入內容將不會顯示。此方法在要求敏感資訊(例如密碼)時非常有用:

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

要求確認

如果您需要向使用者要求簡單的「是或否」確認,您可以使用 confirm 方法。預設情況下,此方法將回傳 false。但是,如果使用者在提示中輸入 yyes,該方法將回傳 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 (string $input) {
    // Return auto-completion options...
});

多選問題

如果您需要在詢問問題時向使用者提供一組預定義的選擇,您可以使用 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
);

寫入輸出

若要將輸出傳送到主控台,您可以使用 lineinfocommentquestionwarnerror 方法。這些方法中的每一個都將使用適合其用途的 ANSI 顏色。例如,讓我們向使用者顯示一些一般資訊。通常,info 方法將以綠色文字顯示在主控台中:

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

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

若要顯示錯誤訊息,請使用 error 方法。錯誤訊息文字通常以紅色顯示:

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

您可以使用 line 方法顯示純文本,不帶顏色:

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

您可以使用 newLine 方法顯示一個空行:

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

// Write three blank lines...
$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 $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 進度條組件文件

註冊指令

預設情況下,Laravel 會自動註冊 app/Console/Commands 目錄中的所有指令。但是,您可以使用應用程式 bootstrap/app.php 檔案中的 withCommands 方法,指示 Laravel 掃描其他目錄以查找 Artisan 指令:

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

如有必要,您也可以透過向 withCommands 方法提供指令的類別名稱來手動註冊指令:

use App\Domain\Orders\Commands\SendEmails;

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

當 Artisan 啟動時,應用程式中的所有指令都將由 服務容器 解析並向 Artisan 註冊。

以程式化方式執行指令

有時您可能希望在 CLI 之外執行 Artisan 指令。例如,您可能希望從路由或控制器中執行 Artisan 指令。您可以使用 Artisan facade 上的 call 方法來實現此目的。call 方法接受指令的簽章名稱或類別名稱作為其第一個引數,以及一個指令參數陣列作為第二個引數。它將返回離開碼:

use Illuminate\Support\Facades\Artisan;

Route::post('/user/{user}/mail', function (string $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 旗標,您應該將 truefalse 作為選項值傳遞:

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

將 Artisan 指令加入佇列

使用 Artisan facade 上的 queue 方法,您甚至可以將 Artisan 指令加入佇列,以便它們由您的 佇列工作者 在背景處理。在使用此方法之前,請確保您已配置佇列並正在運行佇列監聽器:

use Illuminate\Support\Facades\Artisan;

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

    // ...
});

使用 onConnectiononQueue 方法,您可以指定應將 Artisan 指令分派到的連線或佇列:

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

從其他指令呼叫指令

有時您可能希望從現有的 Artisan 指令中呼叫其他指令。您可以透過使用 call 方法來實現此目的。這個 call 方法接受指令名稱和一個指令引數/選項陣列:

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

    // ...
}

如果您希望呼叫另一個主控台指令並抑制其所有輸出,您可以使用 callSilently 方法。callSilently 方法與 call 方法的簽章相同:

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

訊號處理

如您所知,作業系統允許將訊號發送到執行中的程序。例如,SIGTERM 訊號是作業系統要求程序終止的方式。如果您希望在 Artisan 主控台指令中監聽訊號並在它們發生時執行程式碼,您可以使用 trap 方法:

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

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

要同時監聽多個訊號,您可以向 trap 方法提供一個訊號陣列:

$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\CommandFinishedArtisanStarting 事件在 Artisan 開始執行時立即分派。接著,CommandStarting 事件在指令運行之前立即分派。最後,CommandFinished 事件在指令執行完成後分派。