December 7, 2016 —John Koster
There are extensive customizations that can be made to console progress bars. The progress bars that are displayed in Laravel commands are an instance of Symfony\Component\Console\Helper\ProgressBar
class. Progress bars also inherit their styles from the Symfony's console styles. The default styles are different depending on whether or not the command is running on a Windows or Unix-like systems. The following diagram demonstrates the subtle differences between the default Windows and Unlike-like system styles as well as the basic components of a console progress bar:
The progress bar at the top of the diagram is the default style as it appears on a Windows system. The progress bar on the bottom of the diagram is the default style as it appears on a Unix-like system. The basic components of the progress bar is the bar character, the progress character and the empty bar character. By default the progress character is set to an empty string on Unlike-like systems and does not appear in the above diagram. The diagram also shows other components of the progress bar.
Each shaded region with a dashed border contains an additional component of the progress bar. From left to right the components are the current step number (light orange block), the maximum number of steps (light pink block), the progress bar (light green block) and the current progress percentage (light blue block). The order of these components is determined by a progress bar format; it is using these formats that most common progress bar modifications are made.
Progress bar formats are a simple string that contain placeholders and extra characters used to control the appearance and placement of items in a progress bar. The following placeholders can be used when constructing a console progress bar:
Placeholder | Description |
---|---|
current |
The current progress step. |
max |
The maximum number of steps to complete. This value is set to 0 if the total number of steps is indeterminate. |
bar |
The progress bar. |
percent |
The percentage of task completion. This is not available if the total number of steps is indeterminate. |
elapsed |
The time elapsed since the first step was started. |
remaining |
The remaining time to complete the task. This is not available if the total number of steps is indeterminate. |
estimated |
The estimated time to complete the task. This is not available if the total number of steps is indeterminate. |
memory |
The current script memory usage. |
message |
The current message set for the progress bar. This is set using the setMessage method. Other messages can be set with their own names. |
All of the progress bar placeholders are surrounded by the %
character. The following example demonstrates how to set a progress bar format:
1<?php 2 3// ... 4 5public function handle() 6{ 7 // Create the progress bar. 8 $progressBar = $this->output->createProgressBar(10); 9 10 // Set the custom progress bar format.11 $progressBar->setFormat('%bar%');12 13 // ...14}15 16// ...
The following examples shows various different formats and their results. Each of the examples will assume that the total number of steps is 10
, the current step number is 3
and that the operating environment is a Windows machine using the default styling. The format will appear on a line above the end result.
1 2%bar% 3========>------------------- 4 5%current&/%max% [%bar%] 6 3/10 [========>-------------------] 7 8%current%/%max% [%bar%] %percent:3s%% 9 3/10 [========>-------------------] 30%1011 %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%12 3/10 [========>-------------------] 30% 19 secs/2 mins
Each of the placeholders are surrounded by the %
character; any other characters that appear in the format string will appear in the final rendered progress bar. In some of the previous examples the square brackets ([
and ]
) will appear in the console window. The same is true for any %
character that is not part of a progress bar placeholder. Some of the placeholders have been entered with the placeholder name being separated from an additional formatting string by a semicolon (:
).
When using the $placeholder:format%
syntax the specified format
will be used in an internal call to PHP's sprintf
function. This allows for precise control over the formatting and alignment of the rendered progress bar.
In addition to using the setFormat
method there are many additional methods available to customize the way that progress bars are displayed. These additional methods usually control some aspect of a progress bar's format that cannot be set using the format string alone.
setBarCharacter($char)
The setBarCharacter
method is used to change the which character is used to represent which steps have already been completed. On Windows systems this character is =
by default. The following examples demonstrate how to change the bar character.
1<?php 2 3// ... 4 5function handle() 6{ 7 $progressBar = $this->output->createProgressBar(10); 8 $progressBar->setFormat('[%bar%]'); 9 $progressBar->setBarCharacter('*');10 $progressBar->advance(3);11}12 13// ...
The getBarCharacter
method can be used to retrieve the current bar character on any given progress bar instance. This method is used internally by the progress bar when rendering itself to the screen. If the bar character has been set to null
and the progress bar contains a determinate number of steps the method will return the =
character; if the character has been set to null
and the progress bar contains an indeterminate number of steps the method will return the currently configured empty bar character.
The above example would output something similar to the following output on Unix-like systems (the output would be similar on Windows, but the progress and empty bar characters are different by default):
1[********░░░░░░░░░░░░░░░░░░░░]
setEmptyBarCharacter($char)
The setEmptyBarCharacter
method can be used to change the character that is used to represent steps on the progress bar that have not yet been completed. On Windows systems this character is set to -
by default and on Unix-like systems this character is set to ░
. The method defines only one parameter $char
, which is the character that should be used as the empty bar character.
The getEmptyBarCharacter
method can be used to retrieve the current empty bar character for a given progress bar instance.
1<?php 2 3// ... 4 5function handle() 6{ 7 $progressBar = $this->output->createProgressBar(10); 8 $progressBar->setFormat('[%bar%]'); 9 $progressBar->setEmptyBarCharacter('#');10 $progressBar->advance(3);11}12 13// ...
The above example would output something similar to the following output on Unix-like systems (the output would be similar on Windows, but the progress and empty bar characters are different by default):
1[********####################]
setProgressCharacter($char)
The setProgressCharacter
method is used to customize the character that separates the bar and empty bar characters. The method defines only one parameter—the character to use as the progress character. On Windows systems this character is set to >
by default and on Unix-like systems the character is set to to an empty string.
The getProgressCharacter
method can be used to retrieve the current progress character on any given progress bar instance.
1<?php 2 3// ... 4 5function handle() 6{ 7 $progressBar = $this->output->createProgressBar(10); 8 $progressBar->setFormat('[%bar%]'); 9 $progressBar->setProgressCharacter('>');10 $progressBar->advance(3);11}12 13// ...
A command similar to the would output something similar to the following output on Unix-based systems (the default bar and empty bar characters are different on Windows systems by default). Notice that the progress character has been changed to the >
character; this is different than the empty string that is used by default on Unix-like systems.
1[▓▓▓▓▓▓▓▓>░░░░░░░░░░░░░░░░░░░]
setBarWidth($size)
The setBarWidth
method is used to set the width of the progress bar. The width be used to set the width of the actual progress bar itself (using the %bar%
placeholder) and not any other information supplied by other placeholders. The supplied $size
is less than 1
, a bar size of 1
will be used.
If the bar width is less than the total items in the progress bar, the progress bar might not appear to be complete until the final progress step has been completed or the finish
method has been called.
The following example demonstrates the effect of the setBarWidth
method. All of the progress bars have been rendered using the [%bar%]
format with ten total progress items. The current step of each progress bar is 3
.
1# Bar width: 20 2[▓▓▓▓▓▓░░░░░░░░░░░░░░] 3 4# Bar width: 10 5[▓▓▓░░░░░░░] 6 7# Bar width: 5 8[▓░░░░] 910# Bar width: <= 111[░]
As you can see the progress bar component will adjust the number of bar characters it needs to use when rendering the final progress bar. In the example where the bar width was set to 20
, the total number of bar characters used was 6
.
setMessage($message, $name = 'message')
The setMessage
method is used to associate a given message (via an argument to the $message
parameter) with a named placeholder (via an argument to the $name
parameter); these messages can be used in a progress bar format. By default the $name
parameter is set to message
. The following code example demonstrates the usage of the setMessage
method:
1<?php 2 3// ... 4 5function handle() 6{ 7 $progressBar = $this->output->createProgressBar(10); 8 $progressBar->setFormat( 9 "The current step is %current_step%10 \n[%bar%]\nAnother test message %test%"11 );12 13 for ($i = 0; $i < 10; $i++) {14 // We are adding one to the count so that the user can15 // view a count that does not start at 0. Step zero16 // would now display as "The current step is 1".17 // Developers know that the count starts at18 // zero, but not all end users have the19 // same level of technical knowledge.20 $progressBar->setMessage($i + 1, 'current_step');21 $progressBar->setMessage(($i + 1) * 2, 'test');22 $progressBar->advance();23 }24 25}26 27// ...
When a command similar to the previous example executes the user would see something similar to the following output in their console window. Multiple steps of the progress bar have been shown:
1The current step is 2 2[▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░] 3Another test message 4 4 5The current step is 5 6[▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░] 7Another test message 10 8 9The current step is 1010[▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓]11Another test message 20
You can customize or override format definitions and placeholders by using the setFormatDefinition($name, $format)
and setPlaceholderFormatterDefinition($name, callable $callable)
methods, respectively. These methods can be useful when dealing you are required to work with the same formats and placeholders on many different progress bar instances, possibly across multiple commands. These methods are static, meaning they do not have to be called on an actual instance of Symfony\Component\Console\Helper\ProgressBar
. Also, because of the nature of these methods, they are best suited to be utilized in code that will be called when your console application is instantiated, such as a service provider.
The following command will create a new service provider named ConsoleStylesProvider
. The custom placeholders and formats will be created within this service provider.
1php artisan make:provider ConsoleStylesProvider
Don't forget to update the providers
key in the config/app.php
configuration file by adding an App\Providers\ConsoleStylesProvider::class
entry.
Because our service provider will not actually be registering anything the service container we do not need to make use of the provider's boot
method. Additionally, we need to import (using the use
statement) Symfony's ProgressBar
helper class. At this point the service provider looks like this:
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Support\ServiceProvider; 6use Symfony\Component\Console\Helper\ProgressBar; 7 8class ConsoleStylesProvider extends ServiceProvider 9{10 11 /**12 * Register the application services.13 *14 * @return void15 */16 public function register()17 {18 // ...19 }20 21}
The following example will register a new format definition with the progress bar helper. To register or override a format definition we need to supply the name (via an argument to the $name
parameter) of the format and the actual format string (via an argument to the $format
parameter). The $format
string is the typical progress bar format string that was discussed in earlier sections.
1<?php 2 3// ... 4 5public function register() 6{ 7 // Set a new progress bar format definition. 8 ProgressBar::setFormatDefinition( 9 'custom_format',10 'Custom Format: Progress %percent%'11 );12}13 14// ...
Now, when we are creating our progress bars within commands we can utilize the custom_format
like so:
1<?php 2 3// ... 4 5public function handle() 6{ 7 // Create a progress bar instance. 8 $progressBar = new $this->output->createProgressBar(10); 9 10 // Utilize the custom format.11 $progressBar->setFormat('custom_format');12 13 // ...14}15 16// ...
A command using the progress bar in the previous example would display output similar to the following on the third step of the progress bar:
1Custom Format: Progress 30
You can retrieve the format string for any given named format by using the getFormatDefinition($name)
method. If the format string does not exist, the method will return null
.
The setPlaceholderFormatterDefinition
method can be used to add new or override existing placeholders (remember the placeholders are, by default, strings enclosed in the %
symbol). Like the setFormatDefinition
method, this method is also static and best added to initialization code such as the service provider we created earlier. The following example will continue to use the service provider created in the previous step. To set the contents of a format placeholder we need to supply the name for the placeholder as well as a callable
instance, such as a Closure. The following examples will all use a Closure for the callable $callable
parameter.
The functions that are written to be supplied as an argument for the $callable
parameter should accept an instance of Symfony\Component\Console\Helper\ProgressBar
as their only argument. The function should return a string that will be used as the replacement inside the progress bar's format string.
1<?php 2 3// ... 4 5public function register() 6{ 7 // Set a new progress bar placeholder definition. 8 ProgressBar::setPlaceholderFormatterDefinition( 9 'custom_placeholder',10 function(ProgressBar $bar) {11 return 'Custom Placeholder Content';12 }13 );14}15 16// ...
After the custom_placeholder
placeholder definition has been registered, we can use it in our progress bar format strings like so (keep in mind that these custom placeholders can also be used when defining new format definitions using the setFormatDefinition
method):
1<?php 2 3// ... 4 5public function handle() 6{ 7 // Create a progress bar instance. 8 $progressBar = new $this->output->createProgressBar(10); 9 10 // Utilize the custom placeholder formatter definition.11 $progressBar->setFormat('%custom_placeholder%');12 13 // ...14}15 16// ...
A command using the progress bar in the previous example would display output similar to the following:
1Custom Placeholder Content
The getPlaceholderFormatterDefinition($name)
method can be used to retrieve the callable
instance that is registered to the placeholder formatter definition with the given $name
. If a placeholder formatter definition does not exist, the method will return null
.
The previous example, while complete, is not very useful. The following placeholder formatter definition is more interesting and will return the time that the progress bar started:
1<?php 2 3// ... 4 5public function register() 6{ 7 // Set a new progress bar placeholder definition. 8 ProgressBar::setPlaceholderFormatterDefinition( 9 'started',10 function(ProgressBar $bar) {11 // Use the `date` function to format the start time date.12 return date('H\hi m/d/Y', $bar->getStartTime());13 }14 );15}16 17// ...
Progress bars using a format like %started% [%bar%]
might display output similar to the following:
117h59 07/12/2016 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░]
The Symfony documentation contains more useful information related to the setPlaceholderFormatterDefinition
and setFormatDefinition
methods, as well as utilizing progress bars in general. To learn more about these methods visit the URL: http://symfony.com/doc/current/components/console/helpers/progressbar.html.
Progress bar format strings can utilize the color formatting syntax and strings to have various parts of the progress bar rendered with different styling. The process is similar to how custom styles are used when printing lines of text or rendering tables. The following example will create a new progress bar with a custom format to change the colors of various parts of the progress bar. The output (as viewed on a Windows system) follows the example.
1<?php 2 3// ... 4 5public function handle() 6{ 7 // Create a progress bar instance. 8 $progressBar = new $this->output->createProgressBar(10); 9 10 // Set a new, colored, format.11 $progressBar->setFormat('[<fg=magenta>%bar%</>] <info>%elapsed%</info>');12 13 // ...14}15 16// ...
After a command similar to the previous example has executed the user would see output similar to the following on Windows systems that support colored console output:
To add a little extra touch to your progress bars (or console output in general), we can add embed Unicode symbols and pictographs into our console output. This is done by using double quotes and supplying the UTF-8 hex encoding of the character to use. For example, to set a progress bar's progress character to the cyclone symbol (U+1F300) we would need to use the following:
1<?php 2 3// ... 4function handle() 5{ 6 $this->line("Cookie: \xF0\x9F\x8D\xAA"); 7 $progressBar = $this->output->createProgressBar(10); 8 9 // Set the progress character to the cyclone character.10 $progressBar->setProgressCharacter("\xf0\x9f\x8c\x80");11}12 13// ...
On supported systems the user would see something similar to the following output (the example image was rendered on macOs El Capitan):
When developing commands that are required to be used on multiple systems it can be beneficial to your users if your commands respond to the operating environment. When it comes to how console commands interact with terminals it is only really necessary to be concerned with Windows or Unix-like systems. Internally, Symfony checks the DIRECTORY_SEPARATOR
constant each time it needs to determine if the command is running on a Windows environment, like so:
1<?php 2 3// ... 4 5function handle() 6{ 7 if (DIRECTORY_SEPARATOR === '\\') { 8 // Windows system. 9 } else {10 // Unix-like systems.11 }12}13 14// ...
However, since we are developing commands in the context of a Laravel console application, we can make use of the windows_os
helper function to improve readability:
1<?php 2 3// ... 4 5function handle() 6{ 7 if (windows_os()) { 8 // Windows system. 9 } else {10 // Unix-like systems.11 }12}13 14// ...
A useful way to use this technique is when defining progress bar formats, placeholder or when customizing the characters used by the progress bar. The following example demonstrates how to use it to customize the progress bar characters:
1<?php 2 3// ... 4 5function handle() 6{ 7 $progressBar = $this->output->createProgressBar(10); 8 9 if (windows_os()) {10 // Set the progress character to '$' only on Windows system.11 $progressBar->setProgressCharacter('$');12 }13}14 15// ...
The previous example was fairly trivial but can be expanded on to create interesting environment-aware console commands.
∎
The following amazing people help support this site and my open source projects ♥️
If you're interesting in supporting my work and want to show up on this list, check out my GitHub Sponsors Profile.