主控台指令
Symfony 框架透過 bin/console
腳本提供許多指令(例如,著名的 bin/console cache:clear
指令)。這些指令是使用 Console 組件建立的。您也可以使用它來建立您自己的指令。
執行指令
每個 Symfony 應用程式都帶有一大組指令。您可以使用 list
指令來檢視應用程式中所有可用的指令
1 2 3 4 5 6 7 8 9 10 11 12 13
$ php bin/console list
...
Available commands:
about Display information about the current project
completion Dump the shell completion script
help Display help for a command
list List commands
assets
assets:install Install bundle's web assets under a public directory
cache
cache:clear Clear the cache
...
注意
list
是預設指令,因此執行 php bin/console
是一樣的。
如果您找到需要的指令,可以使用 --help
選項來執行它,以檢視指令的文件
1
$ php bin/console assets:install --help
注意
--help
是 Console 組件的內建全域選項之一,適用於所有指令,包括您可以建立的指令。若要深入了解它們,您可以閱讀此章節。
APP_ENV & APP_DEBUG
主控台指令在 環境中執行,該環境定義在 .env
檔案的 APP_ENV
變數中,預設為 dev
。它也會讀取 APP_DEBUG
值來開啟或關閉「除錯」模式(預設為 1
,即開啟)。
若要在另一個環境或除錯模式下執行指令,請編輯 APP_ENV
和 APP_DEBUG
的值。您也可以在執行指令時定義這些環境變數,例如
1 2
# clears the cache for the prod environment
$ APP_ENV=prod php bin/console cache:clear
主控台補完
如果您使用 Bash、Zsh 或 Fish shell,您可以安裝 Symfony 的補完腳本,以便在終端機中輸入指令時獲得自動補完。所有指令都支援名稱和選項補完,有些甚至可以補完值。

