Symfony透過bin/console提供許多指令(e.g. 常見的指令bin/console cache:clear),這些指令是由Console component建立,你可以使用它來建立新指令。
The Console: APP_ENV & APP_DEBUG
檔案.env裡的變數APP_ENV定義了主控台指令所運行的環境(environment),預設為 dev ,另外也會讀取 APP_DEBUG 的值來決定是否開啟或關閉"除錯(debug)"模式(預設 1 是開啟)。若要在其他環境跑指令或是除錯模式,可以編輯 APP_ENV 和 APP_DEBUG 的值。
建立指令
指令定義在一個延展(extned)Command的class。例如,你可能想要有個指令可以建立使用者:// src/Command/CreateUserCommand.php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class CreateUserCommand extends Command
{
// the name of the command (the part after "bin/console")
protected static $defaultName = 'app:create-user';
protected function configure()
{
// ...
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// ...
}
}
設定指令
你可以選擇要不要定義敘述(description)、提示語和輸入選項及變數(input options and arguments):// ...
protected function configure()
{
$this
// the short description shown while running "php bin/console list"
->setDescription('Creates a new user.')
// the full command description shown when running the command with
// the "--help" option
->setHelp('This command allows you to create a user...')
;
}
在指令的建構裡,最後會自動呼叫configure(),如果你的指令定義在自己的建構函數裡,先設定屬性並呼叫上層的建構函數(constructor),讓它的屬性可以在configure()裡被使用。// ...
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()
{
$this
// ...
->addArgument('password', $this->requirePassword ? InputArgument::REQUIRED : InputArgument::OPTIONAL, 'User password')
;
}
}
註冊指令
Symfony指令必須註冊成為一個服務(serviece)並標記(tagged)上標籤console.command,如果你使用預設的service.yaml設定檔(default service.yaml configuration),它已經幫你把這些事處理好了,真是多虧了autoconfiguration。執行指令
設定並註冊指令之後,你可以在終端機裡執行:php bin/console app:create-user就像你預期的一樣,這個指令並不會執行任何事情,因為你根本還沒寫任何程式邏輯在內,請把這些寫到 execute() 裡面。
主控台輸出(Console Output)
execute()方法可以存取輸出端,將訊息呈現在主控台上。// ...
protected function execute(InputInterface $input, OutputInterface $output)
{
// 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://secure.php.net/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.');
}
現在請試著執行指令:php bin/console app:create-user User Creator ============ Whoa! You are about to create a user.
輸出區塊(Output Sections)
一般主控台的輸入可以分為多個獨立的區塊(section),稱為"output sections",當你需要清除或是覆蓋掉輸出的資訊,可以建立一個或是多個區塊。section()會回傳一個ConsoleSectionOutput實例(instance),用來建立區塊:
class MyCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output)
{
$section1 = $output->section();
$section2 = $output->section();
$section1->writeln('Hello');
$section2->writeln('World!');
// Output displays "Hello\nWorld!\n"
// overwrite() replaces all the existing section contents with the given content
$section1->overwrite('Goodbye');
// Output now displays "Goodbye\nWorld!\n"
// clear() deletes all the section contents...
$section2->clear();
// 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);
// Output is now completely empty!
}
}
輸出區塊可以讓你以更進階的方式操控主控台輸出,像是呈現多個進度條(displaying multiple progress bars),能夠獨立地更新;以及加入多列(rows)到已經呈現的表格中。
主控台輸入(Console Input)
使用輸入選項或變數來傳遞訊息給指令:use Symfony\Component\Console\Input\InputArgument;
// ...
protected function configure()
{
$this
// configure an argument
->addArgument('username', InputArgument::REQUIRED, 'The username of the user.')
// ...
;
}
// ...
public function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln([
'User Creator',
'============',
'',
]);
// retrieve the argument value using getArgument()
$output->writeln('Username: '.$input->getArgument('username'));
}
現在,你可以在指令裡傳入使用者名稱:php bin/console app:create-user Wouter User Creator ============ Username: Wouter
從服務容器中取得服務(Getting Services from the Service Container)
為了要真正建立新的使用者,指令必須存取一些服務(services),因為指令已經註冊成服務,所以你可以使用一般的依賴注入(dependency injection),想像一下你打算存取一個服務 App\Service\UserManager:// ...
use App\Service\UserManager;
use Symfony\Component\Console\Command\Command;
class CreateUserCommand extends Command
{
private $userManager;
public function __construct(UserManager $userManager)
{
$this->userManager = $userManager;
parent::__construct();
}
// ...
protected function execute(InputInterface $input, OutputInterface $output)
{
// ...
$this->userManager->create($input->getArgument('username'));
$output->writeln('User successfully generated!');
}
}
指令的生命週期
運行指令時有三個生命週期方法可以調用(invoked):initialize()(optional)
這個方法會在interact()和execute()之前執行,主要的目的是初始化在其他指令方法中使用的變數。
interact()(optional)
這個方法在initialize()之後、execute()之前執行,目的是確認某些選項或變數是否遺漏,以及互動式地詢問使用者那些值,這是最後一次確認遺漏的選項或變數,若經過這個步驟仍有缺少的選項或變數,會導致錯誤發生。
execute()(required)
這個方法在interact()和initialize()之後執行,它包含所有指令程式邏輯。
測試指令
Symfony提供一些工具幫助你測試指令,最有用的是一個叫CommandTester的class,它使用特殊的輸入和輸出classes,無需實際控制台即可輕鬆進行測試:// 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()
{
$kernel = static::createKernel();
$application = new Application($kernel);
$command = $application->find('app:create-user');
$commandTester = new CommandTester($command);
$commandTester->execute([
'command' => $command->getName(),
// pass arguments to the helper
'username' => 'Wouter',
// prefix the key with two dashes when passing options,
// e.g: '--some-option' => 'option_value',
]);
// the output of the command in the console
$output = $commandTester->getDisplay();
$this->assertContains('Username: Wouter', $output);
// ...
}
}
紀錄指令錯誤(Logging Command Errors)
運行指令時若拋出任何例外(exception),Symfony會包含整個失敗的指令新增一筆紀錄。此外,Symfony註冊事件訂閱者(event subscriber)來監聽ConsoleEvents::TERMINATE event,以及每當指令並沒有以狀態0成功離開時,記錄這些訊息。資料翻譯自:
https://symfony.com/doc/4.3/console.html
系列文章:
- Symfony4.2 基本應用【第一篇】表單(Form)
- Symfony4.3 基本應用【第二篇】Doctrine ORM
- Symfony4.3 基本應用【第四篇】權限與安全 Authentication & Security

沒有留言:
張貼留言