Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 47 additions & 4 deletions WebFiori/Cli/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,17 @@
public function createProgressBar(int $total = 100): ProgressBar {
return new ProgressBar($this->getOutputStream(), $total);
}

/**
* Display a message only when verbosity is DEBUG (-vv).
*
* @param string $message The message that will be shown.
*/
public function debug(string $message): void {
if ($this->getVerbosityLevel() >= Verbosity::DEBUG) {
$this->printMsg($message, 'Debug', 'gray');
}
}
/**
* Display a message that represents an error.
*
Expand Down Expand Up @@ -712,13 +723,15 @@
* Display a message that represents extra information.
*
* The message will be prefixed with the string 'Info:' in
* blue.
* blue. Suppressed in quiet mode.
*
* @param string $message The message that will be shown.
*
*/
public function info(string $message): void {
$this->printMsg($message, 'Info', 'blue');
if ($this->getVerbosityLevel() >= Verbosity::NORMAL) {
$this->printMsg($message, 'Info', 'blue');
}
}
/**
* Checks if an argument is provided in the CLI or not.
Expand Down Expand Up @@ -1256,13 +1269,16 @@
/**
* Display a message that represents a success status.
*
* The message will be prefixed with the string "Success:" in green.
* The message will be prefixed with the string "Success:" in green.
* Suppressed in quiet mode.
*
* @param string $message The message that will be displayed.
*
*/
public function success(string $message): void {
$this->printMsg($message, 'Success', 'light-green');
if ($this->getVerbosityLevel() >= Verbosity::NORMAL) {
$this->printMsg($message, 'Success', 'light-green');
}
}

/**
Expand Down Expand Up @@ -1422,6 +1438,17 @@

return $this;
}

/**
* Display a message only when verbosity is VERBOSE (-v) or higher.
*
* @param string $message The message that will be shown.
*/
public function verbose(string $message): void {
if ($this->getVerbosityLevel() >= Verbosity::VERBOSE) {
$this->printMsg($message, 'Verbose', 'cyan');
}
}
/**
* Display a message that represents a warning.
*
Expand Down Expand Up @@ -1603,6 +1630,21 @@
return 80;
}

/**
* Returns the current verbosity level from the Runner, or NORMAL as default.
*
* @return int One of the Verbosity constants.
*/
private function getVerbosityLevel(): int {
$owner = $this->getOwner();

Check warning on line 1639 in WebFiori/Cli/Command.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename "$owner" which has the same name as the field declared at line 60.

See more on https://sonarcloud.io/project/issues?id=WebFiori_cli&issues=AZ6-n9G8veQgd5znGK8k&open=AZ6-n9G8veQgd5znGK8k&pullRequest=54

if ($owner !== null) {
return $owner->getVerbosity();
}

return Verbosity::NORMAL;
}

/**
* Checks if ANSI output is enabled for this command.
*
Expand All @@ -1620,6 +1662,7 @@

return $this->isArgProvided('--ansi');
}

private function parseArgsHelper() : bool {
$options = $this->getArgs();
$invalidArgsVals = [];
Expand Down
88 changes: 72 additions & 16 deletions WebFiori/Cli/Runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ class Runner {
*/
private $signalHandler;

/**
* The current verbosity level.
*
* @var int
*/
private $verbosity;

/**
* Creates new instance of the class.
*/
Expand All @@ -132,6 +139,7 @@ public function __construct() {
$this->afterRunPool = [];
$this->signalHandler = null;
$this->shutdownRequested = false;
$this->verbosity = Verbosity::NORMAL;

// Initialize discovery properties
$this->commandDiscovery = null;
Expand All @@ -146,12 +154,25 @@ public function __construct() {
ArgumentOption::OPTIONAL => true,
ArgumentOption::DESCRIPTION => 'Disable ANSI colored output.'
]);
$this->addArg('-q', [
ArgumentOption::OPTIONAL => true,
ArgumentOption::DESCRIPTION => 'Quiet mode. Suppress non-critical output.'
]);
$this->addArg('-v', [
ArgumentOption::OPTIONAL => true,
ArgumentOption::DESCRIPTION => 'Verbose output.'
]);
$this->addArg('-vv', [
ArgumentOption::OPTIONAL => true,
ArgumentOption::DESCRIPTION => 'Debug output (most verbose).'
]);
$this->setBeforeStart(function (Runner $r) {
if (count($r->getArgsVector()) == 0) {
$r->setArgsVector($_SERVER['argv']);
}
$r->checkIsInteractive();
$r->resolveAnsi();
$r->resolveVerbosity();
});
$this->register(new HelpCommand(), ['-h']);
$this->setDefaultCommand('help');
Expand Down Expand Up @@ -586,6 +607,15 @@ public function getSignalHandler(): ?SignalHandler {
return $this->signalHandler;
}

