进度条
当执行耗时较长的命令时,显示进度信息可能会有所帮助,进度信息会随着命令的运行而更新。

注意
作为替代方案,请考虑使用 SymfonyStyle 来显示进度条。
要显示进度详情,请使用 ProgressBar,传递总单元数,并在命令执行时推进进度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
use Symfony\Component\Console\Helper\ProgressBar;
// creates a new progress bar (50 units)
$progressBar = new ProgressBar($output, 50);
// starts and displays the progress bar
$progressBar->start();
$i = 0;
while ($i++ < 50) {
// ... do some work
// advances the progress bar 1 unit
$progressBar->advance();
// you can also advance the progress bar by more than 1 unit
// $progressBar->advance(3);
}
// ensures that the progress bar is at 100%
$progressBar->finish();
提示
您也可以通过使用负值调用 $progress->advance()
来回退进度条(即向后退步)。例如,如果您调用 $progress->advance(-2)
,则进度条将回退 2 步。
注意
默认情况下,进度条助手使用错误输出 (stderr
) 作为其默认输出。可以通过将 StreamOutput 的实例传递给 ProgressBar 构造函数来更改此行为。
除了通过步数推进进度条(使用 advance() 方法),您还可以通过调用 setProgress() 方法来设置当前进度。
如果您要恢复长时间运行的任务,最好从某个特定点开始绘制进度条。使用 start()
的第二个可选参数来设置起始点。
1 2 3 4 5 6 7
use Symfony\Component\Console\Helper\ProgressBar;
// creates a new progress bar (100 units)
$progressBar = new ProgressBar($output, 100);
// displays the progress bar starting at 25 completed units
$progressBar->start(null, 25);
提示
如果您的平台不支持 ANSI 代码,则进度条的更新将作为新行添加。为防止输出被淹没,请使用 minSecondsBetweenRedraws() 方法来限制重绘次数,并使用 setRedrawFrequency() 方法来每 N 次迭代重绘一次。默认情况下,重绘频率为 100 毫秒或 max
值的 10%。
如果您事先不知道确切的步数,请将其设置为合理的值,然后调用 setMaxSteps()
方法根据需要进行更新。
1 2 3 4 5
// start with a 50 units progressbar
$progressBar = new ProgressBar($output, 50);
// a complex task has just been created: increase the progressbar to 200 units
$progressBar->setMaxSteps(200);
另一种解决方案是在创建 ProgressBar 实例时省略 steps 参数。
1
$progressBar = new ProgressBar($output);
然后,进度将显示为指示器。
1 2 3 4 5 6 7 8 9
# no max steps (displays it like a throbber)
0 [>---------------------------]
5 [----->----------------------]
5 [============================]
# max steps defined
0/3 [>---------------------------] 0%
1/3 [=========>------------------] 33%
3/3 [============================] 100%
提示
另一种替代方案是使用进度指示器而不是进度条。
无论何时完成任务,都不要忘记调用 finish() 以确保进度条显示刷新为 100% 完成。
如果进度信息存储在可迭代变量中(例如数组或 PHP 生成器),则可以使用 iterate() 方法,该方法会自动启动、推进和完成进度条。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use Symfony\Component\Console\Helper\ProgressBar;
$progressBar = new ProgressBar($output);
// $iterable can be array
$iterable = [1, 2];
foreach ($progressBar->iterate($iterable) as $value) {
// ... do some work
}
// or a generator
function iterable() { yield 1; yield 2; ... };
foreach ($progressBar->iterate(iterable()) as $value) {
// ... do some work
}
前面的代码将输出:
1 2 3
0/2 [>---------------------------] 0%
1/2 [==============>-------------] 50%
2/2 [============================] 100%
自定义进度条
内置格式
默认情况下,进度条上呈现的信息取决于 OutputInterface
实例的当前详细级别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# OutputInterface::VERBOSITY_NORMAL (CLI with no verbosity flag)
0/3 [>---------------------------] 0%
1/3 [=========>------------------] 33%
3/3 [============================] 100%
# OutputInterface::VERBOSITY_VERBOSE (-v)
0/3 [>---------------------------] 0% 1 sec
1/3 [=========>------------------] 33% 1 sec
3/3 [============================] 100% 1 sec
# OutputInterface::VERBOSITY_VERY_VERBOSE (-vv)
0/3 [>---------------------------] 0% 1 sec/1 sec
1/3 [=========>------------------] 33% 1 sec/1 sec
3/3 [============================] 100% 1 sec/1 sec
# OutputInterface::VERBOSITY_DEBUG (-vvv)
0/3 [>---------------------------] 0% 1 sec/1 sec 1.0 MB
1/3 [=========>------------------] 33% 1 sec/1 sec 1.0 MB
3/3 [============================] 100% 1 sec/1 sec 1.0 MB
注意
如果您使用 quiet 标志 (-q
) 调用命令,则不会显示进度条。
除了依赖当前命令的详细模式外,您还可以通过 setFormat()
强制使用格式。
1
$progressBar->setFormat('verbose');
内置格式如下:
normal
verbose
very_verbose
debug
如果您没有为进度条设置步数,请使用 _nomax
变体:
normal_nomax
verbose_nomax
very_verbose_nomax
debug_nomax
自定义格式
除了使用内置格式外,您还可以设置自己的格式。
1
$progressBar->setFormat('%bar%');
这将格式设置为仅显示进度条本身。
1 2 3
>---------------------------
=========>------------------
============================
进度条格式是一个字符串,其中包含特定的占位符(用 %
字符括起来的名称);占位符根据进度条的当前进度进行替换。以下是内置占位符的列表:
current
:当前步骤;max
:最大步数(如果未定义最大值,则为 0);bar
:进度条本身;percent
:完成百分比(如果未定义最大值,则不可用);elapsed
:自进度条开始以来经过的时间;remaining
:完成任务的剩余时间(如果未定义最大值,则不可用);estimated
:完成任务的估计时间(如果未定义最大值,则不可用);memory
:当前内存使用量;message
:用于在进度条中显示任意消息(稍后说明)。
时间字段 elapsed
、remaining
和 estimated
的精度为 2。这意味着 172799
秒显示为 1 天 23 小时
,而不是 1 天 23 小时 59 分 59 秒
。
例如,以下是如何将格式设置为与 debug
格式相同:
1
$progressBar->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:16s%/%estimated:-16s% %memory:6s%');
注意到添加到某些占位符的 :6s
部分了吗?这就是您可以调整进度条外观(格式和对齐方式)的方式。冒号 (:
) 后的部分用于设置字符串的 sprintf
格式。
除了为进度条的给定实例设置格式外,您还可以定义全局格式。
1 2 3 4
ProgressBar::setFormatDefinition('minimal', 'Progress: %percent%%');
$progressBar = new ProgressBar($output, 3);
$progressBar->setFormat('minimal');
此代码定义了一个新的 minimal
格式,然后您可以将其用于进度条。
1 2 3
Progress: 0%
Progress: 33%
Progress: 100%
提示
几乎总是最好重新定义内置格式,而不是创建新格式,因为这样可以使显示根据命令的详细程度标志自动变化。
当定义包含仅在已知最大步数时才可用的占位符的新样式时,您应该创建一个 _nomax
变体。
1 2 3 4 5
ProgressBar::setFormatDefinition('minimal', '%percent%% %remaining%');
ProgressBar::setFormatDefinition('minimal_nomax', '%percent%%');
$progressBar = new ProgressBar($output);
$progressBar->setFormat('minimal');
显示进度条时,如果进度条没有最大步数(如上面的示例所示),格式将自动设置为 minimal_nomax
。
提示
格式可以包含任何有效的 ANSI 代码,也可以使用 Symfony 特定的方式来设置颜色。
1 2 3 4
ProgressBar::setFormatDefinition(
'minimal',
'<info>%percent%</info>\033[32m%\033[0m <fg=white;bg=blue>%remaining%</>'
);
注意
格式可以跨越多行;当您想要在进度条旁边显示更多上下文信息时,这非常有用(请参阅本文开头的示例)。
进度条设置
在占位符中,bar
有点特殊,因为用于显示它的所有字符都可以自定义。
1 2 3 4 5 6 7 8 9 10 11
// the finished part of the bar
$progressBar->setBarCharacter('<comment>=</comment>');
// the unfinished part of the bar
$progressBar->setEmptyBarCharacter(' ');
// the progress character
$progressBar->setProgressCharacter('|');
// the bar width
$progressBar->setBarWidth(50);
警告
出于性能原因,Symfony 每 100 毫秒重绘一次屏幕。如果对于您的应用程序来说速度太快或太慢,请使用方法 minSecondsBetweenRedraws() 和 maxSecondsBetweenRedraws()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
$progressBar = new ProgressBar($output, 50000);
$progressBar->start();
// this redraws the screen every 100 iterations, but sets additional limits:
// don't redraw slower than 200ms (0.2) or faster than 100ms (0.1)
$progressBar->setRedrawFrequency(100);
$progressBar->maxSecondsBetweenRedraws(0.2);
$progressBar->minSecondsBetweenRedraws(0.1);
$i = 0;
while ($i++ < 50000) {
// ... do some work
$progressBar->advance();
}
自定义占位符
如果您想显示一些依赖于进度条显示的信息,而这些信息在内置占位符列表中不可用,您可以创建自己的占位符。让我们看看如何创建一个 remaining_steps
占位符来显示剩余步数。
1 2 3 4 5 6 7
// This definition is globally registered for all ProgressBar instances
ProgressBar::setPlaceholderFormatterDefinition(
'remaining_steps',
function (ProgressBar $progressBar, OutputInterface $output): int {
return $progressBar->getMaxSteps() - $progressBar->getProgress();
}
);
也可以使用 setPlaceholderFormatter
方法为每个 ProgressBar 实例设置占位符格式化程序。
1 2 3 4 5
$progressBar = new ProgressBar($output, 3, 0);
$progressBar->setFormat('%countdown% [%bar%]');
$progressBar->setPlaceholderFormatter('countdown', function (ProgressBar $progressBar) {
return $progressBar->getMaxSteps() - $progressBar->getProgress();
});
自定义消息
进度条定义了一个名为 message
的占位符来显示任意消息。但是,没有内置格式包含该占位符,因此在显示这些消息之前,您必须定义自己的自定义格式。
1 2 3 4
ProgressBar::setFormatDefinition('custom', ' %current%/%max% -- %message%');
$progressBar = new ProgressBar($output, 100);
$progressBar->setFormat('custom');
现在,在显示进度条之前,使用 setMessage()
方法设置 %message%
占位符的值。
1 2 3 4 5 6 7 8
// ...
$progressBar->setMessage('Start');
$progressBar->start();
// 0/100 -- Start
$progressBar->setMessage('Task is in progress...');
$progressBar->advance();
// 1/100 -- Task is in progress...
消息也可以与自定义占位符组合使用。在此示例中,进度条使用 %message%
和 %filename%
占位符。
1 2 3 4
ProgressBar::setFormatDefinition('custom', ' %current%/%max% -- %message% (%filename%)');
$progressBar = new ProgressBar($output, 100);
$progressBar->setFormat('custom');
setMessage()
方法接受第二个可选参数,用于设置自定义占位符的值。
1 2 3 4 5 6 7 8
// ...
// $files = ['client-001/invoices.xml', '...'];
foreach ($files as $filename) {
$progressBar->setMessage('Importing invoices...');
$progressBar->setMessage($filename, 'filename');
$progressBar->advance();
// 2/100 -- Importing invoices... (client-001/invoices.xml)
}
显示多个进度条
当使用控制台输出节时,可以同时显示多个进度条并独立更改它们的进度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
$section1 = $output->section();
$section2 = $output->section();
$progress1 = new ProgressBar($section1);
$progress2 = new ProgressBar($section2);
$progress1->start(100);
$progress2->start(100);
$i = 0;
while (++$i < 100) {
$progress1->advance();
if ($i % 2 === 0) {
$progress2->advance(4);
}
usleep(50000);
}
经过几次迭代后,终端中的输出将如下所示:
1 2
34/100 [=========>------------------] 34%
68/100 [===================>--------] 68%