Laravel Artisan Custom Styles: Custom Progress Bar Styles

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:

Console Progress Bar | The 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%
10
11 %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[▓░░░░]
9
10# Bar width: <= 1
11[░]

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 can
15 // view a count that does not start at 0. Step zero
16 // would now display as "The current step is 1".
17 // Developers know that the count starts at
18 // zero, but not all end users have the
19 // 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 10
10[▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓]
11Another test message 20

#Overriding Placeholders and Formats

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 void
15 */
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.

#Styling Text Within Progress Bars

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:

Styling progress bar text

#Using Icons in Progress Bar Text

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):

An example of icons in progress output

#Different Styles on Different Systems

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.

Some absolutely amazing
people

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.