0.6.0
This commit is contained in:
342
ProgressBar.php
Normal file
342
ProgressBar.php
Normal file
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
/**
|
||||
* PHP CLI Progress bar
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright 2011, Andy Dawson
|
||||
* @link http://ad7six.com
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
|
||||
/**
|
||||
* ProgressBar
|
||||
*
|
||||
* Static wrapper class for generating progress bars for cli tasks
|
||||
*
|
||||
*/
|
||||
class ProgressBar
|
||||
{
|
||||
|
||||
/**
|
||||
* Merged with options passed in start function
|
||||
*/
|
||||
protected static $defaults = array(
|
||||
'format' => "\r:message::padding:%.01f%% %2\$d/%3\$d Est: %4\$s. Elapsed: %5\$s [%6\$s]",
|
||||
'message' => 'Running',
|
||||
'size' => 30,
|
||||
'width' => null
|
||||
);
|
||||
|
||||
/**
|
||||
* Runtime options
|
||||
*/
|
||||
protected static $options = array();
|
||||
|
||||
/**
|
||||
* How much have we done already
|
||||
*/
|
||||
protected static $done = 0;
|
||||
|
||||
/**
|
||||
* The format string used for the rendered status bar - see $defaults
|
||||
*/
|
||||
protected static $format;
|
||||
|
||||
/**
|
||||
* message to display prefixing the progress bar text
|
||||
*/
|
||||
protected static $message;
|
||||
|
||||
/**
|
||||
* How many chars to use for the progress bar itself. Not to be confused with $width
|
||||
*/
|
||||
protected static $size = 30;
|
||||
|
||||
/**
|
||||
* When did we start (timestamp)
|
||||
*/
|
||||
protected static $start;
|
||||
|
||||
/**
|
||||
* The width in characters the whole rendered string must fit in. defaults to the width of the
|
||||
* terminal window
|
||||
*/
|
||||
protected static $width;
|
||||
|
||||
/**
|
||||
* What's the total number of times we're going to call set
|
||||
*/
|
||||
protected static $total;
|
||||
|
||||
/**
|
||||
* Show a progress bar, actually not usually called explicitly. Called by next()
|
||||
*
|
||||
* @param int $done what fraction of $total to set as progress uses internal counter if not passed
|
||||
*
|
||||
* @static
|
||||
* @return string, the formatted progress bar prefixed with a carriage return
|
||||
*/
|
||||
public static function display($done = null)
|
||||
{
|
||||
if ($done) {
|
||||
self::$done = $done;
|
||||
}
|
||||
|
||||
$now = time();
|
||||
|
||||
if (self::$total) {
|
||||
$fractionComplete = (double) (self::$done / self::$total);
|
||||
} else {
|
||||
$fractionComplete = 0;
|
||||
}
|
||||
|
||||
$bar = floor($fractionComplete * self::$size);
|
||||
$barSize = min($bar, self::$size);
|
||||
|
||||
$barContents = str_repeat('=', $barSize);
|
||||
if ($bar < self::$size) {
|
||||
$barContents .= '>';
|
||||
$barContents .= str_repeat(' ', self::$size - $barSize);
|
||||
} elseif ($fractionComplete > 1) {
|
||||
$barContents .= '!';
|
||||
} else {
|
||||
$barContents .= '=';
|
||||
}
|
||||
|
||||
$percent = number_format($fractionComplete * 100, 0);
|
||||
|
||||
$elapsed = $now - self::$start;
|
||||
if (self::$done) {
|
||||
$rate = $elapsed / self::$done;
|
||||
} else {
|
||||
$rate = 0;
|
||||
}
|
||||
$left = self::$total - self::$done;
|
||||
$etc = round($rate * $left, 2);
|
||||
|
||||
if (self::$done) {
|
||||
$etcNowText = '< 1 sec';
|
||||
} else {
|
||||
$etcNowText = '???';
|
||||
}
|
||||
$timeRemaining = self::humanTime($etc, $etcNowText);
|
||||
$timeElapsed = self::humanTime($elapsed);
|
||||
|
||||
$return = sprintf(
|
||||
self::$format,
|
||||
$percent,
|
||||
self::$done,
|
||||
self::$total,
|
||||
$timeRemaining,
|
||||
$timeElapsed,
|
||||
$barContents
|
||||
);
|
||||
|
||||
$width = strlen(preg_replace('@(?:\r|:\w+:)@', '', $return));
|
||||
|
||||
if (strlen(self::$message) > (self::$width - $width - 3)) {
|
||||
$message = substr(self::$message, 0, (self::$width - $width - 4)) . '...';
|
||||
$padding = '';
|
||||
echo "\n" . strlen($return);
|
||||
} else {
|
||||
$message = self::$message;
|
||||
$width += strlen($message);
|
||||
$padding = str_repeat(' ', (self::$width - $width));
|
||||
}
|
||||
|
||||
$return = str_replace(':message:', $message, $return);
|
||||
$return = str_replace(':padding:', $padding, $return);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* reset internal state, and send a new line so that the progress bar text is "finished"
|
||||
*
|
||||
* @static
|
||||
* @return string, a new line
|
||||
*/
|
||||
public static function finish()
|
||||
{
|
||||
self::reset();
|
||||
return "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the internal counter, and returns the result of display
|
||||
*
|
||||
* @param int $inc Amount to increment the internal counter
|
||||
* @param string $message If passed, overrides the existing message
|
||||
*
|
||||
* @static
|
||||
* @return string - the progress bar
|
||||
*/
|
||||
public static function next($inc = 1, $message = '')
|
||||
{
|
||||
self::$done += $inc;
|
||||
|
||||
if ($message) {
|
||||
self::$message = $message;
|
||||
}
|
||||
|
||||
return self::display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by start and finish
|
||||
*
|
||||
* @param array $options array
|
||||
*
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
public static function reset($options = array())
|
||||
{
|
||||
$options = array_merge(self::$defaults, $options);
|
||||
|
||||
if (empty($options['done'])) {
|
||||
$options['done'] = 0;
|
||||
}
|
||||
if (empty($options['start'])) {
|
||||
$options['start'] = time();
|
||||
}
|
||||
if (empty($options['total'])) {
|
||||
$options['total'] = 0;
|
||||
}
|
||||
|
||||
self::$done = $options['done'];
|
||||
self::$format = $options['format'];
|
||||
self::$message = $options['message'];
|
||||
self::$size = $options['size'];
|
||||
self::$start = $options['start'];
|
||||
self::$total = $options['total'];
|
||||
self::setWidth($options['width']);
|
||||
}
|
||||
|
||||
/**
|
||||
* change the message to be used the next time the display method is called
|
||||
*
|
||||
* @param string $message the string to display
|
||||
*
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
public static function setMessage($message = '')
|
||||
{
|
||||
self::$message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* change the total on a running progress bar
|
||||
*
|
||||
* @param int $total the new number of times we're expecting to run for
|
||||
*
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
public static function setTotal($total = '')
|
||||
{
|
||||
self::$total = $total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a progress bar
|
||||
*
|
||||
* @param mixed $total number of times we're going to call set
|
||||
* @param int $message message to prefix the bar with
|
||||
* @param int $options overrides for default options
|
||||
*
|
||||
* @static
|
||||
* @return string - the progress bar string with 0 progress
|
||||
*/
|
||||
public static function start($total = null, $message = '', $options = array())
|
||||
{
|
||||
if ($message) {
|
||||
$options['message'] = $message;
|
||||
}
|
||||
$options['total'] = $total;
|
||||
$options['start'] = time();
|
||||
self::reset($options);
|
||||
|
||||
return self::display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a number of seconds into something human readable like "2 days, 4 hrs"
|
||||
*
|
||||
* @param int $seconds how far in the future/past to display
|
||||
* @param string $nowText if there are no seconds, what text to display
|
||||
*
|
||||
* @static
|
||||
* @return string representation of the time
|
||||
*/
|
||||
protected static function humanTime($seconds, $nowText = '< 1 sec')
|
||||
{
|
||||
$prefix = '';
|
||||
if ($seconds < 0) {
|
||||
$prefix = '- ';
|
||||
$seconds = -$seconds;
|
||||
}
|
||||
|
||||
$days = $hours = $minutes = 0;
|
||||
|
||||
if ($seconds >= 86400) {
|
||||
$days = (int) ($seconds / 86400);
|
||||
$seconds = $seconds - $days * 86400;
|
||||
}
|
||||
if ($seconds >= 3600) {
|
||||
$hours = (int) ($seconds / 3600);
|
||||
$seconds = $seconds - $hours * 3600;
|
||||
}
|
||||
if ($seconds >= 60) {
|
||||
$minutes = (int) ($seconds / 60);
|
||||
$seconds = $seconds - $minutes * 60;
|
||||
}
|
||||
$seconds = (int) $seconds;
|
||||
|
||||
$return = array();
|
||||
|
||||
if ($days) {
|
||||
$return[] = "$days days";
|
||||
}
|
||||
if ($hours) {
|
||||
$return[] = "$hours hrs";
|
||||
}
|
||||
if ($minutes) {
|
||||
$return[] = "$minutes mins";
|
||||
}
|
||||
if ($seconds) {
|
||||
$return[] = "$seconds secs";
|
||||
}
|
||||
|
||||
if (!$return) {
|
||||
return $nowText;
|
||||
}
|
||||
return $prefix . implode(array_slice($return, 0, 2), ', ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the width the rendered text must fit in
|
||||
*
|
||||
* @param int $width passed in options
|
||||
*
|
||||
* @static
|
||||
* @return void
|
||||
*/
|
||||
protected static function setWidth($width = null)
|
||||
{
|
||||
if ($width === null) {
|
||||
if (DIRECTORY_SEPARATOR === '/') {
|
||||
$width = `tput cols`;
|
||||
}
|
||||
if ($width < 80) {
|
||||
$width = 80;
|
||||
}
|
||||
}
|
||||
self::$width = $width;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user