Subversion Repositories ALCASAR

Rev

Details | Last modification | View Log

Rev Author Line No. Line
3241 rexy 1
<?php
2
/**
3
 * PHP CLI Progress bar
4
 *
5
 * PHP 5
6
 *
7
 * Licensed under The MIT License
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright 2011, Andy Dawson
11
 * @link          http://ad7six.com
12
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
13
 */
14
 
15
/**
16
 * progressbar
17
 *
18
 * Static wrapper class for generating progress bars for cli tasks
19
 *
20
 */
21
namespace mbolli\nfsen_ng\vendor;
22
 
23
class ProgressBar
24
{
25
 
26
    /**
27
     * Merged with options passed in start function
28
     */
29
    protected static $defaults = ['format' => "\r:message::padding:%.01f%% %2\$d/%3\$d ETC: %4\$s. Elapsed: %5\$s [%6\$s]", 'message' => 'Running', 'size' => 30, 'width' => null];
30
 
31
    /**
32
     * Runtime options
33
     */
34
    protected static $options = [];
35
 
36
    /**
37
     * How much have we done already
38
     */
39
    protected static $done = 0;
40
 
41
    /**
42
     * The format string used for the rendered status bar - see $defaults
43
     */
44
    protected static $format;
45
 
46
    /**
47
     * message to display prefixing the progress bar text
48
     */
49
    protected static $message;
50
 
51
    /**
52
     * How many chars to use for the progress bar itself. Not to be confused with $width
53
     */
54
    protected static $size = 30;
55
 
56
    /**
57
     * When did we start (timestamp)
58
     */
59
    protected static $start;
60
 
61
    /**
62
     * The width in characters the whole rendered string must fit in. defaults to the width of the
63
     * terminal window
64
     */
65
    protected static $width;
66
 
67
    /**
68
     * What's the total number of times we're going to call set
69
     */
70
    protected static $total;
71
 
72
    /**
73
     * Show a progress bar, actually not usually called explicitly. Called by next()
74
     *
75
     * @param int $done what fraction of $total to set as progress uses internal counter if not passed
76
     *
77
     * @static
78
     * @return string, the formatted progress bar prefixed with a carriage return
79
     */
80
    public static function display($done = null)
81
    {
82
        if ($done) {
83
            self::$done = $done;
84
        }
85
 
86
        $now = time();
87
 
88
        if (self::$total) {
89
            $fractionComplete = (double) (self::$done / self::$total);
90
        } else {
91
            $fractionComplete = 0;
92
        }
93
 
94
        $bar = floor($fractionComplete * self::$size);
95
        $barSize = min($bar, self::$size);
96
 
97
        $barContents = str_repeat('=', $barSize);
98
        if ($bar < self::$size) {
99
            $barContents .= '>';
100
            $barContents .= str_repeat(' ', self::$size - $barSize);
101
        } elseif ($fractionComplete > 1) {
102
            $barContents .= '!';
103
        } else {
104
            $barContents .= '=';
105
        }
106
 
107
        $percent = number_format($fractionComplete * 100, 1);
108
 
109
        $elapsed = $now - self::$start;
110
        if (self::$done) {
111
            $rate = $elapsed / self::$done;
112
        } else {
113
            $rate = 0;
114
        }
115
        $left = self::$total - self::$done;
116
        $etc = round($rate * $left, 2);
117
 
118
        if (self::$done) {
119
            $etcNowText = '< 1 sec';
120
        } else {
121
            $etcNowText = '???';
122
        }
123
        $timeRemaining = self::humanTime($etc, $etcNowText);
124
        $timeElapsed = self::humanTime($elapsed);
125
 
126
        $return = sprintf(
127
            self::$format,
128
            $percent,
129
            self::$done,
130
            self::$total,
131
            $timeRemaining,
132
            $timeElapsed,
133
            $barContents
134
        );
135
 
136
        $width = strlen(preg_replace('@(?:\r|:\w+:)@', '', $return));
137
 
138
        if (strlen((string) self::$message) > ((int)self::$width - (int)$width - 3)) {
139
            $message = substr((string) self::$message, 0, ((int)self::$width - (int)$width - 4)) . '...';
140
            $padding = '';
141
        } else {
142
            $message = self::$message;
143
            $width += strlen((string) $message);
144
            $padding = str_repeat(' ', ((int)self::$width - (int)$width));
145
        }
146
 
147
        $return = str_replace(':message:', $message, $return);
148
        $return = str_replace(':padding:', $padding, $return);
149
 
150
        return $return;
151
    }
152
 
153
    /**
154
     * reset internal state, and send a new line so that the progress bar text is "finished"
155
     *
156
     * @static
157
     * @return string, a new line
158
     */
159
    public static function finish()
160
    {
161
        self::reset();
162
        return "\n";
163
    }
164
 
165
    /**
166
     * Increment the internal counter, and returns the result of display
167
     *
168
     * @param int    $inc     Amount to increment the internal counter
169
     * @param string $message If passed, overrides the existing message
170
     *
171
     * @static
172
     * @return string - the progress bar
173
     */
174
    public static function next($inc = 1, $message = '')
175
    {
176
        self::$done += $inc;
177
 
178
        if ($message) {
179
            self::$message = $message;
180
        }
181
 
182
        return self::display();
183
    }
184
 
185
    /**
186
     * Called by start and finish
187
     *
188
     * @param array $options array
189
     *
190
     * @static
191
     * @return void
192
     */
193
    public static function reset(array $options = [])
194
    {
195
        $options = array_merge(self::$defaults, $options);
196
 
197
        if (empty($options['done'])) {
198
            $options['done'] = 0;
199
        }
200
        if (empty($options['start'])) {
201
            $options['start'] = time();
202
        }
203
        if (empty($options['total'])) {
204
            $options['total'] = 0;
205
        }
206
 
207
        self::$done = $options['done'];
208
        self::$format = $options['format'];
209
        self::$message = $options['message'];
210
        self::$size = $options['size'];
211
        self::$start = $options['start'];
212
        self::$total = $options['total'];
213
        self::setWidth($options['width']);
214
    }
215
 
216
    /**
217
     * change the message to be used the next time the display method is called
218
     *
219
     * @param string $message the string to display
220
     *
221
     * @static
222
     * @return void
223
     */
224
    public static function setMessage($message = '')
225
    {
226
        self::$message = $message;
227
    }
228
 
229
    /**
230
     * change the total on a running progress bar
231
     *
232
     * @param int|string $total the new number of times we're expecting to run for
233
     *
234
     * @static
235
     * @return void
236
     */
237
    public static function setTotal($total = '')
238
    {
239
        self::$total = $total;
240
    }
241
 
242
    /**
243
     * Initialize a progress bar
244
     *
245
     * @param int|null $total   number of times we're going to call set
246
     * @param string   $message message to prefix the bar with
247
     * @param array    $options overrides for default options
248
     *
249
     * @static
250
     * @return string - the progress bar string with 0 progress
251
     */
252
    public static function start(?int $total = null, string $message = '', array $options = [])
253
    {
254
        if ($message) {
255
            $options['message'] = $message;
256
        }
257
        $options['total'] = $total;
258
        $options['start'] = time();
259
        self::reset($options);
260
 
261
        return self::display();
262
    }
263
 
264
    /**
265
     * Convert a number of seconds into something human readable like "2 days, 4 hrs"
266
     *
267
     * @param int|float $seconds how far in the future/past to display
268
     * @param string    $nowText if there are no seconds, what text to display
269
     *
270
     * @static
271
     * @return string representation of the time
272
     */
273
    protected static function humanTime($seconds, string $nowText = '< 1 sec')
274
    {
275
        $prefix = '';
276
        if ($seconds < 0) {
277
            $prefix = '- ';
278
            $seconds = -$seconds;
279
        }
280
 
281
        $days = $hours = $minutes = 0;
282
 
283
        if ($seconds >= 86400) {
284
            $days = (int) ($seconds / 86400);
285
            $seconds = $seconds - $days * 86400;
286
        }
287
        if ($seconds >= 3600) {
288
            $hours = (int) ($seconds / 3600);
289
            $seconds = $seconds - $hours * 3600;
290
        }
291
        if ($seconds >= 60) {
292
            $minutes = (int) ($seconds / 60);
293
            $seconds = $seconds - $minutes * 60;
294
        }
295
        $seconds = (int) $seconds;
296
 
297
        $return = [];
298
 
299
        if ($days) {
300
            $return[] = "$days days";
301
        }
302
        if ($hours) {
303
            $return[] = "$hours hrs";
304
        }
305
        if ($minutes) {
306
            $return[] = "$minutes mins";
307
        }
308
        if ($seconds) {
309
            $return[] = "$seconds secs";
310
        }
311
 
312
        if (!$return) {
313
            return $nowText;
314
        }
315
        return $prefix . implode( ', ', array_slice($return, 0, 2));
316
    }
317
 
318
    /**
319
     * Set the width the rendered text must fit in
320
     *
321
     * @param int $width passed in options
322
     *
323
     * @static
324
     * @return void
325
     */
326
    protected static function setWidth($width = null)
327
    {
328
        if ($width === null) {
329
            if (DIRECTORY_SEPARATOR === '/' && getenv("TERM")) {
330
                $width = `tput cols`;
331
            }
332
            if ($width < 80) {
333
                $width = 80;
334
            }
335
        }
336
        self::$width = $width;
337
    }
338
}