- Added new ConsoleProgressBar
class for UI improvement, imrpoved the CLI Progress Bar inspired by
[pacman](https://wiki.archlinux.org/title/pacman)
This commit is contained in:
parent
173032df72
commit
2605b8d218
8 changed files with 524 additions and 111 deletions
|
@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Implemented support in the AST traversal for the PHP statements `include`, `include_once`, `require`, and
|
||||
`require_once`. These statements are transformed into function calls. With this change, ncc can correctly handle and
|
||||
import files from system packages or direct binary package files.
|
||||
- Added new `ConsoleProgressBar` class for UI improvement, imrpoved the CLI Progress Bar inspired by
|
||||
[pacman](https://wiki.archlinux.org/title/pacman)
|
||||
|
||||
### Fixed
|
||||
- When finding package versions in the package lock, ncc will try to find a satisfying version rather than the exact
|
||||
|
|
|
@ -255,10 +255,12 @@
|
|||
}
|
||||
|
||||
$total_items = count($build_files);
|
||||
$processed_items = 1;
|
||||
//$processed_items = 1;
|
||||
$progress_bar = new \ncc\Utilities\ConsoleProgressBar('Installing ncc', $total_items);
|
||||
|
||||
foreach ($build_files as $item)
|
||||
{
|
||||
$progress_bar->setMiscText($item, true);
|
||||
$source = __DIR__ . DIRECTORY_SEPARATOR . $item;
|
||||
$destination = $NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . $item;
|
||||
|
||||
|
@ -281,10 +283,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
++$processed_items;
|
||||
Console::inlineProgressBar($processed_items, $total_items);
|
||||
//++$processed_items;
|
||||
//Console::inlineProgressBar($processed_items, $total_items);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
}
|
||||
|
||||
$progress_bar->setMiscText('done', true);
|
||||
unset($progress_bar);
|
||||
|
||||
// Initialize ncc's files
|
||||
try
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
use ncc\Objects\ProjectConfiguration\Dependency;
|
||||
use ncc\Utilities\Base64;
|
||||
use ncc\Utilities\Console;
|
||||
use ncc\Utilities\ConsoleProgressBar;
|
||||
use ncc\Utilities\Functions;
|
||||
use ncc\Utilities\IO;
|
||||
use ncc\Utilities\Resolver;
|
||||
|
@ -103,15 +104,15 @@
|
|||
$package_path = ConstantCompiler::compileConstants($this->project_manager->getProjectConfiguration(), $configuration->getOutput());
|
||||
}
|
||||
|
||||
$progress = 0;
|
||||
//$progress = 0;
|
||||
$steps =
|
||||
count($this->project_manager->getProjectConfiguration()->getExecutionPolicies()) +
|
||||
count($this->project_manager->getComponents($build_configuration)) +
|
||||
count($this->project_manager->getResources($build_configuration));
|
||||
|
||||
$progress_bar = new ConsoleProgressBar(sprintf('Building project \'%s\'', $this->project_manager->getProjectConfiguration()->getAssembly()->getName()), $steps);
|
||||
$package_writer = $this->createPackageWriter($package_path, $configuration);
|
||||
|
||||
Console::out(sprintf('Building project \'%s\'', $this->project_manager->getProjectConfiguration()->getAssembly()->getName()));
|
||||
Console::outVerbose(sprintf('Building project \'%s\'', $this->project_manager->getProjectConfiguration()->getAssembly()->getName()));
|
||||
|
||||
if($static_dependencies)
|
||||
{
|
||||
|
@ -155,39 +156,51 @@
|
|||
|
||||
if(count($execution_units) === 0)
|
||||
{
|
||||
$progress = count($this->project_manager->getProjectConfiguration()->getExecutionPolicies());
|
||||
Console::inlineProgressBar($progress, $steps);
|
||||
//$progress = count($this->project_manager->getProjectConfiguration()->getExecutionPolicies());
|
||||
//Console::inlineProgressBar($progress, $steps);
|
||||
$progress_bar->increaseValue(count($this->project_manager->getProjectConfiguration()->getExecutionPolicies()), true);
|
||||
Console::outWarning('The project contains execution policies but none of them are used');
|
||||
}
|
||||
|
||||
foreach($execution_units as $unit)
|
||||
{
|
||||
$progress++;
|
||||
Console::inlineProgressBar($progress, $steps);
|
||||
$progress_bar->setMiscText($unit->getExecutionPolicy()->getName());
|
||||
//$progress++;
|
||||
//Console::inlineProgressBar($progress, $steps);
|
||||
$package_writer->addExecutionUnit($unit);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Compile package components
|
||||
foreach($this->project_manager->getComponents($build_configuration) as $component)
|
||||
{
|
||||
$progress++;
|
||||
Console::inlineProgressBar($progress, $steps);
|
||||
//$progress++;
|
||||
//Console::inlineProgressBar($progress, $steps);
|
||||
$progress_bar->setMiscText($component);
|
||||
Console::outVerbose(sprintf('Compiling \'%s\'', $component));
|
||||
|
||||
$this->processComponent($package_writer, $component);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
}
|
||||
|
||||
// Compile package resources
|
||||
foreach($this->project_manager->getResources($build_configuration) as $resource)
|
||||
{
|
||||
$progress++;
|
||||
Console::inlineProgressBar($progress, $steps);
|
||||
//$progress++;
|
||||
//Console::inlineProgressBar($progress, $steps);
|
||||
$progress_bar->setMiscText($resource);
|
||||
Console::outVerbose(sprintf('Processing \'%s\'', $resource));
|
||||
|
||||
$this->processResource($package_writer, $resource);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
}
|
||||
|
||||
$progress_bar->setMiscText('done', true);
|
||||
unset($progress_bar);
|
||||
|
||||
Console::out(sprintf('Processing dependencies...'));
|
||||
|
||||
// Add the project dependencies
|
||||
foreach($this->project_manager->getProjectConfiguration()->getBuild()->getDependencies() as $dependency)
|
||||
{
|
||||
|
@ -211,6 +224,7 @@
|
|||
* @param Dependency $dependency
|
||||
* @param bool $static
|
||||
* @return void
|
||||
* @throws ConfigurationException
|
||||
* @throws IOException
|
||||
*/
|
||||
private function processDependency(PackageWriter $package_writer, Dependency $dependency, bool $static=false): void
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
use ncc\Enums\PackageDirectory;
|
||||
use ncc\Enums\PackageStructure;
|
||||
use ncc\Enums\PackageStructureVersions;
|
||||
use ncc\Exceptions\ConfigurationException;
|
||||
use ncc\Exceptions\IOException;
|
||||
use ncc\Objects\Package\Component;
|
||||
use ncc\Objects\Package\ExecutionUnit;
|
||||
|
@ -39,6 +40,7 @@
|
|||
use ncc\Objects\ProjectConfiguration\Installer;
|
||||
use ncc\Extensions\ZiProto\ZiProto;
|
||||
use ncc\Utilities\Console;
|
||||
use ncc\Utilities\ConsoleProgressBar;
|
||||
|
||||
class PackageWriter
|
||||
{
|
||||
|
@ -357,13 +359,17 @@
|
|||
*
|
||||
* @param PackageReader $reader
|
||||
* @return void
|
||||
* @throws ConfigurationException
|
||||
*/
|
||||
public function merge(PackageReader $reader): void
|
||||
{
|
||||
$progress_bar = new ConsoleProgressBar(sprintf('Merging %s', $reader->getAssembly()->getPackage()), count($reader->getDirectory()));
|
||||
$processed_resources = [];
|
||||
|
||||
foreach($reader->getDirectory() as $name => $pointer)
|
||||
{
|
||||
$progress_bar->setMiscText($name, true);
|
||||
|
||||
switch((int)substr(explode(':', $name, 2)[0], 1))
|
||||
{
|
||||
case PackageDirectory::METADATA:
|
||||
|
@ -383,9 +389,13 @@
|
|||
|
||||
Console::outDebug(sprintf('Merging %s', $name));
|
||||
$processed_resources[$pointer] = $this->add($name, $reader->get($name));
|
||||
|
||||
}
|
||||
|
||||
$progress_bar->increaseValue(1, true);
|
||||
}
|
||||
|
||||
$progress_bar->setMiscText('done', true);
|
||||
unset($progress_bar);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
use ncc\ThirdParty\Symfony\Process\ExecutableFinder;
|
||||
use ncc\ThirdParty\Symfony\Process\Process;
|
||||
use ncc\Utilities\Console;
|
||||
use ncc\Utilities\ConsoleProgressBar;
|
||||
use ncc\Utilities\Functions;
|
||||
use ncc\Utilities\PathFinder;
|
||||
|
||||
|
@ -168,32 +169,35 @@
|
|||
*/
|
||||
private function hexDump(string $input_path, string $output_path, string $variable_name): void
|
||||
{
|
||||
Console::out(sprintf('Processing %s to hex dump', $input_path));
|
||||
Console::outVerbose(sprintf('Processing %s to hex dump', $input_path));
|
||||
|
||||
$input = fopen($input_path, 'rb');
|
||||
$output = fopen($output_path, 'wb');
|
||||
$byte_count = 0;
|
||||
$total_bytes = filesize($input_path);
|
||||
fwrite($output, sprintf("unsigned char %s[] = {\n", Functions::toSnakeCase($variable_name)));
|
||||
$progress_bar = new ConsoleProgressBar(sprintf('HexDump %s', $input_path), filesize($input_path));
|
||||
|
||||
// Convert the binary data to hex and write it to the output file
|
||||
fwrite($output, sprintf("unsigned char %s[] = {\n", Functions::toSnakeCase($variable_name)));
|
||||
// Convert the binary data to hex and write it to the output file using chunks
|
||||
while (!feof($input))
|
||||
{
|
||||
Console::inlineProgressBar(ftell($input), $total_bytes);
|
||||
$bytes = fread($input, 5026);
|
||||
$len = strlen($bytes);
|
||||
|
||||
$byte = fread($input, 1);
|
||||
if (strlen($byte) === 1)
|
||||
for ($i = 0; $i < $len; $i++)
|
||||
{
|
||||
fwrite($output, sprintf(" 0x%02x,", ord($byte)));
|
||||
fwrite($output, sprintf(" 0x%02x,", ord($bytes[$i])));
|
||||
$byte_count++;
|
||||
|
||||
// Write 12 bytes per line or when reaching the end of the file
|
||||
if ($byte_count === 12 || ($i == $len - 1 && feof($input)))
|
||||
{
|
||||
fwrite($output, "\n");
|
||||
$byte_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Write 12 bytes per line or when reaching the end of the file
|
||||
if ($byte_count === 12 || feof($input))
|
||||
{
|
||||
fwrite($output, "\n");
|
||||
$byte_count = 0;
|
||||
}
|
||||
$progress_bar->increaseValue($len, true);
|
||||
$progress_bar->setMiscText(sprintf('Processed (%d/%d)', $progress_bar->getValue(), $progress_bar->getMaxValue()));
|
||||
}
|
||||
|
||||
// Close the output file
|
||||
|
@ -204,5 +208,9 @@
|
|||
// Finally, close the input and output files
|
||||
fclose($input);
|
||||
fclose($output);
|
||||
|
||||
// Close the progress bar
|
||||
$progress_bar->setMiscText('done', true);
|
||||
unset($progress_bar);
|
||||
}
|
||||
}
|
|
@ -56,6 +56,7 @@
|
|||
use ncc\Objects\RemotePackageInput;
|
||||
use ncc\ThirdParty\Symfony\Filesystem\Filesystem;
|
||||
use ncc\Utilities\Console;
|
||||
use ncc\Utilities\ConsoleProgressBar;
|
||||
use ncc\Utilities\Functions;
|
||||
use ncc\Utilities\IO;
|
||||
use ncc\Utilities\PathFinder;
|
||||
|
@ -643,6 +644,7 @@
|
|||
* @return void
|
||||
* @throws ConfigurationException
|
||||
* @throws IOException
|
||||
* @throws NotSupportedException
|
||||
* @throws OperationException
|
||||
*/
|
||||
private function extractPackageContents(PackageReader $package_reader, string $package_path): void
|
||||
|
@ -654,61 +656,88 @@
|
|||
count($package_reader->getResources()) +
|
||||
count($package_reader->getExecutionUnits()) +
|
||||
6;
|
||||
$current_step = 0;
|
||||
//$current_step = 0;
|
||||
$progress_bar = new ConsoleProgressBar(sprintf('Extracting package %s=%s', $package_reader->getAssembly()->getPackage(), $package_reader->getAssembly()->getVersion()), $total_steps);
|
||||
|
||||
Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
//Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
foreach($package_reader->getComponents() as $component_name)
|
||||
{
|
||||
$progress_bar->setMiscText($component_name);
|
||||
|
||||
if(Resolver::checkLogLevel(LogLevel::VERBOSE, Main::getLogLevel()))
|
||||
{
|
||||
Console::outVerbose(sprintf('Extracting component %s to %s', $component_name, $bin_path . DIRECTORY_SEPARATOR . $component_name));
|
||||
}
|
||||
|
||||
IO::fwrite(
|
||||
$bin_path . DIRECTORY_SEPARATOR . $component_name,
|
||||
$package_reader->getComponent($component_name)->getData([ComponentDecodeOptions::AS_FILE]), 0755
|
||||
);
|
||||
|
||||
Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
//Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
}
|
||||
|
||||
foreach($package_reader->getResources() as $resource_name)
|
||||
{
|
||||
IO::fwrite(
|
||||
$bin_path . DIRECTORY_SEPARATOR . $resource_name,
|
||||
$package_reader->getResource($resource_name)->getData(), 0755
|
||||
);
|
||||
$progress_bar->setMiscText($resource_name);
|
||||
|
||||
if(Resolver::checkLogLevel(LogLevel::VERBOSE, Main::getLogLevel()))
|
||||
{
|
||||
Console::outVerbose(sprintf('Extracting resource %s to %s', $resource_name, $bin_path . DIRECTORY_SEPARATOR . $resource_name));
|
||||
}
|
||||
|
||||
Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
IO::fwrite(
|
||||
$bin_path . DIRECTORY_SEPARATOR . $resource_name,
|
||||
$package_reader->getResource($resource_name)->getData(), 0755
|
||||
);
|
||||
|
||||
//Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
}
|
||||
|
||||
foreach($package_reader->getExecutionUnits() as $unit)
|
||||
{
|
||||
$progress_bar->setMiscText($unit);
|
||||
|
||||
if(Resolver::checkLogLevel(LogLevel::VERBOSE, Main::getLogLevel()))
|
||||
{
|
||||
Console::outVerbose(sprintf('Extracting execution unit %s to %s', $unit, $package_path . DIRECTORY_SEPARATOR . 'units' . DIRECTORY_SEPARATOR . $package_reader->getExecutionUnit($unit)->getExecutionPolicy()->getName() . '.unit'));
|
||||
}
|
||||
|
||||
$execution_unit = $package_reader->getExecutionUnit($unit);
|
||||
$unit_path = $package_path . DIRECTORY_SEPARATOR . 'units' . DIRECTORY_SEPARATOR . $execution_unit->getExecutionPolicy()->getName() . '.unit';
|
||||
IO::fwrite($unit_path, ZiProto::encode($execution_unit->toArray(true)), 0755);
|
||||
|
||||
Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
//Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
}
|
||||
|
||||
$class_map = [];
|
||||
foreach($package_reader->getClassMap() as $class)
|
||||
{
|
||||
$progress_bar->setMiscText($class);
|
||||
$class_map[$class] = $package_reader->getComponentByClass($class)->getName();
|
||||
}
|
||||
Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
//Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
|
||||
if($package_reader->getInstaller() !== null)
|
||||
{
|
||||
$progress_bar->setMiscText('installer');
|
||||
IO::fwrite($package_path . DIRECTORY_SEPARATOR . FileDescriptor::INSTALLER, ZiProto::encode($package_reader->getInstaller()?->toArray(true)));
|
||||
}
|
||||
Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
//Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
|
||||
if(count($class_map) > 0)
|
||||
{
|
||||
$progress_bar->setMiscText('class map');
|
||||
IO::fwrite($package_path . DIRECTORY_SEPARATOR . FileDescriptor::CLASS_MAP, ZiProto::encode($class_map));
|
||||
}
|
||||
Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
//Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
|
||||
IO::fwrite($package_path . DIRECTORY_SEPARATOR . FileDescriptor::ASSEMBLY, ZiProto::encode($package_reader->getAssembly()->toArray(true)));
|
||||
IO::fwrite($package_path . DIRECTORY_SEPARATOR . FileDescriptor::METADATA, ZiProto::encode($package_reader->getMetadata()->toArray(true)));
|
||||
|
@ -717,11 +746,16 @@
|
|||
{
|
||||
IO::fwrite($package_path . DIRECTORY_SEPARATOR . FileDescriptor::UPDATE, ZiProto::encode($package_reader->getMetadata()->getUpdateSource()?->toArray(true)));
|
||||
}
|
||||
//Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
|
||||
Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
|
||||
$progress_bar->setMiscText('creating shadowcopy', true);
|
||||
$package_reader->saveCopy($package_path . DIRECTORY_SEPARATOR . FileDescriptor::SHADOW_PACKAGE);
|
||||
Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
//Console::inlineProgressBar(++$current_step, $total_steps);
|
||||
|
||||
$progress_bar->setMiscText('done', true);
|
||||
$progress_bar->increaseValue(1, true);
|
||||
unset($progress_bar);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -851,6 +885,7 @@
|
|||
|
||||
$file_handle = fopen($file_path, 'wb');
|
||||
$end = false;
|
||||
$progress_bar = new ConsoleProgressBar(sprintf('Downloading %s', $url), 100);
|
||||
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
|
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
@ -859,34 +894,39 @@
|
|||
curl_setopt($curl, CURLOPT_HTTPHEADER, [
|
||||
'User-Agent: ncc'
|
||||
]);
|
||||
curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, static function ($resource, $downloadSize, $downloaded) use ($url, &$end)
|
||||
curl_setopt($curl, CURLOPT_PROGRESSFUNCTION, static function ($resource, $download_size, $downloaded) use ($url, &$end, $progress_bar)
|
||||
{
|
||||
if($downloadSize === $downloaded && $end)
|
||||
if($download_size === $downloaded && $end)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if($downloadSize === 0)
|
||||
if($download_size === 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(Resolver::checkLogLevel(LogLevel::VERBOSE, Main::getLogLevel()))
|
||||
{
|
||||
$percentage = round(($downloaded / $downloadSize) * 100, 2);
|
||||
Console::out(sprintf('Download progress %s (%s/%s) for %s', $percentage, $downloaded, $downloadSize, $url));
|
||||
$percentage = round(($downloaded / $download_size) * 100, 2);
|
||||
Console::out(sprintf('Download progress %s (%s/%s) for %s', $percentage, $downloaded, $download_size, $url));
|
||||
}
|
||||
else
|
||||
{
|
||||
Console::inlineProgressBar($downloaded, $downloadSize);
|
||||
$progress_bar->setMaxValue($download_size);
|
||||
$progress_bar->setValue($downloaded);
|
||||
$progress_bar->setMiscText(sprintf('%s/%s', $downloaded, $download_size));
|
||||
|
||||
$progress_bar->update();
|
||||
}
|
||||
|
||||
if($downloadSize === $downloaded)
|
||||
if($download_size === $downloaded)
|
||||
{
|
||||
$end = true;
|
||||
}
|
||||
});
|
||||
|
||||
unset($progress_bar);
|
||||
curl_exec($curl);
|
||||
fclose($file_handle);
|
||||
|
||||
|
|
|
@ -43,66 +43,6 @@
|
|||
*/
|
||||
private static $last_tick_time;
|
||||
|
||||
/**
|
||||
* Inline Progress bar, created by dealnews.com.
|
||||
*
|
||||
* @param int $value
|
||||
* @param int $total
|
||||
* @param int $size
|
||||
* @param array $options
|
||||
* @return void
|
||||
* @copyright Copyright (c) 2010, dealnews.com, Inc. All rights reserved.
|
||||
* @copyright Copyright (c) 2023, Nosial. All rights reserved
|
||||
*/
|
||||
public static function inlineProgressBar(int $value, int $total, int $size = 10, array $options = []): void
|
||||
{
|
||||
if(Resolver::checkLogLevel(LogLevel::VERBOSE, Main::getLogLevel()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static $start_time;
|
||||
|
||||
// Start time initialization
|
||||
if (!$start_time)
|
||||
{
|
||||
$start_time = time();
|
||||
}
|
||||
|
||||
// If the value is out of bounds or zero, return early
|
||||
if ($value > $total || $value === 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Build status bar
|
||||
$percentage = $value / $total;
|
||||
$barLength = floor($percentage * $size);
|
||||
$statusBar = "\r[ "
|
||||
. str_repeat("=", $barLength)
|
||||
. ($barLength < $size ? ">" : "=")
|
||||
. str_repeat(" ", $size - $barLength)
|
||||
. " ] "
|
||||
. number_format($percentage * 100) . "% $value/$total";
|
||||
|
||||
// ETA and elapsed time calculation
|
||||
$rate = (time() - $start_time) / $value;
|
||||
$eta = round($rate * ($total - $value), 2);
|
||||
$elapsed = time() - $start_time;
|
||||
$remaining_text = $options['remaining_text'] ?? 'remaining: ';
|
||||
$statusBar .= " $remaining_text " . number_format($eta) . " sec. elapsed: " . number_format($elapsed) . " sec.";
|
||||
print("$statusBar ");
|
||||
|
||||
flush();
|
||||
|
||||
// Reset variables once the progress is complete
|
||||
if ($value === $total)
|
||||
{
|
||||
print("\n");
|
||||
$start_time = null; // This resets the start time for the next progress bar
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a verbose prefix to the message
|
||||
*
|
||||
|
|
393
src/ncc/Utilities/ConsoleProgressBar.php
Normal file
393
src/ncc/Utilities/ConsoleProgressBar.php
Normal file
|
@ -0,0 +1,393 @@
|
|||
<?php
|
||||
|
||||
/** @noinspection PhpMissingFieldTypeInspection */
|
||||
|
||||
/*
|
||||
* Copyright (c) Nosial 2022-2023, all rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
* associated documentation files (the "Software"), to deal in the Software without restriction, including without
|
||||
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ncc\Utilities;
|
||||
|
||||
class ConsoleProgressBar
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $max_value;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $terminal_width;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $progress_width;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $ended;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $misc_text;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $auto_end;
|
||||
|
||||
/**
|
||||
* Constructor for the object.
|
||||
*
|
||||
* This constructor initializes the object with the given parameters. By default,
|
||||
* the $max_value parameter is set to 100, and the $progress_width parameter is set to 20.
|
||||
* The object's title is set to the given $title value, and other properties are initialized
|
||||
* accordingly.
|
||||
*
|
||||
* @param string $title The title to be set for the object.
|
||||
* @param int $max_value Optional. The maximum value for the object's progress. Defaults to 100.
|
||||
* @param bool $auto_end Optional. If True, when the progress bar reaches the $max_value, a new line is created.
|
||||
* @param int $progress_width Optional. The width of the progress bar in characters. Defaults to 20.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $title, int $max_value=100, bool $auto_end=false, int $progress_width=20)
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->progress_width = $progress_width;
|
||||
$this->value = 0;
|
||||
$this->max_value = $max_value;
|
||||
$this->ended = false;
|
||||
$this->auto_end = $auto_end;
|
||||
$this->terminal_width = $this->getTerminalWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of the object.
|
||||
*
|
||||
* @return string The title of the object.
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of the object.
|
||||
*
|
||||
* This method sets the title of the object to the given value. Optionally,
|
||||
* it can also trigger an update if the $update parameter is set to true.
|
||||
*
|
||||
* @param string $title The new title to be set.
|
||||
* @param bool $update Optional. Whether to trigger an update after setting the title.
|
||||
* Defaults to false.
|
||||
* @return void
|
||||
*/
|
||||
public function setTitle(string $title, bool $update=false): void
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
if($update)
|
||||
{
|
||||
$this->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the miscellaneous text.
|
||||
*
|
||||
* This method retrieves the miscellaneous text associated with the object.
|
||||
*
|
||||
* @return string|null The miscellaneous text, or null if it is not set.
|
||||
*/
|
||||
public function getMiscText(): ?string
|
||||
{
|
||||
return $this->misc_text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the miscellaneous text of the object.
|
||||
*
|
||||
* This method sets the miscellaneous text of the object to the given value. Optionally,
|
||||
* it can also trigger an update if the $update parameter is set to true.
|
||||
*
|
||||
* @param string|null $misc_text The new miscellaneous text to be set. If null, the miscellaneous text will be cleared.
|
||||
* @param bool $update Optional. Whether to trigger an update after setting the miscellaneous text.
|
||||
* Defaults to false.
|
||||
* @return void
|
||||
*/
|
||||
public function setMiscText(?string $misc_text, bool $update=false): void
|
||||
{
|
||||
$this->misc_text = $misc_text;
|
||||
|
||||
if($update)
|
||||
{
|
||||
$this->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the object.
|
||||
*
|
||||
* This method retrieves the value of the object.
|
||||
*
|
||||
* @return int The value of the object.
|
||||
*/
|
||||
public function getValue(): int
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the object.
|
||||
*
|
||||
* This method sets the value of the object to the given value. Optionally,
|
||||
* it can also trigger an update if the $update parameter is set to true.
|
||||
* If the given value is greater than the maximum value, it will be set to
|
||||
* the maximum value. If the given value is less than 0, it will be set to 0.
|
||||
*
|
||||
* @param int $value The new value to be set.
|
||||
* @param bool $update Optional. Whether to trigger an update after setting the value.
|
||||
* Defaults to false.
|
||||
* @return void
|
||||
*/
|
||||
public function setValue(int $value, bool $update=false): void
|
||||
{
|
||||
if($value > $this->max_value)
|
||||
{
|
||||
$value = $this->max_value;
|
||||
}
|
||||
elseif($value < 0)
|
||||
{
|
||||
$value = 0;
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
|
||||
if($update)
|
||||
{
|
||||
$this->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases the value of the object by the given amount.
|
||||
*
|
||||
* This method increases the current value of the object by the specified amount.
|
||||
* Optionally, it can also trigger an update if the $update parameter is set to true.
|
||||
*
|
||||
* @param int $value The amount by which to increase the value.
|
||||
* @param bool $update Optional. Whether to trigger an update after increasing the value.
|
||||
* Defaults to false.
|
||||
* @return void
|
||||
*/
|
||||
public function increaseValue(int $value=1, bool $update=false): void
|
||||
{
|
||||
$this->setValue($this->value + $value, $update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the value of the object by the given amount.
|
||||
*
|
||||
* This method decreases the value of the object by the specified amount.
|
||||
* Optionally, it can also trigger an update if the $update parameter is set to true.
|
||||
*
|
||||
* @param int $value The amount to decrease the value by.
|
||||
* @param bool $update Optional. Whether to trigger an update after decreasing the value.
|
||||
* Defaults to false.
|
||||
* @return void
|
||||
*/
|
||||
public function decreaseValue(int $value, bool $update=false): void
|
||||
{
|
||||
$this->setValue($this->value - $value, $update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the maximum value.
|
||||
*
|
||||
* This method returns the current maximum value stored in the object.
|
||||
*
|
||||
* @return int The maximum value.
|
||||
*/
|
||||
public function getMaxValue(): int
|
||||
{
|
||||
return $this->max_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum value of the object.
|
||||
*
|
||||
* This method sets the maximum value of the object to the given value. Optionally,
|
||||
* it can also trigger an update if the $update parameter is set to true.
|
||||
* If the given $max_value is negative, it is set to 0.
|
||||
*
|
||||
* @param int $max_value The new maximum value to be set.
|
||||
* @param bool $update Optional. Whether to trigger an update after setting the maximum value.
|
||||
* Defaults to false.
|
||||
* @return void
|
||||
*/
|
||||
public function setMaxValue(int $max_value, bool $update=false): void
|
||||
{
|
||||
if($max_value < 0)
|
||||
{
|
||||
$max_value = 0;
|
||||
}
|
||||
|
||||
$this->max_value = $max_value;
|
||||
|
||||
if($update)
|
||||
{
|
||||
$this->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the object's state.
|
||||
*
|
||||
* This method updates the state of the object based on its current value and max value.
|
||||
* If the current value is greater than or equal to the max value, it prints the information
|
||||
* and progress bar using the renderInformation() and renderProgressBar() methods respectively,
|
||||
* and sets the 'ended' flag to true.
|
||||
* If the current value is less than the max value, it prints the information and progress bar
|
||||
* using the renderInformation() and renderProgressBar() methods respectively, but without
|
||||
* printing a new line.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function update(): void
|
||||
{
|
||||
if($this->auto_end && $this->value >= $this->max_value)
|
||||
{
|
||||
print($this->renderInformation() . $this->renderProgressBar() . "\n");
|
||||
$this->ended = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
print($this->renderInformation() . $this->renderProgressBar() . "\r");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the width of the terminal.
|
||||
*
|
||||
* This method retrieves the width of the terminal by executing the 'tput cols' command.
|
||||
* If the command execution fails or the output is empty, a default width of 70 is returned.
|
||||
*
|
||||
* @return int The width of the terminal.
|
||||
*/
|
||||
private function getTerminalWidth(): int
|
||||
{
|
||||
exec('tput cols', $output, $result);
|
||||
|
||||
if(empty($output[0]) || $result !== 0)
|
||||
{
|
||||
return 70;
|
||||
}
|
||||
|
||||
return (int)$output[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the information to be displayed.
|
||||
*
|
||||
* This method calculates and returns a string containing the rendered information
|
||||
* based on the current state of the object. The information includes the title,
|
||||
* optional miscellaneous text, and any required spacing and formatting for display.
|
||||
*
|
||||
* @return string The rendered information string.
|
||||
*/
|
||||
private function renderInformation(): string
|
||||
{
|
||||
// Resize title and misc if the terminal width is too small
|
||||
$max_text_length = $this->terminal_width - $this->progress_width - 10;
|
||||
|
||||
if(strlen($this->title . ' ' . ($this->misc_text ?? '')) > $max_text_length)
|
||||
{
|
||||
// Calculate the maximum length of title and misc and assign them new truncated values
|
||||
$new_title_length = floor($max_text_length * strlen($this->title) / (strlen($this->title) + strlen($this->misc_text)));
|
||||
|
||||
$title = substr($this->title, 0, $new_title_length);
|
||||
$misc = substr($this->misc_text, 0, ($max_text_length - $new_title_length));
|
||||
}
|
||||
else
|
||||
{
|
||||
$title = $this->title;
|
||||
$misc = $this->misc_text;
|
||||
}
|
||||
|
||||
$spaces = $this->terminal_width - strlen($title) - strlen($misc) - $this->progress_width - 10;
|
||||
$line = $title . str_repeat(' ', $spaces);
|
||||
|
||||
if (!empty($misc))
|
||||
{
|
||||
$line .= ' ' . $misc;
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the progress bar as a string.
|
||||
*
|
||||
* This method calculates the number of hashes and dashes based on the current
|
||||
* progress and width of the progress bar. It also calculates the percentage
|
||||
* completed and formats it with two decimal places at the end. It then constructs
|
||||
* and returns the progress bar string.
|
||||
*
|
||||
* @return string The progress bar string.
|
||||
*/
|
||||
private function renderProgressBar(): string
|
||||
{
|
||||
$hashes_count = round($this->progress_width * $this->value / $this->max_value);
|
||||
$dashes_count = $this->progress_width - $hashes_count;
|
||||
$percent_done = round($this->value * 100 / $this->max_value);
|
||||
|
||||
return ' [' . str_repeat('#', $hashes_count) . str_repeat('-', $dashes_count) . ']' . sprintf('%4s', $percent_done) . '%';
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor for the object.
|
||||
*
|
||||
* This method is automatically called when the object is destroyed. It checks
|
||||
* if the object has already ended and if not, it prints a new line character.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if(!$this->ended)
|
||||
{
|
||||
print(PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue