Modern PHP, soft skills, productivity and time management.

How to make cool progressbar in Symfony command?

If you’re writing commands in Symfony then you probably know the ProgressBar component. It’s useful tool showing the current state of operations and, more importantly, ETA and used memory. Cool.

If you’ll to the official ProgressBar component page then at the beginning you’ll see a very cool progress bar, with colors, icons, changing status messages, but… If you’ll look a little further you’ll see a sad, black and white progress bar, that is far from a “promise” made at the beginning 🙁

Heads up! We’ll make it like this 🙂

Preparations…

First of all, create a simple Symfony Command.

class ProgressBarCommand extends ContainerAwareCommand
{
    protected function configure()
    {
        $this
            ->setName('app:progress_bar_command');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {

    }
}

Then create a simple code to show a progress bar, I write simple `for` loop with 1000 iterations:

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $progressBar = new ProgressBar($output, 1000);
        $progressBar->start();

        for ($i = 0; $i < 1000; $i++) {
            $progressBar->advance();
            usleep(1000); // sleep a little bit
        }

        $progressBar->finish();
    }

Now if you call the command you’ll see a very basic progress bar.

But if you add a verbose option and set it to `debug` level then the progress bar will show some extra information, like ETA or memory usage:

I use ProgressBar in that mode most of the time, it’s pretty useful. But often (well, always!) I want not only information how many items were handled but which element is handled right now! Thankfully ProgressBar gives a way to messing around the format of the information and even adding custom ones.

Add some info…

Let’s add additional info about a currently handled item:

$progressBar->setFormat(sprintf('%s item: <info>%%item%%</info>', 
$progressBar->getFormatDefinition('debug'))); // the new format
$progressBar->start();

for ($i = 0; $i < 1000; $i++) {
    $progressBar->setMessage($i, 'item'); // set the `item` value
    $progressBar->advance();
    usleep(1000);
}

The most important part is setting up the format and then filling the additional information with some values. I use the original format in `debug` mode and add some parameters (`%%item%%`). And then, before advancing the ProgressBar I set `item` value to the current iteration. In this example, it’s not very useful, but when you’ll process some data with own ids you’ll find it damn useful.

Ok, so our ProgressBar has now every required part to make it look cool 🙂 For me, it took about 30 minutes to make it looks like in the documentation, so not too much 🙂 And if you know the basics then any further modification is very easy.

Make it cool ?

Let’s start with characters in the bar itself. We can set it using some setters from ProgressBar:

$progressBar->setBarCharacter('<fg=green>⚬</>');
$progressBar->setEmptyBarCharacter("<fg=red>⚬</>");
$progressBar->setProgressCharacter("<fg=green>➤</>");

As you can see I added some colors, <fg=green></> tag sets the font color of the value to green. You can find more about coloring the output here.

Then we have to split the output into 3 lines: status, progress bar and information like ETA and memory usage (yup, I changed `item` parameter to `status`):

$progressBar->setFormat(
    "%status%\n%current%/%max% [%bar%] %percent:3s%%\n  %estimated:-6s%  %memory:6s%"
);

Now it looks a little bit closer to the original:

At last, we have to do some more formatting, coloring, etc:
Format:

        
$progressBar->setFormat(
    "<fg=white;bg=cyan> %status:-45s%</>\n%current%/%max% [%bar%] %percent:3s%%\n?  %estimated:-20s%  %memory:20s%"
);

Some explanations:

  • <fg=white;bg=cyan</> sets the status colors to white font and cyan background
  • %status:-45s% sets the status value to 45 characters long and align it left

Current status:

if ($i < 300) {
    $progressBar->setMessage("Starting...", 'status');
} elseif ($i < 700) {
    $progressBar->setMessage("All right :)", 'status');
} else {
    $progressBar->setMessage("Almost there...", 'status');
}

Done ?

And now it looks almost exactly like in the documentation 🙂 The only thing I don’t know how to do is coloring the memory information depending on the usage. But it’s neat thou and I have much fun playing with the ProgressBar output.

Do you want to see the whole example? Come visit my repo on GitHub!

4 Comments

  1. Petru

    Thank you. Awesome article.

  2. aso

    Nice one, never thought that so fancy progress bar is possible, thanks

    • krzych

      Your welcome 🙂 This is possibly an overkill, but using `format` to print some useful info (like currently processed `id` for example) is a great feature. Also, you could change just `BarCharacter` to, for example, beer to create… PROGRESSBEER 🙂

  3. rob

    Try this nice styling:

    $progressBar->setBarCharacter(‘█’);
    $progressBar->setEmptyBarCharacter(“░”);
    $progressBar->setProgressCharacter(“▓”);

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

© 2024 Krzych Jończyk

Theme by Anders NorenUp ↑