/**
* Returns the current verbosity level.
*
* @return int One of the Verbosity constants.
*/
public function getVerbosity(): int {
return $this->verbosity;
}

/**
* Check if an alias is registered.
*
Expand Down Expand Up @@ -1095,6 +1125,19 @@ public function setSignalHandler(int $signal, callable $handler): Runner {
return $this;
}

/**
* Sets the verbosity level.
*
* @param int $level One of the Verbosity constants.
*
* @return Runner The method returns same instance for chaining.
*/
public function setVerbosity(int $level): Runner {
$this->verbosity = $level;

return $this;
}

/**
* Determines if ANSI output should be used based on environment detection.
*
Expand Down Expand Up @@ -1139,7 +1182,7 @@ public function start(): int {
if ($this->isInteractive()) {
if (in_array('--no-color', $this->getArgsVector())) {
$this->isAnsi = false;
} elseif (in_array('--ansi', $this->getArgsVector())) {
} else if (in_array('--ansi', $this->getArgsVector())) {
$this->isAnsi = true;
}
$this->printMsg('Running in interactive mode.', '>>', 'blue');
Expand Down Expand Up @@ -1228,7 +1271,7 @@ private function readInteractive(): array {

$argsArr = strlen($input) != 0 ? explode(' ', $input) : [];

$argsArr = $this->removeAnsiArgs($argsArr);
$argsArr = $this->removeGlobalFlags($argsArr);

// Preprocess help patterns
$argsArr = $this->preprocessHelpPattern($argsArr);
Expand Down Expand Up @@ -1280,23 +1323,33 @@ private function registerCommandSignalHandlers(Command $c): void {
}
}
}

private function removeCommandSignalHandlers(Command $c): void {
if ($this->signalHandler !== null) {
foreach ($c->getSignalHandlers() as $signal => $handler) {
$this->signalHandler->remove($signal);
}
$c->clearSignalHandlers();
}
}
/**
* Removes --ansi and --no-color flags from an arguments array.
*
* @param array $argsArr The arguments array.
*
* @return array The filtered arguments array.
*/
private function removeAnsiArgs(array $argsArr): array {
private function removeGlobalFlags(array $argsArr): array {
$flags = ['--ansi', '--no-color', '-q', '-v', '-vv'];
$tempArgs = [];

foreach ($argsArr as $argName => $val) {
if (gettype($argName) == 'integer') {
if ($val != '--ansi' && $val != '--no-color') {
if (!in_array($val, $flags)) {
$tempArgs[] = $val;
}
} else {
if ($argName != '--ansi' && $argName != '--no-color') {
if (!in_array($argName, $flags)) {
$tempArgs[$argName] = $val;
}
}
Expand All @@ -1305,21 +1358,24 @@ private function removeAnsiArgs(array $argsArr): array {
return $tempArgs;
}

private function removeCommandSignalHandlers(Command $c): void {
if ($this->signalHandler !== null) {
foreach ($c->getSignalHandlers() as $signal => $handler) {
$this->signalHandler->remove($signal);
}
$c->clearSignalHandlers();
}
}

private function resolveAnsi(): void {
if ($this->outputStream instanceof StdOut) {
$this->isAnsi = self::shouldUseAnsi();
}
}

private function resolveVerbosity(): void {
$args = $this->getArgsVector();

if (in_array('-vv', $args)) {
$this->verbosity = Verbosity::DEBUG;
} else if (in_array('-v', $args)) {
$this->verbosity = Verbosity::VERBOSE;
} else if (in_array('-q', $args)) {
$this->verbosity = Verbosity::QUIET;
}
}

/**
* Run the command line as single run.
*
Expand All @@ -1330,11 +1386,11 @@ private function run(): int {

if (in_array('--no-color', $argsArr)) {
$this->isAnsi = false;
} elseif (in_array('--ansi', $argsArr)) {
} else if (in_array('--ansi', $argsArr)) {
$this->isAnsi = true;
}

$argsArr = $this->removeAnsiArgs($argsArr);
$argsArr = $this->removeGlobalFlags($argsArr);

// Preprocess help patterns for non-interactive mode
$argsArr = $this->preprocessHelpPattern($argsArr);
Expand Down
28 changes: 28 additions & 0 deletions WebFiori/Cli/Verbosity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);
namespace WebFiori\Cli;

/**
* Constants that define the verbosity levels for CLI output.
*
* @author Ibrahim
*/
class Verbosity {
/**
* Debug mode - maximum output detail.
*/
const DEBUG = 3;
/**
* Normal mode - default output level.
*/
const NORMAL = 1;
/**
* Quiet mode - only errors and warnings are shown.
*/
const QUIET = 0;
/**
* Verbose mode - additional diagnostic information.
*/
const VERBOSE = 2;
}
19 changes: 17 additions & 2 deletions tests/WebFiori/Tests/Cli/RunnerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ public function testRunner00() {
$this->assertTrue($runner->addArg('global-arg', [
ArgumentOption::OPTIONAL => true
]));
$this->assertEquals(3, count($runner->getArgs()));
$this->assertEquals(6, count($runner->getArgs()));
$runner->removeArgument('--ansi');
$this->assertEquals(2, count($runner->getArgs()));
$this->assertEquals(5, count($runner->getArgs()));
$this->assertFalse($runner->hasArg('--ansi'));
$runner->register(new Command00());
$this->assertEquals(2, count($runner->getCommands())); // help + super-hero
Expand Down Expand Up @@ -151,6 +151,9 @@ public function testRunner05() {
// Don't register HelpCommand again - it's already automatically registered
$runner->removeArgument('--ansi');
$runner->removeArgument('--no-color');
$runner->removeArgument('-q');
$runner->removeArgument('-v');
$runner->removeArgument('-vv');
$runner->setDefaultCommand('help');
$runner->setInputs([]);
$this->assertEquals(0, $runner->runCommand(null, []));
Expand All @@ -174,6 +177,9 @@ public function testRunner06() {
"Global Arguments:\n",
" --ansi:[Optional] Force the use of ANSI output.\n",
" --no-color:[Optional] Disable ANSI colored output.\n",
" -q:[Optional] Quiet mode. Suppress non-critical output.\n",
" -v:[Optional] Verbose output.\n",
" -vv:[Optional] Debug output (most verbose).\n",
"Available Commands:\n",
" help: Display CLI Help. To display help for specific command, use the argument \"--command\" with this command.\n",
" super-hero: A command to display hero's name.\n"
Expand Down Expand Up @@ -201,6 +207,9 @@ public function testRunner07() {
"\e[1;93mGlobal Arguments:\e[0m\n",
"\e[1;33m --ansi:\e[0m[Optional] Force the use of ANSI output.\n",
"\e[1;33m --no-color:\e[0m[Optional] Disable ANSI colored output.\n",
"\e[1;33m -q:\e[0m[Optional] Quiet mode. Suppress non-critical output.\n",
"\e[1;33m -v:\e[0m[Optional] Verbose output.\n",
"\e[1;33m -vv:\e[0m[Optional] Debug output (most verbose).\n",
"\e[1;93mAvailable Commands:\e[0m\n",
"\e[1;33m help\e[0m: Display CLI Help. To display help for specific command, use the argument \"--command\" with this command.\n",
"\e[1;33m super-hero\e[0m: A command to display hero's name.\n"
Expand Down Expand Up @@ -231,6 +240,9 @@ public function testRunner09() {
$runner = new Runner();
$runner->removeArgument('--ansi');
$runner->removeArgument('--no-color');
$runner->removeArgument('-q');
$runner->removeArgument('-v');
$runner->removeArgument('-vv');
$runner->register(new Command00());
// Don't register HelpCommand - it's automatically registered
$runner->setDefaultCommand('help');
Expand Down Expand Up @@ -337,6 +349,9 @@ public function testRunner13() {
"Global Arguments:\n",
" --ansi:[Optional] Force the use of ANSI output.\n",
" --no-color:[Optional] Disable ANSI colored output.\n",
" -q:[Optional] Quiet mode. Suppress non-critical output.\n",
" -v:[Optional] Verbose output.\n",
" -vv:[Optional] Debug output (most verbose).\n",
"Available Commands:\n",
" help: Display CLI Help. To display help for specific command, use the argument \"--command\" with this command.\n",
" super-hero: A command to display hero's name.\n",
Expand Down
Loading
Loading