@@ -32,6 +32,10 @@ final class ProgressBar
32
32
private $ format ;
33
33
private $ internalFormat ;
34
34
private $ redrawFreq = 1 ;
35
+ private $ writeCount ;
36
+ private $ lastWriteTime ;
37
+ private $ minSecondsBetweenRedraws = 0 ;
38
+ private $ maxSecondsBetweenRedraws = 1 ;
35
39
private $ output ;
36
40
private $ step = 0 ;
37
41
private $ max ;
@@ -51,7 +55,7 @@ final class ProgressBar
51
55
* @param OutputInterface $output An OutputInterface instance
52
56
* @param int $max Maximum steps (0 if unknown)
53
57
*/
54
- public function __construct (OutputInterface $ output , int $ max = 0 )
58
+ public function __construct (OutputInterface $ output , int $ max = 0 , float $ minSecondsBetweenRedraws = 0 )
55
59
{
56
60
if ($ output instanceof ConsoleOutputInterface) {
57
61
$ output = $ output ->getErrorOutput ();
@@ -61,12 +65,17 @@ public function __construct(OutputInterface $output, int $max = 0)
61
65
$ this ->setMaxSteps ($ max );
62
66
$ this ->terminal = new Terminal ();
63
67
68
+ if (0 < $ minSecondsBetweenRedraws ) {
69
+ $ this ->setRedrawFrequency (null );
70
+ $ this ->minSecondsBetweenRedraws = $ minSecondsBetweenRedraws ;
71
+ }
72
+
64
73
if (!$ this ->output ->isDecorated ()) {
65
74
// disable overwrite when output does not support ANSI codes.
66
75
$ this ->overwrite = false ;
67
76
68
77
// set a reasonable redraw frequency so output isn't flooded
69
- $ this ->setRedrawFrequency ($ max / 10 );
78
+ $ this ->setRedrawFrequency (null );
70
79
}
71
80
72
81
$ this ->startTime = time ();
@@ -183,6 +192,11 @@ public function getProgressPercent(): float
183
192
return $ this ->percent ;
184
193
}
185
194
195
+ public function getBarOffset (): int
196
+ {
197
+ return floor ($ this ->max ? $ this ->percent * $ this ->barWidth : (null === $ this ->redrawFreq ? min (5 , $ this ->barWidth / 15 ) * $ this ->writeCount : $ this ->step ) % $ this ->barWidth );
198
+ }
199
+
186
200
public function setBarWidth (int $ size )
187
201
{
188
202
$ this ->barWidth = max (1 , $ size );
@@ -238,9 +252,19 @@ public function setFormat(string $format)
238
252
*
239
253
* @param int|float $freq The frequency in steps
240
254
*/
241
- public function setRedrawFrequency (int $ freq )
255
+ public function setRedrawFrequency (?int $ freq )
256
+ {
257
+ $ this ->redrawFreq = null !== $ freq ? max (1 , $ freq ) : null ;
258
+ }
259
+
260
+ public function preventRedrawFasterThan (float $ intervalInSeconds ): void
261
+ {
262
+ $ this ->minSecondsBetweenRedraws = $ intervalInSeconds ;
263
+ }
264
+
265
+ public function forceRedrawSlowerThan (float $ intervalInSeconds ): void
242
266
{
243
- $ this ->redrawFreq = max ( $ freq , 1 ) ;
267
+ $ this ->maxSecondsBetweenRedraws = $ intervalInSeconds ;
244
268
}
245
269
246
270
/**
@@ -305,11 +329,27 @@ public function setProgress(int $step)
305
329
$ step = 0 ;
306
330
}
307
331
308
- $ prevPeriod = (int ) ($ this ->step / $ this ->redrawFreq );
309
- $ currPeriod = (int ) ($ step / $ this ->redrawFreq );
332
+ $ redrawFreq = $ this ->redrawFreq ?? (($ this ->max ?: 10 ) / 10 );
333
+ $ prevPeriod = (int ) ($ this ->step / $ redrawFreq );
334
+ $ currPeriod = (int ) ($ step / $ redrawFreq );
310
335
$ this ->step = $ step ;
311
336
$ this ->percent = $ this ->max ? (float ) $ this ->step / $ this ->max : 0 ;
312
- if ($ prevPeriod !== $ currPeriod || $ this ->max === $ step ) {
337
+ $ timeInterval = microtime (true ) - $ this ->lastWriteTime ;
338
+
339
+ // Draw regardless of other limits
340
+ if ($ this ->max === $ step ) {
341
+ $ this ->display ();
342
+
343
+ return ;
344
+ }
345
+
346
+ // Throttling
347
+ if ($ timeInterval < $ this ->minSecondsBetweenRedraws ) {
348
+ return ;
349
+ }
350
+
351
+ // Draw each step period, but not too late
352
+ if ($ prevPeriod !== $ currPeriod || $ timeInterval >= $ this ->maxSecondsBetweenRedraws ) {
313
353
$ this ->display ();
314
354
}
315
355
}
@@ -413,8 +453,10 @@ private function overwrite(string $message): void
413
453
}
414
454
415
455
$ this ->firstRun = false ;
456
+ $ this ->lastWriteTime = microtime (true );
416
457
417
458
$ this ->output ->write ($ message );
459
+ ++$ this ->writeCount ;
418
460
}
419
461
420
462
private function determineBestFormat (): string
@@ -436,7 +478,7 @@ private static function initPlaceholderFormatters(): array
436
478
{
437
479
return [
438
480
'bar ' => function (self $ bar , OutputInterface $ output ) {
439
- $ completeBars = floor ( $ bar ->getMaxSteps () > 0 ? $ bar -> getProgressPercent () * $ bar -> getBarWidth () : $ bar -> getProgress () % $ bar -> getBarWidth () );
481
+ $ completeBars = $ bar ->getBarOffset ( );
440
482
$ display = str_repeat ($ bar ->getBarCharacter (), $ completeBars );
441
483
if ($ completeBars < $ bar ->getBarWidth ()) {
442
484
$ emptyBars = $ bar ->getBarWidth () - $ completeBars - Helper::strlenWithoutDecoration ($ output ->getFormatter (), $ bar ->getProgressCharacter ());
0 commit comments