跳到內容

主控台指令

編輯此頁

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_ENVAPP_DEBUG 的值。您也可以在執行指令時定義這些環境變數,例如

1
2
# clears the cache for the prod environment
$ APP_ENV=prod php bin/console cache:clear

主控台補完

如果您使用 Bash、Zsh 或 Fish shell,您可以安裝 Symfony 的補完腳本,以便在終端機中輸入指令時獲得自動補完。所有指令都支援名稱和選項補完,有些甚至可以補完值。

The terminal completes the command name "secrets:remove" and the argument "SOME_OTHER_SECRET".

首先,您必須一次安裝補完腳本。執行 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 將為 consolecomposer 指令提供補完。

建立指令

指令定義在擴展 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」 - 不同的小型工具,能夠協助您完成不同的任務

本作品,包括程式碼範例,根據 Creative Commons BY-SA 3.0 授權條款授權。
TOC
    版本