@@ -44,61 +44,96 @@ class ProgressBar
4444 private $ step ;
4545 private $ max ;
4646 private $ startTime ;
47+ private $ stepWidth ;
48+ private $ percent ;
4749 private $ lastMessagesLength ;
4850 private $ barCharOriginal ;
4951
52+ static private $ formatters ;
53+
5054 /**
51- * List of formatting variables
55+ * Constructor.
5256 *
53- * @var array
57+ * @param OutputInterface $output An OutputInterface instance
58+ * @param integer $max Maximum steps (0 if unknown)
5459 */
55- private $ defaultFormatVars = array (
56- 'current ' ,
57- 'max ' ,
58- 'bar ' ,
59- 'percent ' ,
60- 'elapsed ' ,
61- );
60+ public function __construct (OutputInterface $ output , $ max = 0 )
61+ {
62+ // Disabling output when it does not support ANSI codes as it would result in a broken display anyway.
63+ $ this ->output = $ output ->isDecorated () ? $ output : new NullOutput ();
64+ $ this ->max = (int ) $ max ;
65+ $ this ->stepWidth = $ this ->max > 0 ? Helper::strlen ($ this ->max ) : 4 ;
66+
67+ if (!self ::$ formatters ) {
68+ self ::$ formatters = self ::initPlaceholderFormatters ();
69+ }
70+ }
6271
6372 /**
64- * Available formatting variables
73+ * Sets a placeholder formatter for a given name.
74+ *
75+ * This method also allow you to override an existing placeholder.
6576 *
66- * @var array
77+ * @param string $name The placeholder name (including the delimiter char like %)
78+ * @param callable $callable A PHP callable
6779 */
68- private $ formatVars ;
80+ public static function setPlaceholderFormatter ($ name , $ callable )
81+ {
82+ if (!self ::$ formatters ) {
83+ self ::$ formatters = self ::initPlaceholderFormatters ();
84+ }
85+
86+ self ::$ formatters [$ name ] = $ callable ;
87+ }
6988
7089 /**
71- * Various time formats
90+ * Gets the progress bar start time.
7291 *
73- * @var array
92+ * @return int The progress bar start time
7493 */
75- private $ timeFormats = array (
76- array (0 , '??? ' ),
77- array (2 , '1 sec ' ),
78- array (59 , 'secs ' , 1 ),
79- array (60 , '1 min ' ),
80- array (3600 , 'mins ' , 60 ),
81- array (5400 , '1 hr ' ),
82- array (86400 , 'hrs ' , 3600 ),
83- array (129600 , '1 day ' ),
84- array (604800 , 'days ' , 86400 ),
85- );
94+ public function getStartTime ()
95+ {
96+ return $ this ->startTime ;
97+ }
8698
87- private $ stepWidth ;
88- private $ percent ;
99+ /**
100+ * Gets the progress bar maximal steps.
101+ *
102+ * @return int The progress bar max steps
103+ */
104+ public function getMaxSteps ()
105+ {
106+ return $ this ->max ;
107+ }
89108
90109 /**
91- * Constructor .
110+ * Gets the progress bar step .
92111 *
93- * @param OutputInterface $output An OutputInterface instance
94- * @param integer $max Maximum steps (0 if unknown)
112+ * @return int The progress bar step
95113 */
96- public function __construct ( OutputInterface $ output , $ max = 0 )
114+ public function getStep ( )
97115 {
98- // Disabling output when it does not support ANSI codes as it would result in a broken display anyway.
99- $ this ->output = $ output ->isDecorated () ? $ output : new NullOutput ();
100- $ this ->max = (int ) $ max ;
101- $ this ->stepWidth = $ this ->max > 0 ? Helper::strlen ($ this ->max ) : 4 ;
116+ return $ this ->step ;
117+ }
118+
119+ /**
120+ * Gets the progress bar step width.
121+ *
122+ * @return int The progress bar step width
123+ */
124+ public function getStepWidth ()
125+ {
126+ return $ this ->stepWidth ;
127+ }
128+
129+ /**
130+ * Gets the current progress bar percent.
131+ *
132+ * @return int The current progress bar percent
133+ */
134+ public function getProgressPercent ()
135+ {
136+ return $ this ->percent ;
102137 }
103138
104139 /**
@@ -111,6 +146,16 @@ public function setBarWidth($size)
111146 $ this ->barWidth = (int ) $ size ;
112147 }
113148
149+ /**
150+ * Gets the progress bar width.
151+ *
152+ * @return int The progress bar size
153+ */
154+ public function getBarWidth ()
155+ {
156+ return $ this ->barWidth ;
157+ }
158+
114159 /**
115160 * Sets the bar character.
116161 *
@@ -121,6 +166,16 @@ public function setBarCharacter($char)
121166 $ this ->barChar = $ char ;
122167 }
123168
169+ /**
170+ * Gets the bar character.
171+ *
172+ * @return string A character
173+ */
174+ public function getBarCharacter ()
175+ {
176+ return $ this ->barChar ;
177+ }
178+
124179 /**
125180 * Sets the empty bar character.
126181 *
@@ -131,6 +186,16 @@ public function setEmptyBarCharacter($char)
131186 $ this ->emptyBarChar = $ char ;
132187 }
133188
189+ /**
190+ * Gets the empty bar character.
191+ *
192+ * @return string A character
193+ */
194+ public function getEmptyBarCharacter ()
195+ {
196+ return $ this ->emptyBarChar ;
197+ }
198+
134199 /**
135200 * Sets the progress bar character.
136201 *
@@ -141,6 +206,16 @@ public function setProgressCharacter($char)
141206 $ this ->progressChar = $ char ;
142207 }
143208
209+ /**
210+ * Gets the progress bar character.
211+ *
212+ * @return string A character
213+ */
214+ public function getProgressCharacter ()
215+ {
216+ return $ this ->progressChar ;
217+ }
218+
144219 /**
145220 * Sets the progress bar format.
146221 *
@@ -176,13 +251,6 @@ public function start()
176251 $ this ->format = $ this ->determineBestFormat ();
177252 }
178253
179- $ this ->formatVars = array ();
180- foreach ($ this ->defaultFormatVars as $ var ) {
181- if (false !== strpos ($ this ->format , "% {$ var }% " )) {
182- $ this ->formatVars [$ var ] = true ;
183- }
184- }
185-
186254 if (!$ this ->max ) {
187255 $ this ->barCharOriginal = $ this ->barChar ;
188256 $ this ->barChar = $ this ->emptyBarChar ;
@@ -267,11 +335,11 @@ public function display()
267335 throw new \LogicException ('You must start the progress bar before calling display(). ' );
268336 }
269337
270- $ message = $ this -> format ;
271- foreach ( $ this -> generate () as $ name => $ value ) {
272- $ message = str_replace ( " % { $ name } % " , $ value , $ message );
273- }
274- $ this ->overwrite ( $ message );
338+ $ regex = implode ( ' | ' , array_keys ( self :: $ formatters )) ;
339+ $ self = $ this ;
340+ $ this -> overwrite ( preg_replace_callback ( " {( $ regex )} " , function ( $ matches ) use ( $ self ) {
341+ return call_user_func ( self :: $ formatters [ $ matches [ 1 ]], $ self );
342+ }, $ this ->format ) );
275343 }
276344
277345 /**
@@ -286,72 +354,6 @@ public function clear()
286354 $ this ->overwrite ('' );
287355 }
288356
289- /**
290- * Generates the array map of format variables to values.
291- *
292- * @return array Array of format vars and values
293- */
294- private function generate ()
295- {
296- $ vars = array ();
297-
298- if (isset ($ this ->formatVars ['bar ' ])) {
299- $ completeBars = floor ($ this ->max > 0 ? $ this ->percent * $ this ->barWidth : $ this ->step % $ this ->barWidth );
300- $ emptyBars = $ this ->barWidth - $ completeBars - Helper::strlen ($ this ->progressChar );
301- $ bar = str_repeat ($ this ->barChar , $ completeBars );
302- if ($ completeBars < $ this ->barWidth ) {
303- $ bar .= $ this ->progressChar ;
304- $ bar .= str_repeat ($ this ->emptyBarChar , $ emptyBars );
305- }
306-
307- $ vars ['bar ' ] = $ bar ;
308- }
309-
310- if (isset ($ this ->formatVars ['elapsed ' ])) {
311- $ elapsed = time () - $ this ->startTime ;
312- $ vars ['elapsed ' ] = str_pad ($ this ->humaneTime ($ elapsed ), 6 , ' ' , STR_PAD_LEFT );
313- }
314-
315- if (isset ($ this ->formatVars ['current ' ])) {
316- $ vars ['current ' ] = str_pad ($ this ->step , $ this ->stepWidth , ' ' , STR_PAD_LEFT );
317- }
318-
319- if (isset ($ this ->formatVars ['max ' ])) {
320- $ vars ['max ' ] = $ this ->max ;
321- }
322-
323- if (isset ($ this ->formatVars ['percent ' ])) {
324- $ vars ['percent ' ] = str_pad (floor ($ this ->percent * 100 ), 3 , ' ' , STR_PAD_LEFT );
325- }
326-
327- return $ vars ;
328- }
329-
330- /**
331- * Converts seconds into human-readable format.
332- *
333- * @param integer $secs Number of seconds
334- *
335- * @return string Time in readable format
336- */
337- private function humaneTime ($ secs )
338- {
339- $ text = '' ;
340- foreach ($ this ->timeFormats as $ format ) {
341- if ($ secs < $ format [0 ]) {
342- if (count ($ format ) == 2 ) {
343- $ text = $ format [1 ];
344- break ;
345- } else {
346- $ text = ceil ($ secs / $ format [2 ]).' ' .$ format [1 ];
347- break ;
348- }
349- }
350- }
351-
352- return $ text ;
353- }
354-
355357 /**
356358 * Overwrites a previous message to the output.
357359 *
@@ -400,4 +402,32 @@ private function determineBestFormat()
400402
401403 return $ format ;
402404 }
405+
406+ static private function initPlaceholderFormatters ()
407+ {
408+ return array (
409+ '%bar% ' => function (ProgressBar $ bar ) {
410+ $ completeBars = floor ($ bar ->getMaxSteps () > 0 ? $ bar ->getProgressPercent () * $ bar ->getBarWidth () : $ bar ->getStep () % $ bar ->getBarWidth ());
411+ $ emptyBars = $ bar ->getBarWidth () - $ completeBars - Helper::strlen ($ bar ->getProgressCharacter ());
412+ $ display = str_repeat ($ bar ->getBarCharacter (), $ completeBars );
413+ if ($ completeBars < $ bar ->getBarWidth ()) {
414+ $ display .= $ bar ->getProgressCharacter ().str_repeat ($ bar ->getEmptyBarCharacter (), $ emptyBars );
415+ }
416+
417+ return $ display ;
418+ },
419+ '%elapsed% ' => function (ProgressBar $ bar ) {
420+ return str_pad (Helper::formatTime (time () - $ bar ->getStartTime ()), 6 , ' ' , STR_PAD_LEFT );
421+ },
422+ '%current% ' => function (ProgressBar $ bar ) {
423+ return str_pad ($ bar ->getStep (), $ bar ->getStepWidth (), ' ' , STR_PAD_LEFT );
424+ },
425+ '%max% ' => function (ProgressBar $ bar ) {
426+ return $ bar ->getMaxSteps ();
427+ },
428+ '%percent% ' => function (ProgressBar $ bar ) {
429+ return str_pad (floor ($ bar ->getProgressPercent () * 100 ), 3 , ' ' , STR_PAD_LEFT );
430+ },
431+ );
432+ }
403433}
0 commit comments