From 5d366de1151ef48b204b3b56fbb81057bab4c5b1 Mon Sep 17 00:00:00 2001 From: Mohammad Nael Dahman Date: Fri, 28 Jun 2024 00:59:01 +0300 Subject: [PATCH] Update progressbar.class.php Fixed getting terminal's width in Windows/Linux/Mac dynamically, improved building progressbar by adding the ability to change the progressbar current track and whole track characters, added coloring ability by adding the method color() to colorize the progressbar track in the progressbar constructor parameters. --- progressbar.class.php | 322 ++++++++++++++++++++++++------------------ 1 file changed, 183 insertions(+), 139 deletions(-) diff --git a/progressbar.class.php b/progressbar.class.php index 08be927..451a869 100644 --- a/progressbar.class.php +++ b/progressbar.class.php @@ -51,155 +51,199 @@ */ class ProgressBar { - private $currentProgress; - private $endProgress; - private $currentTime; - - /** - * Constructor. Requires $endProgress param. - * This param should be Integer value indicating, how many iterations - * will loop, that this progress bar is used in, contain. - * - * @param integer $endProgress end progress - * @throws InvalidArgumentException - */ - public function __construct($endProgress) - { - if (!is_numeric($endProgress) || $endProgress < 1) { - throw new InvalidArgumentException('Provided end progress value should be numeric.'); - } - $this->endProgress = $endProgress; - $this->currentTime = microtime(true); + private $currentProgress; + private $endProgress; + private $currentTime; + private $progressChar; + private $progressCharColor; + private $progressTrack; + private $progressTrackColor; + + /** + * Constructor. Requires $endProgress param. + * This param should be Integer value indicating, how many iterations + * will loop, that this progress bar is used in, contain. + * + * @param integer $endProgress end progress + * @throws InvalidArgumentException + */ + public function __construct( + $endProgress, + $progressChar = "=", + $progressTrack = " ", + $progressCharColor = "default", + $progressTrackColor = "default" + ) { + if (!is_numeric($endProgress) || $endProgress < 1) { + throw new InvalidArgumentException('Provided end progress value should be numeric.'); } - /** - * Returns current progress. $currentProgress - * parameter is optional. If not provided, current progress - * will be incremented by one. - * - * @param int $currentProgress - * @return string - * @throws InvalidArgumentException - */ - public function drawCurrentProgress($currentProgress = null) - { - if ($currentProgress !== null) { - if ($currentProgress < $this->currentProgress) { - throw new InvalidArgumentException("Provided current progress is smaller than previous one."); - } else { - $this->currentProgress = $currentProgress; - } - } else { - $this->currentProgress++; - } - - $progress = $this->currentPercentage(); - $maxWidth = $this->getTerminalWidth(); - $etaNum = $this->getETA($progress); - - return $this->buildBar($progress, $maxWidth, $etaNum); + $this->endProgress = $endProgress; + $this->currentTime = microtime(true); + $this->progressChar = $progressChar; + $this->progressTrack = $progressTrack; + $this->progressCharColor = $progressCharColor; + $this->progressTrackColor = $progressTrackColor; + } + + /** + * Returns current progress. $currentProgress + * parameter is optional. If not provided, current progress + * will be incremented by one. + * + * @param int $currentProgress + * @return string + * @throws InvalidArgumentException + */ + public function drawCurrentProgress($currentProgress = null) { + if ($currentProgress !== null) { + if ($currentProgress < $this->currentProgress) { + throw new InvalidArgumentException("Provided current progress is smaller than previous one."); + } + else { + $this->currentProgress = $currentProgress; + } } - - /** - * Calculates current percentage - * @return int - */ - private function currentPercentage() - { - $progress = $this->currentProgress / $this->endProgress; - - return $progress * 100; + else { + $this->currentProgress++; } - /** - * Builds progress bar row using provided data - * - * @param int $progress - * @param int $maxWidth - * @param string $etaNum - * @return string - */ - private function buildBar($progress, $maxWidth, $etaNum) - { - $eta = $etaNum ? '(ETA: ' . $etaNum . ')' : ''; - $percentage = number_format($progress, 2) . "%"; - - $widthLeft = $maxWidth - 1 - strlen($eta) - 1 - strlen($percentage) - 2; - - - $prgDone = ceil($widthLeft * ($progress / 100)); - $prgNotDone = $widthLeft - $prgDone; - - $out = "[" . str_repeat("=", $prgDone) . str_repeat(" ", $prgNotDone) . '] ' . $percentage . ' ' . $eta; - - return "\r" . $out; + $progress = $this->currentPercentage(); + $maxWidth = $this->getTerminalWidth(); + $etaNum = $this->getETA($progress); + + return $this->buildBar($progress, $maxWidth, $etaNum); + } + + private function color(string $color, string $text) { + $colors = [ + "black" => "\033[0;30m", + "red" => "\033[0;31m", + "light-red" => "\033[1;31m", + "green" => "\033[0;32m", + "light-green" => "\033[1;32m", + "brown" => "\033[0;33m", + "orange" => "\033[0;33m", + "blue" => "\033[0;34m", + "light-blue" => "\033[1;34m", + "purple" => "\033[0;35m", + "light-purple" => "\033[1;35m", + "cyan" => "\033[0;36m", + "light-cyan" => "\033[1;36m", + "light-gray" => "\033[0;37m", + "dark-gray" => "\033[1;30m", + "yellow" => "\033[1;33m", + "white" => "\033[1;37m", + "default" => "\033[0m", + ]; + + if (!array_key_exists($color, $colors)) $color = $colors["default"]; + + return $colors[$color] . "{$text}" . $colors['default']; + } + + /** + * Calculates current percentage + * @return int + */ + private function currentPercentage() { + $progress = $this->currentProgress / $this->endProgress; + + return $progress * 100; + } + + /** + * Builds progress bar row using provided data + * + * @param int $progress + * @param int $maxWidth + * @param string $etaNum + * @return string + */ + private function buildBar($progress, $maxWidth, $etaNum) { + $eta = $etaNum ? '(ETA: ' . $etaNum . ')' : ''; + $percentage = number_format($progress, 2) . "%"; + $widthLeft = $maxWidth - 1 - strlen($eta) - 1 - strlen($percentage) - 2; + $prgDone = ceil($widthLeft * ($progress / 100)); + $prgNotDone = $widthLeft - $prgDone; + $out = "[" . str_repeat($this->color($this->progressCharColor, $this->progressChar), $prgDone) . str_repeat($this->color($this->progressTrackColor, $this->progressTrack), $prgNotDone) . '] ' . $percentage . ' ' . $eta; + + return "\r{$out}"; + } + + /** + * Returns terminal width + * + * @return int + */ + private function getTerminalWidth() { + switch (PHP_OS_FAMILY) { + case "Windows": + return (int) exec('%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe -Command "$Host.UI.RawUI.WindowSize.Width"'); + case 'Darwin': + return (int) exec('/bin/stty/ size | cut -d" " -f2'); + case 'Linux': + return (int) exec('/usr/bin/env tput cols'); + default: + return 0; } - - /** - * Returns terminal width - * - * @return int - */ - private function getTerminalWidth() - { - return exec('tput cols'); + } + + /** + * Calculates and returns ETA with human timing formatting + * + * @param int $progress + * @return string + */ + private function getETA($progress) { + $currTime = microtime(true); + + if (!$progress || $progress <= 0 || $progress === false) { + return ""; } - /** - * Calculates and returns ETA with human timing formatting - * - * @param int $progress - * @return string - */ - private function getETA($progress) - { - - - $currTime = microtime(true); - - if (!$progress || $progress <= 0 || $progress === false) { - return ""; - } - - try { - $etaTime = (($currTime - $this->currentTime) / $progress) * (100 - $progress); - - $diff = ceil($etaTime); - - $eta = $this->humanTiming($diff); - } catch (Exception $ex) { - $eta = ''; - } - - return $eta; + try { + $etaTime = (($currTime - $this->currentTime) / $progress) * (100 - $progress); + $diff = ceil($etaTime); + $eta = $this->humanTiming($diff); + } + catch (Exception $ex) { + $eta = ''; } - /** - * Converts numeric time to human-readable format - * - * @param int $time - * @return string - */ - private function humanTiming($time) - { - - $tokens = array( - 31536000 => 'y', - 2592000 => 'mo', - 604800 => 'w', - 86400 => 'd', - 3600 => 'h', - 60 => 'm', - 1 => 's' - ); - - foreach ($tokens as $unit => $text) { - if ($time < $unit) { - continue; - } - $numberOfUnits = floor($time / $unit); - return $numberOfUnits . '' . $text; - } + return $eta; + } + + /** + * Converts numeric time to human-readable format + * + * @param int $time + * @return string + */ + private function humanTiming($time) { + $tokens = [ + 31536000 => ['year', 'years'], + 2592000 => ['month', 'months'], + 604800 => ['week', 'weeks'], + 86400 => ['day', 'days'], + 3600 => ['hour', 'hours'], + 60 => ['minute', 'minutes'], + 1 => ['second', 'seconds'] + ]; + + $result = ''; + + foreach ($tokens as $unit => $labels) { + if ($time < $unit) continue; + + $numberOfUnits = floor($time / $unit); + $label = $numberOfUnits === 1 ? $labels[0] : $labels[1]; + + $result .= ($result ? ' ' : '') . $numberOfUnits . ' ' . $label; + $time -= $numberOfUnits * $unit; } + return $result ?: '0 seconds'; + } + }