public static function updateProgress($done, $total, $prefix = null)
{
$width = self::$_progressWidth;
if ($width === false) {
$width = 0;
} else {
$screenSize = static::getScreenSize(true);
if ($screenSize === false && $width < 1) {
$width = 0;
} elseif ($width === null) {
$width = $screenSize[0];
} elseif ($width > 0 && $width < 1) {
$width = floor($screenSize[0] * $width);
}
}
if ($prefix === null) {
$prefix = self::$_progressPrefix;
} else {
self::$_progressPrefix = $prefix;
}
$width -= static::ansiStrlen($prefix);
$percent = $total == 0 ? 1 : $done / $total;
$info = sprintf('%d%% (%d/%d)', $percent * 100, $done, $total);
if ($done > $total || $done == 0) {
self::$_progressEta = null;
self::$_progressEtaLastUpdate = time();
} elseif ($done < $total) {
// update ETA once per second to avoid flapping
if (time() - self::$_progressEtaLastUpdate > 1 && $done > self::$_progressEtaLastDone) {
$rate = (time() - (self::$_progressEtaLastUpdate ?: self::$_progressStart)) / ($done - self::$_progressEtaLastDone);
self::$_progressEta = $rate * ($total - $done);
self::$_progressEtaLastUpdate = time();
self::$_progressEtaLastDone = $done;
}
}
if (self::$_progressEta === null) {
$info .= ' ETA: n/a';
} else {
$info .= sprintf(' ETA: %d sec.', self::$_progressEta);
}
// Number extra characters outputted. These are opening [, closing ], and space before info
// Since Windows uses \r\n\ for line endings, there's one more in the case
$extraChars = static::isRunningOnWindows() ? 4 : 3;
$width -= $extraChars + static::ansiStrlen($info);
// skipping progress bar on very small display or if forced to skip
if ($width < 5) {
static::stdout("\r{$prefix}{$info} ");
} else {
if ($percent < 0) {
$percent = 0;
} elseif ($percent > 1) {
$percent = 1;
}
$bar = floor($percent * $width);
$status = str_repeat('=', $bar);
if ($bar < $width) {
$status .= '>';
$status .= str_repeat(' ', $width - $bar - 1);
}
static::stdout("\r{$prefix}" . "[{$status}] {$info}");
}
flush();
}