|
|
|
@@ -141,6 +141,26 @@ abstract class CliFramework
|
|
|
|
|
/** @var float Script start time for elapsed-time reporting. */
|
|
|
|
|
private float $startTime;
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
|
// Display output — all decorative output goes to stderr
|
|
|
|
|
// =========================================================================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Write decorative/diagnostic output to stderr.
|
|
|
|
|
*
|
|
|
|
|
* All non-data output (banners, progress bars, section headers, status
|
|
|
|
|
* lines, log messages) MUST use this method so that stdout is reserved
|
|
|
|
|
* for machine-readable data. This ensures that shell captures like
|
|
|
|
|
* VERSION=$(php version_read.php --path .)
|
|
|
|
|
* only receive the actual data, not decorative text.
|
|
|
|
|
*
|
|
|
|
|
* @since 04.00.16
|
|
|
|
|
*/
|
|
|
|
|
protected function display(string $text): void
|
|
|
|
|
{
|
|
|
|
|
fwrite(STDERR, $text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
|
// Constructor
|
|
|
|
|
// =========================================================================
|
|
|
|
@@ -326,14 +346,14 @@ abstract class CliFramework
|
|
|
|
|
protected function printHelp(): void
|
|
|
|
|
{
|
|
|
|
|
$w = $this->termWidth();
|
|
|
|
|
echo $this->c(self::C_BOLD . self::C_CYAN, $this->scriptName);
|
|
|
|
|
$this->display($this->c(self::C_BOLD . self::C_CYAN, $this->scriptName));
|
|
|
|
|
if ($this->description !== '') {
|
|
|
|
|
echo ' — ' . $this->description;
|
|
|
|
|
$this->display(' — ' . $this->description);
|
|
|
|
|
}
|
|
|
|
|
echo "\n";
|
|
|
|
|
echo $this->c(self::C_DIM, str_repeat(self::BOX_H, $w)) . "\n\n";
|
|
|
|
|
echo $this->c(self::C_BOLD, 'Usage:') . " php {$this->scriptName}.php [options]\n\n";
|
|
|
|
|
echo $this->c(self::C_BOLD, 'Options:') . "\n";
|
|
|
|
|
$this->display("\n");
|
|
|
|
|
$this->display($this->c(self::C_DIM, str_repeat(self::BOX_H, $w)) . "\n\n");
|
|
|
|
|
$this->display($this->c(self::C_BOLD, 'Usage:') . " php {$this->scriptName}.php [options]\n\n");
|
|
|
|
|
$this->display($this->c(self::C_BOLD, 'Options:') . "\n");
|
|
|
|
|
|
|
|
|
|
$builtIn = [
|
|
|
|
|
'--help' => ['desc' => 'Show this help message', 'default' => null],
|
|
|
|
@@ -348,16 +368,16 @@ abstract class CliFramework
|
|
|
|
|
$hint = ($default !== null && $default !== false)
|
|
|
|
|
? $this->c(self::C_DIM, " (default: {$default})")
|
|
|
|
|
: '';
|
|
|
|
|
printf(
|
|
|
|
|
$this->display(sprintf(
|
|
|
|
|
" %s%-22s%s%s%s\n",
|
|
|
|
|
self::C_CYAN,
|
|
|
|
|
$name,
|
|
|
|
|
self::C_RESET,
|
|
|
|
|
$def['desc'],
|
|
|
|
|
$hint
|
|
|
|
|
);
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
echo "\n";
|
|
|
|
|
$this->display("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
@@ -378,23 +398,23 @@ abstract class CliFramework
|
|
|
|
|
$titleLine = $this->padRight($titleStyled, $inner, strlen($titleRaw));
|
|
|
|
|
$descLine = ($desc !== '') ? $this->padRight(" {$desc}", $inner) : null;
|
|
|
|
|
|
|
|
|
|
echo "\n";
|
|
|
|
|
echo $this->c(
|
|
|
|
|
$this->display("\n");
|
|
|
|
|
$this->display($this->c(
|
|
|
|
|
self::C_CYAN,
|
|
|
|
|
self::BOX_TL . str_repeat(self::BOX_H, $inner) . self::BOX_TR
|
|
|
|
|
) . "\n";
|
|
|
|
|
echo $this->c(self::C_CYAN, self::BOX_V)
|
|
|
|
|
) . "\n");
|
|
|
|
|
$this->display($this->c(self::C_CYAN, self::BOX_V)
|
|
|
|
|
. $this->c(self::C_BOLD, $titleLine)
|
|
|
|
|
. $this->c(self::C_CYAN, self::BOX_V) . "\n";
|
|
|
|
|
. $this->c(self::C_CYAN, self::BOX_V) . "\n");
|
|
|
|
|
if ($descLine !== null) {
|
|
|
|
|
echo $this->c(self::C_CYAN, self::BOX_V)
|
|
|
|
|
$this->display($this->c(self::C_CYAN, self::BOX_V)
|
|
|
|
|
. $this->c(self::C_DIM, $descLine)
|
|
|
|
|
. $this->c(self::C_CYAN, self::BOX_V) . "\n";
|
|
|
|
|
. $this->c(self::C_CYAN, self::BOX_V) . "\n");
|
|
|
|
|
}
|
|
|
|
|
echo $this->c(
|
|
|
|
|
$this->display($this->c(
|
|
|
|
|
self::C_CYAN,
|
|
|
|
|
self::BOX_BL . str_repeat(self::BOX_H, $inner) . self::BOX_BR
|
|
|
|
|
) . "\n\n";
|
|
|
|
|
) . "\n\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Print the dry-run notice box. */
|
|
|
|
@@ -403,18 +423,18 @@ abstract class CliFramework
|
|
|
|
|
$w = min($this->termWidth(), 70);
|
|
|
|
|
$msg = ' ' . self::ICON_DRY . ' DRY-RUN MODE — no changes will be written ';
|
|
|
|
|
$row = $this->padRight($msg, $w - 2);
|
|
|
|
|
echo $this->c(
|
|
|
|
|
$this->display($this->c(
|
|
|
|
|
self::C_YELLOW . self::C_BOLD,
|
|
|
|
|
self::BOX_TL . str_repeat(self::BOX_H, $w - 2) . self::BOX_TR
|
|
|
|
|
) . "\n";
|
|
|
|
|
echo $this->c(
|
|
|
|
|
) . "\n");
|
|
|
|
|
$this->display($this->c(
|
|
|
|
|
self::C_YELLOW . self::C_BOLD,
|
|
|
|
|
self::BOX_V . $row . self::BOX_V
|
|
|
|
|
) . "\n";
|
|
|
|
|
echo $this->c(
|
|
|
|
|
) . "\n");
|
|
|
|
|
$this->display($this->c(
|
|
|
|
|
self::C_YELLOW . self::C_BOLD,
|
|
|
|
|
self::BOX_BL . str_repeat(self::BOX_H, $w - 2) . self::BOX_BR
|
|
|
|
|
) . "\n\n";
|
|
|
|
|
) . "\n\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
@@ -435,11 +455,11 @@ abstract class CliFramework
|
|
|
|
|
$w = $this->termWidth();
|
|
|
|
|
$text = " {$title} ";
|
|
|
|
|
$fill = max(0, $w - strlen($text) - 4);
|
|
|
|
|
echo "\n";
|
|
|
|
|
echo $this->c(
|
|
|
|
|
$this->display("\n");
|
|
|
|
|
$this->display($this->c(
|
|
|
|
|
self::C_CYAN,
|
|
|
|
|
str_repeat(self::BOX_H, 2) . $text . str_repeat(self::BOX_H, $fill)
|
|
|
|
|
) . "\n\n";
|
|
|
|
|
) . "\n\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Print a plain horizontal divider. */
|
|
|
|
@@ -449,7 +469,7 @@ abstract class CliFramework
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$this->clearProgress();
|
|
|
|
|
echo $this->c(self::C_DIM, str_repeat(self::BOX_H, $this->termWidth())) . "\n";
|
|
|
|
|
$this->display($this->c(self::C_DIM, str_repeat(self::BOX_H, $this->termWidth())) . "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
@@ -495,11 +515,7 @@ abstract class CliFramework
|
|
|
|
|
|
|
|
|
|
$line = "{$ts} {$icon} {$badge} {$text}\n";
|
|
|
|
|
|
|
|
|
|
if ($level === 'ERROR') {
|
|
|
|
|
fwrite(STDERR, $line);
|
|
|
|
|
} else {
|
|
|
|
|
echo $line;
|
|
|
|
|
}
|
|
|
|
|
$this->display($line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Log a success message. */
|
|
|
|
@@ -564,7 +580,7 @@ abstract class CliFramework
|
|
|
|
|
? ' ' . $this->c(self::C_DIM, "— {$detail}")
|
|
|
|
|
: '';
|
|
|
|
|
|
|
|
|
|
echo ' ' . $this->c($color . self::C_BOLD, $icon) . ' ' . $label . $suffix . "\n";
|
|
|
|
|
$this->display(' ' . $this->c($color . self::C_BOLD, $icon) . ' ' . $label . $suffix . "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
@@ -601,10 +617,10 @@ abstract class CliFramework
|
|
|
|
|
$line = " [{$bar}] {$percent} {$counter}{$suffix}";
|
|
|
|
|
|
|
|
|
|
if ($newline) {
|
|
|
|
|
echo "\r{$line}\n";
|
|
|
|
|
$this->display("\r{$line}\n");
|
|
|
|
|
$this->progressActive = false;
|
|
|
|
|
} else {
|
|
|
|
|
echo "\r{$line}";
|
|
|
|
|
$this->display("\r{$line}");
|
|
|
|
|
$this->progressActive = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -613,7 +629,7 @@ abstract class CliFramework
|
|
|
|
|
protected function clearProgress(): void
|
|
|
|
|
{
|
|
|
|
|
if ($this->progressActive) {
|
|
|
|
|
echo "\r" . str_repeat(' ', $this->termWidth()) . "\r";
|
|
|
|
|
$this->display("\r" . str_repeat(' ', $this->termWidth()) . "\r");
|
|
|
|
|
$this->progressActive = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -644,8 +660,8 @@ abstract class CliFramework
|
|
|
|
|
$maxKey = max(array_map('strlen', array_keys($rows)));
|
|
|
|
|
$inner = $maxKey + 20;
|
|
|
|
|
|
|
|
|
|
echo "\n";
|
|
|
|
|
echo $this->c($color, self::BOX_TL . str_repeat(self::BOX_H, $inner) . self::BOX_TR) . "\n";
|
|
|
|
|
$this->display("\n");
|
|
|
|
|
$this->display($this->c($color, self::BOX_TL . str_repeat(self::BOX_H, $inner) . self::BOX_TR) . "\n");
|
|
|
|
|
|
|
|
|
|
foreach ($rows as $label => $value) {
|
|
|
|
|
$valStr = (string) $value;
|
|
|
|
@@ -653,10 +669,10 @@ abstract class CliFramework
|
|
|
|
|
$padding = $inner - strlen($label) - $valVis - 4;
|
|
|
|
|
$row = ' ' . $this->c(self::C_BOLD, $label)
|
|
|
|
|
. str_repeat(' ', max(1, $padding)) . $valStr . ' ';
|
|
|
|
|
echo $this->c($color, self::BOX_V) . $row . $this->c($color, self::BOX_V) . "\n";
|
|
|
|
|
$this->display($this->c($color, self::BOX_V) . $row . $this->c($color, self::BOX_V) . "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
echo $this->c($color, self::BOX_BL . str_repeat(self::BOX_H, $inner) . self::BOX_BR) . "\n\n";
|
|
|
|
|
$this->display($this->c($color, self::BOX_BL . str_repeat(self::BOX_H, $inner) . self::BOX_BR) . "\n\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@@ -702,7 +718,7 @@ abstract class CliFramework
|
|
|
|
|
$this->clearProgress();
|
|
|
|
|
$badge = $this->c(self::C_BOLD . self::C_MAGENTA, "Step {$current}/{$total}");
|
|
|
|
|
$arrow = $this->c(self::C_DIM, self::ICON_INFO);
|
|
|
|
|
echo "\n{$badge} {$arrow} {$title}\n";
|
|
|
|
|
$this->display("\n{$badge} {$arrow} {$title}\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =========================================================================
|
|
|
|
@@ -964,13 +980,13 @@ abstract class CliFramework
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Header.
|
|
|
|
|
echo $sep . "\n";
|
|
|
|
|
$this->display($sep . "\n");
|
|
|
|
|
$headerLine = '|';
|
|
|
|
|
foreach ($headers as $i => $h) {
|
|
|
|
|
$headerLine .= ' ' . $this->c(self::C_BOLD, str_pad($h, $widths[$i])) . ' |';
|
|
|
|
|
}
|
|
|
|
|
echo $headerLine . "\n";
|
|
|
|
|
echo $sep . "\n";
|
|
|
|
|
$this->display($headerLine . "\n");
|
|
|
|
|
$this->display($sep . "\n");
|
|
|
|
|
|
|
|
|
|
// Rows.
|
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
@@ -978,9 +994,9 @@ abstract class CliFramework
|
|
|
|
|
foreach ($row as $i => $cell) {
|
|
|
|
|
$line .= ' ' . str_pad((string) $cell, $widths[$i]) . ' |';
|
|
|
|
|
}
|
|
|
|
|
echo $line . "\n";
|
|
|
|
|
$this->display($line . "\n");
|
|
|
|
|
}
|
|
|
|
|
echo $sep . "\n";
|
|
|
|
|
$this->display($sep . "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|