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
沒有留言:
張貼留言