首先,您必須一次安裝補完腳本。執行 bin/console completion --help
以取得適用於您的 shell 的安裝指示。
注意
當使用 Bash 時,請確保您已為您的作業系統安裝並設定「bash completion」套件(通常名為 bash-completion
)。
安裝並重新啟動您的終端機後,您就設定完成可以使用補完了(預設情況下,按下 Tab 鍵)。
提示
許多 PHP 工具都是使用 Symfony Console 組件建置的(例如 Composer、PHPstan 和 Behat)。如果它們使用的是 5.4 或更高版本,您也可以安裝它們的補完腳本來啟用主控台補完
1 2
$ php vendor/bin/phpstan completion --help
$ composer completion --help
提示
如果您使用 Symfony 本地 web server,建議使用內建的補完腳本,這將確保在執行主控台補完時使用正確的 PHP 版本和設定。執行 symfony completion --help
以取得適用於您的 shell 的安裝指示。Symfony CLI 將為 console
和 composer
指令提供補完。
建立指令
指令定義在擴展 Command 的類別中。例如,您可能想要一個指令來建立使用者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// src/Command/CreateUserCommand.php
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
// the name of the command is what users type after "php bin/console"
#[AsCommand(name: 'app:create-user')]
class CreateUserCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
// ... put here the code to create the user
// this method must return an integer number with the "exit status code"
// of the command. You can also use these constants to make code more readable
// return this if there was no problem running the command
// (it's equivalent to returning int(0))
return Command::SUCCESS;
// or return this if some error happened during the execution
// (it's equivalent to returning int(1))
// return Command::FAILURE;
// or return this to indicate incorrect command usage; e.g. invalid options
// or missing arguments (it's equivalent to returning int(2))
// return Command::INVALID
}
}
設定指令
您可以選擇性地定義描述、說明訊息和 輸入選項和引數,方法是覆寫 configure()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// src/Command/CreateUserCommand.php
// ...
class CreateUserCommand extends Command
{
// ...
protected function configure(): void
{
$this
// the command description shown when running "php bin/console list"
->setDescription('Creates a new user.')
// the command help shown when running the command with the "--help" option
->setHelp('This command allows you to create a user...')
;
}
}
提示
使用 #[AsCommand]
屬性來定義描述,而不是使用 setDescription()
方法,可以讓您在不實例化其類別的情況下取得指令描述。這使得 php bin/console list
指令執行速度更快。
如果您希望始終快速執行 list
指令,請將 --short
選項新增至其中(php bin/console list --short
)。這將避免實例化指令類別,但對於使用 setDescription()
方法而不是屬性來定義指令描述的指令,它將不會顯示任何描述。
configure()
方法會在指令建構子的結尾自動呼叫。如果您的指令定義了自己的建構子,請先設定屬性,然後呼叫父建構子,以使這些屬性在 configure()
方法中可用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// ...
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
class CreateUserCommand extends Command
{
// ...
public function __construct(bool $requirePassword = false)
{
// best practices recommend to call the parent constructor first and
// then set your own properties. That wouldn't work in this case
// because configure() needs the properties set in this constructor
$this->requirePassword = $requirePassword;
parent::__construct();
}
protected function configure(): void
{
$this
// ...
->addArgument('password', $this->requirePassword ? InputArgument::REQUIRED : InputArgument::OPTIONAL, 'User password')
;
}
}
註冊指令
您可以透過將 AsCommand
屬性新增至指令來註冊指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// src/Command/CreateUserCommand.php
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
#[AsCommand(
name: 'app:create-user',
description: 'Creates a new user.',
hidden: false,
aliases: ['app:add-user']
)]
class CreateUserCommand extends Command
{
// ...
}
如果您無法使用 PHP 屬性,請將指令註冊為服務,並使用 console.command
標籤標記它。如果您使用的是預設 services.yaml 設定,這已經為您完成,這要歸功於自動設定。
執行指令
在設定和註冊指令後,您可以在終端機中執行它
1
$ php bin/console app:create-user
正如您可能預期的那樣,此指令將不會執行任何操作,因為您尚未撰寫任何邏輯。在 execute()
方法內新增您自己的邏輯。
主控台輸出
execute()
方法可以存取輸出流,以將訊息寫入主控台
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// ...
protected function execute(InputInterface $input, OutputInterface $output): int
{
// outputs multiple lines to the console (adding "\n" at the end of each line)
$output->writeln([
'User Creator',
'============',
'',
]);
// the value returned by someMethod() can be an iterator (https://php.dev.org.tw/iterator)
// that generates and returns the messages with the 'yield' PHP keyword
$output->writeln($this->someMethod());
// outputs a message followed by a "\n"
$output->writeln('Whoa!');
// outputs a message without adding a "\n" at the end of the line
$output->write('You are about to ');
$output->write('create a user.');
return Command::SUCCESS;
}
現在,嘗試執行指令
1 2 3 4 5 6
$ php bin/console app:create-user
User Creator
============
Whoa!
You are about to create a user.
輸出區段
常規主控台輸出可以劃分為多個獨立的區域,稱為「輸出區段」。當您需要清除和覆寫輸出資訊時,請建立一個或多個這些區段。
區段是使用 ConsoleOutput::section() 方法建立的,該方法會傳回 ConsoleSectionOutput 的實例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
// ...
use Symfony\Component\Console\Output\ConsoleOutputInterface;
class MyCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!$output instanceof ConsoleOutputInterface) {
throw new \LogicException('This command accepts only an instance of "ConsoleOutputInterface".');
}
$section1 = $output->section();
$section2 = $output->section();
$section1->writeln('Hello');
$section2->writeln('World!');
sleep(1);
// Output displays "Hello\nWorld!\n"
// overwrite() replaces all the existing section contents with the given content
$section1->overwrite('Goodbye');
sleep(1);
// Output now displays "Goodbye\nWorld!\n"
// clear() deletes all the section contents...
$section2->clear();
sleep(1);
// Output now displays "Goodbye\n"
// ...but you can also delete a given number of lines
// (this example deletes the last two lines of the section)
$section1->clear(2);
sleep(1);
// Output is now completely empty!
// setting the max height of a section will make new lines replace the old ones
$section1->setMaxHeight(2);
$section1->writeln('Line1');
$section1->writeln('Line2');
$section1->writeln('Line3');
return Command::SUCCESS;
}
}
注意
當在區段中顯示資訊時,會自動附加新行。
輸出區段可讓您以進階方式操作主控台輸出,例如顯示多個進度列,這些進度列會獨立更新,以及將列附加到已呈現的表格。
警告
終端機僅允許覆寫可見內容,因此當嘗試寫入/覆寫區段內容時,您必須考量主控台高度。
主控台輸入
使用輸入選項或引數將資訊傳遞至指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
use Symfony\Component\Console\Input\InputArgument;
// ...
protected function configure(): void
{
$this
// configure an argument
->addArgument('username', InputArgument::REQUIRED, 'The username of the user.')
// ...
;
}
// ...
public function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln([
'User Creator',
'============',
'',
]);
// retrieve the argument value using getArgument()
$output->writeln('Username: '.$input->getArgument('username'));
return Command::SUCCESS;
}
現在,您可以將使用者名稱傳遞至指令
1 2 3 4 5
$ php bin/console app:create-user Wouter
User Creator
============
Username: Wouter
另請參閱
閱讀主控台輸入(引數和選項),以取得有關主控台選項和引數的更多資訊。
從服務容器取得服務
為了實際建立新使用者,指令必須存取一些服務。由於您的指令已註冊為服務,因此您可以使用正常的依賴注入。假設您有一個 App\Service\UserManager
服務,您想要存取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// ...
use App\Service\UserManager;
use Symfony\Component\Console\Command\Command;
class CreateUserCommand extends Command
{
public function __construct(
private UserManager $userManager,
){
parent::__construct();
}
// ...
protected function execute(InputInterface $input, OutputInterface $output): int
{
// ...
$this->userManager->create($input->getArgument('username'));
$output->writeln('User successfully generated!');
return Command::SUCCESS;
}
}
指令生命週期
指令有三個生命週期方法,會在執行指令時調用
- initialize() (選用)
- 此方法在
interact()
和execute()
方法之前執行。其主要目的是初始化指令方法其餘部分中使用的變數。 - interact() (選用)
- 此方法在
initialize()
之後和execute()
之前執行。其目的是檢查是否遺失某些選項/引數,並以互動方式要求使用者提供這些值。這是您可以要求遺失的必要選項/引數的最後一個位置。此方法在驗證輸入之前呼叫。請注意,當指令在沒有互動的情況下執行時(例如,當傳遞--no-interaction
全域選項旗標時),將不會呼叫此方法。 - execute() (必要)
- 此方法在
interact()
和initialize()
之後執行。它包含您希望指令執行的邏輯,並且必須傳回一個整數,該整數將用作指令結束狀態。
測試指令
Symfony 提供多種工具來協助您測試指令。最有用的工具是 CommandTester 類別。它使用特殊的輸入和輸出類別,以便在沒有實際主控台的情況下簡化測試
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
// tests/Command/CreateUserCommandTest.php
namespace App\Tests\Command;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Tester\CommandTester;
class CreateUserCommandTest extends KernelTestCase
{
public function testExecute(): void
{
self::bootKernel();
$application = new Application(self::$kernel);
$command = $application->find('app:create-user');
$commandTester = new CommandTester($command);
$commandTester->execute([
// pass arguments to the helper
'username' => 'Wouter',
// prefix the key with two dashes when passing options,
// e.g: '--some-option' => 'option_value',
// use brackets for testing array value,
// e.g: '--some-option' => ['option_value'],
]);
$commandTester->assertCommandIsSuccessful();
// the output of the command in the console
$output = $commandTester->getDisplay();
$this->assertStringContainsString('Username: Wouter', $output);
// ...
}
}
如果您使用的是單一指令應用程式,請在其上呼叫 setAutoExit(false)
,以在 CommandTester
中取得指令結果。
提示
您也可以使用 ApplicationTester 來測試整個主控台應用程式。
警告
當使用 CommandTester
類別測試指令時,不會分派主控台事件。如果您需要測試這些事件,請改用 ApplicationTester。
警告
當使用 ApplicationTester 類別測試指令時,請不要忘記停用自動結束旗標
1 2 3 4
$application = new Application();
$application->setAutoExit(false);
$tester = new ApplicationTester($application);
警告
當測試 InputOption::VALUE_NONE
指令選項時,您必須將 true
傳遞給它們
1 2
$commandTester = new CommandTester($command);
$commandTester->execute(['--some-option' => true]);
注意
當在獨立專案中使用 Console 組件時,請使用 Application 並擴展正常的 \PHPUnit\Framework\TestCase
。
當測試您的指令時,了解您的指令在不同設定(例如終端機的寬度和高度,甚至使用的色彩模式)下的反應方式可能會很有用。由於 Terminal 類別,您可以存取此類資訊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use Symfony\Component\Console\Terminal;
$terminal = new Terminal();
// gets the number of lines available
$height = $terminal->getHeight();
// gets the number of columns available
$width = $terminal->getWidth();
// gets the color mode
$colorMode = $terminal->getColorMode();
// changes the color mode
$colorMode = $terminal->setColorMode(AnsiColorMode::Ansi24);
記錄指令錯誤
每當在執行指令時擲出例外狀況時,Symfony 會為其新增一個記錄訊息,其中包含整個失敗的指令。此外,Symfony 註冊了一個事件訂閱者,以監聽 ConsoleEvents::TERMINATE 事件,並且每當指令未以 0
結束狀態完成時,就會新增一個記錄訊息。
分析指令效能
Symfony 允許分析任何指令(包括您的指令)的執行效能。首先,請確保啟用除錯模式和效能分析器。然後,在執行指令時新增 --profile
選項
1
$ php bin/console --profile app:my-command
Symfony 現在將收集有關指令執行的資料,這有助於除錯錯誤或檢查其他問題。當指令執行完成後,可以透過效能分析器的網頁存取效能分析。
提示
如果您在詳細模式下執行指令(新增 -v
選項),Symfony 將在輸出中顯示一個可點擊的連結,指向指令效能分析(如果您的終端機支援連結)。如果您在除錯詳細模式下執行它(-vvv
),您還將看到指令消耗的時間和記憶體。
警告
當分析來自Messenger 組件的 messenger:consume
指令時,請將 --no-reset
選項新增至指令,否則您將不會獲得任何效能分析。此外,請考慮使用 --limit
選項僅處理少量訊息,以使效能分析在效能分析器中更易於閱讀。
深入了解
主控台組件還包含一組「helpers」 - 不同的小型工具,能夠協助您完成不同的任務
- Question Helper:以互動方式要求使用者提供資訊
- Formatter Helper:自訂輸出色彩
- Progress Bar:顯示進度列
- Progress Indicator:顯示進度指示器
- Table:以表格形式顯示表格資料
- Debug Formatter Helper:在執行外部程式時提供輸出除錯資訊的功能
- Process Helper:允許使用
DebugFormatterHelper
執行處理程序 - Cursor Helper:允許操作終端機中的游標