如何设置控制台命令的样式
创建控制台命令时,最枯燥的任务之一是处理命令输入和输出的样式。显示标题和表格或向用户提问涉及大量重复代码。
例如,考虑用于显示以下命令标题的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// src/Command/GreetCommand.php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class GreetCommand extends Command
{
// ...
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln([
'<info>Lorem Ipsum Dolor Sit Amet</>',
'<info>==========================</>',
'',
]);
// ...
}
}
显示一个简单的标题需要三行代码,即更改字体颜色、为内容添加下划线并在标题后留出一个额外的空行。对于设计良好的命令来说,处理样式是必需的,但这会不必要地使代码复杂化。
为了减少这些样板代码,Symfony 命令可以选择使用 Symfony 样式指南。这些样式实现为一组辅助方法,允许创建语义化命令并忘记它们的样式。
基本用法
在您的命令中,实例化 SymfonyStyle 类,并将 $input
和 $output
变量作为其参数传递。然后,您可以开始使用它的任何辅助方法,例如 title()
,它显示命令的标题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// src/Command/GreetCommand.php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class GreetCommand extends Command
{
// ...
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title('Lorem Ipsum Dolor Sit Amet');
// ...
}
}
辅助方法
SymfonyStyle 类定义了一些辅助方法,涵盖了控制台命令执行的最常见交互。
内容方法
- text()
-
将给定的字符串或字符串数组显示为常规文本。这对于呈现帮助消息和运行命令的用户的说明很有用
1 2 3 4 5 6 7 8 9 10 11
// use simple strings for short messages $io->text('Lorem ipsum dolor sit amet'); // ... // consider using arrays when displaying long messages $io->text([ 'Lorem ipsum dolor sit amet', 'Consectetur adipiscing elit', 'Aenean sit amet arcu vitae sem faucibus porta', ]);
- listing()
-
显示作为数组传递的元素的无序列表
1 2 3 4 5
$io->listing([ 'Element #1 Lorem ipsum dolor sit amet', 'Element #2 Lorem ipsum dolor sit amet', 'Element #3 Lorem ipsum dolor sit amet', ]);
- table()
-
将给定的标题和行数组显示为紧凑的表格
1 2 3 4 5 6 7 8
$io->table( ['Header 1', 'Header 2'], [ ['Cell 1-1', 'Cell 1-2'], ['Cell 2-1', 'Cell 2-2'], ['Cell 3-1', 'Cell 3-2'], ] );
- horizontalTable()
-
将给定的标题和行数组显示为紧凑的水平表格
1 2 3 4 5 6 7 8
$io->horizontalTable( ['Header 1', 'Header 2'], [ ['Cell 1-1', 'Cell 1-2'], ['Cell 2-1', 'Cell 2-2'], ['Cell 3-1', 'Cell 3-2'], ] );
- definitionList()
-
将给定的
key => value
对显示为紧凑的元素列表1 2 3 4 5 6 7 8 9
$io->definitionList( 'This is a title', ['foo1' => 'bar1'], ['foo2' => 'bar2'], ['foo3' => 'bar3'], new TableSeparator(), 'This is another title', ['foo4' => 'bar4'] );
- createTable()
- 创建 Table 的实例,其样式根据 Symfony 样式指南设置,允许您使用诸如动态追加行等功能。
- newLine()
-
在命令输出中显示一个空行。虽然它看起来可能很有用,但在大多数情况下您根本不需要它。原因是每个辅助方法都已经添加了自己的空行,因此您不必关心垂直间距
1 2 3 4 5
// outputs a single blank line $io->newLine(); // outputs three consecutive blank lines $io->newLine(3);
告诫方法
- note()
-
将给定的字符串或字符串数组显示为突出显示的告诫。谨慎使用此辅助方法,以避免使命令的输出混乱
1 2 3 4 5 6 7 8 9 10 11
// use simple strings for short notes $io->note('Lorem ipsum dolor sit amet'); // ... // consider using arrays when displaying long notes $io->note([ 'Lorem ipsum dolor sit amet', 'Consectetur adipiscing elit', 'Aenean sit amet arcu vitae sem faucibus porta', ]);
- caution()
-
与
note()
辅助方法类似,但内容更加突出显示。结果内容类似于错误消息,因此除非绝对必要,否则应避免使用此辅助方法1 2 3 4 5 6 7 8 9 10 11
// use simple strings for short caution message $io->caution('Lorem ipsum dolor sit amet'); // ... // consider using arrays when displaying long caution messages $io->caution([ 'Lorem ipsum dolor sit amet', 'Consectetur adipiscing elit', 'Aenean sit amet arcu vitae sem faucibus porta', ]);
进度条方法
- progressStart()
-
显示一个进度条,其步数等于传递给该方法的参数(如果进度条的长度未知,则不要传递任何值)
1 2 3 4 5
// displays a progress bar of unknown length $io->progressStart(); // displays a 100-step length progress bar $io->progressStart(100);
- progressAdvance()
-
使进度条前进给定的步数(如果未传递任何参数,则前进
1
步)1 2 3 4 5
// advances the progress bar 1 step $io->progressAdvance(); // advances the progress bar 10 steps $io->progressAdvance(10);
- progressFinish()
-
完成进度条(当其长度已知时,填充所有剩余步骤)
1
$io->progressFinish();
- progressIterate()
-
如果您的进度条循环遍历可迭代的集合,请使用
progressIterate()
辅助方法1 2 3 4 5
$iterable = [1, 2]; foreach ($io->progressIterate($iterable) as $value) { // ... do some work }
- createProgressBar()
- 创建 ProgressBar 的实例,其样式根据 Symfony 样式指南设置。
用户输入方法
- ask()
-
要求用户提供一些值
1
$io->ask('What is your name?');
您可以将默认值作为第二个参数传递,以便用户可以按 <Enter> 键来选择该值
1
$io->ask('Where are you from?', 'United States');
如果您需要验证给定的值,请将回调验证器作为第三个参数传递
1 2 3 4 5 6 7
$io->ask('Number of workers to start', '1', function (string $number): int { if (!is_numeric($number)) { throw new \RuntimeException('You must type a number.'); } return (int) $number; });
- askHidden()
-
它与
ask()
方法非常相似,但用户的输入将被隐藏,并且它不能定义默认值。在询问敏感信息时使用它1
$io->askHidden('What is your password?');
如果您需要验证给定的值,请将回调验证器作为第二个参数传递
1 2 3 4 5 6 7
$io->askHidden('What is your password?', function (string $password): string { if (empty($password)) { throw new \RuntimeException('Password cannot be empty.'); } return $password; });
- confirm()
-
向用户询问一个“是/否”问题,并且只返回
true
或false
1
$io->confirm('Restart the web server?');
您可以将默认值作为第二个参数传递,以便用户可以按 <Enter> 键来选择该值
1
$io->confirm('Restart the web server?', true);
- choice()
-
提出一个问题,其答案被限制在给定的有效答案列表中
1
$io->choice('Select the queue to analyze', ['queue1', 'queue2', 'queue3']);
您可以将默认值作为第三个参数传递,以便用户可以按 <Enter> 键来选择该值
1
$io->choice('Select the queue to analyze', ['queue1', 'queue2', 'queue3'], 'queue1');
最后,您可以允许用户选择多个选项。为此,用户必须用逗号分隔每个选项(例如,键入
1, 2
将选择选项 1 和 2)1
$io->choice('Select the queue to analyze', ['queue1', 'queue2', 'queue3'], multiSelect: true);
结果方法
注意
如果您打印任何 URL,它都不会被断开/切断,它将是可点击的 - 如果终端提供此功能。如果“格式良好的输出”更重要,您可以关闭它
1
$io->getOutputWrapper()->setAllowCutUrls(true);
- success()
-
将给定的字符串或字符串数组显示为突出显示为成功消息的消息(带有绿色背景和
[OK]
标签)。它旨在用于显示执行给定命令的最终结果一次,但您可以在命令执行期间重复使用它1 2 3 4 5 6 7 8 9 10
// use simple strings for short success messages $io->success('Lorem ipsum dolor sit amet'); // ... // consider using arrays when displaying long success messages $io->success([ 'Lorem ipsum dolor sit amet', 'Consectetur adipiscing elit', ]);
- info()
-
它类似于
success()
方法(给定的字符串或字符串数组以绿色背景显示),但没有前缀[OK]
标签。它旨在用于显示执行给定命令的最终结果一次,而不将结果显示为成功或失败1 2 3 4 5 6 7 8 9 10
// use simple strings for short info messages $io->info('Lorem ipsum dolor sit amet'); // ... // consider using arrays when displaying long info messages $io->info([ 'Lorem ipsum dolor sit amet', 'Consectetur adipiscing elit', ]);
- warning()
-
将给定的字符串或字符串数组显示为突出显示为警告消息的消息(带有红色背景和
[WARNING]
标签)。它旨在用于显示执行给定命令的最终结果一次,但您可以在命令执行期间重复使用它1 2 3 4 5 6 7 8 9 10
// use simple strings for short warning messages $io->warning('Lorem ipsum dolor sit amet'); // ... // consider using arrays when displaying long warning messages $io->warning([ 'Lorem ipsum dolor sit amet', 'Consectetur adipiscing elit', ]);
- error()
-
将给定的字符串或字符串数组显示为突出显示为错误消息的消息(带有红色背景和
[ERROR]
标签)。它旨在用于显示执行给定命令的最终结果一次,但您可以在命令执行期间重复使用它1 2 3 4 5 6 7 8 9 10
// use simple strings for short error messages $io->error('Lorem ipsum dolor sit amet'); // ... // consider using arrays when displaying long error messages $io->error([ 'Lorem ipsum dolor sit amet', 'Consectetur adipiscing elit', ]);
配置默认样式
默认情况下,Symfony 样式会包装所有内容,以避免文本行过长。唯一的例外是 URL,无论它们有多长,都不会被包装。这样做是为了在支持它们的终端中启用可点击的 URL。
如果您希望包装所有内容,包括 URL,请使用此方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/Command/GreetCommand.php
namespace App\Command;
// ...
use Symfony\Component\Console\Style\SymfonyStyle;
class GreetCommand extends Command
{
// ...
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->getOutputWrapper()->setAllowCutUrls(true);
// ...
}
}
定义您自己的样式
如果您不喜欢使用 Symfony 样式的命令的设计,您可以定义自己的一组控制台样式。创建一个实现 StyleInterface 的类
1 2 3 4 5 6 7 8
namespace App\Console;
use Symfony\Component\Console\Style\StyleInterface;
class CustomStyle implements StyleInterface
{
// ...implement the methods of the interface
}
然后,在您的命令中实例化这个自定义类,而不是默认的 SymfonyStyle
。感谢 StyleInterface
,您无需更改命令的代码即可更改其外观
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// src/Command/GreetCommand.php
namespace App\Console;
use App\Console\CustomStyle;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class GreetCommand extends Command
{
// ...
protected function execute(InputInterface $input, OutputInterface $output): int
{
// Before
$io = new SymfonyStyle($input, $output);
// After
$io = new CustomStyle($input, $output);
// ...
}
}
写入错误输出
如果您重用命令的输出作为其他命令的输入,或将其转储到文件中以供以后重用,您可能希望排除进度条、注释和其他不提供实际价值的输出。
命令可以在两个不同的流中输出信息:stdout
(标准输出)是应该输出实际内容的流,而 stderr
(标准错误)是应该输出错误和调试消息的流。
SymfonyStyle 类提供了一个名为 getErrorStyle() 的便捷方法,用于在两个流之间切换。此方法返回一个新的 SymfonyStyle
实例,该实例使用错误输出
1 2 3 4 5 6 7
$io = new SymfonyStyle($input, $output);
// Write to the standard output
$io->write('Reusable information');
// Write to the error output
$io->getErrorStyle()->warning('Debugging information or errors');
注意
如果您使用不是 ConsoleOutputInterface 实例的 OutputInterface
对象创建 SymfonyStyle
实例,则 getErrorStyle()
方法将不起作用,并且返回的对象仍将写入标准输出而不是错误输出。