summaryrefslogtreecommitdiffstats
path: root/vendor/gipfl/cli/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:23:16 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:23:16 +0000
commit3e97c51418e6d27e9a81906f347fcb7c78e27d4f (patch)
treeee596ce1bc9840661386f96f9b8d1f919a106317 /vendor/gipfl/cli/src
parentInitial commit. (diff)
downloadicingaweb2-module-incubator-3e97c51418e6d27e9a81906f347fcb7c78e27d4f.tar.xz
icingaweb2-module-incubator-3e97c51418e6d27e9a81906f347fcb7c78e27d4f.zip
Adding upstream version 0.20.0.upstream/0.20.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gipfl/cli/src')
-rw-r--r--vendor/gipfl/cli/src/AnsiScreen.php128
-rw-r--r--vendor/gipfl/cli/src/Process.php141
-rw-r--r--vendor/gipfl/cli/src/Screen.php190
-rw-r--r--vendor/gipfl/cli/src/Spinner.php69
-rw-r--r--vendor/gipfl/cli/src/Tty.php132
-rw-r--r--vendor/gipfl/cli/src/TtyMode.php95
6 files changed, 755 insertions, 0 deletions
diff --git a/vendor/gipfl/cli/src/AnsiScreen.php b/vendor/gipfl/cli/src/AnsiScreen.php
new file mode 100644
index 0000000..2ae3f40
--- /dev/null
+++ b/vendor/gipfl/cli/src/AnsiScreen.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace gipfl\Cli;
+
+use InvalidArgumentException;
+
+/**
+ * Screen implementation for screens with ANSI escape code support
+ *
+ * @see http://en.wikipedia.org/wiki/ANSI_escape_code
+ */
+class AnsiScreen extends Screen
+{
+ const FG_COLORS = [
+ 'black' => '30',
+ 'darkgray' => '1;30',
+ 'red' => '31',
+ 'lightred' => '1;31',
+ 'green' => '32',
+ 'lightgreen' => '1;32',
+ 'brown' => '33',
+ 'yellow' => '1;33',
+ 'blue' => '34',
+ 'lightblue' => '1;34',
+ 'purple' => '35',
+ 'lightpurple' => '1;35',
+ 'cyan' => '36',
+ 'lightcyan' => '1;36',
+ 'lightgray' => '37',
+ 'white' => '1;37',
+ ];
+
+ const BG_COLORS = [
+ 'black' => '40',
+ 'red' => '41',
+ 'green' => '42',
+ 'brown' => '43',
+ 'blue' => '44',
+ 'purple' => '45',
+ 'cyan' => '46',
+ 'lightgray' => '47',
+ ];
+
+ /**
+ * Remove all ANSI escape codes from a given string
+ * @param $string
+ * @return string|string[]|null
+ */
+ public function stripAnsiCodes($string)
+ {
+ return \preg_replace('/\e\[?.*?[@-~]/', '', $string);
+ }
+
+ public function clear()
+ {
+ return "\033[2J" // Clear the whole screen
+ . "\033[1;1H" // Move the cursor to row 1, column 1
+ . "\033[1S"; // Scroll whole page up by 1 line (why?)
+ }
+
+ public function colorize($text, $fgColor = null, $bgColor = null)
+ {
+ return $this->startColor($fgColor, $bgColor)
+ . $text
+ . "\033[0m"; // Reset color codes
+ }
+
+ public function strlen($string)
+ {
+ return parent::strlen($this->stripAnsiCodes($string));
+ }
+
+ public function underline($text)
+ {
+ return "\033[4m"
+ . $text
+ . "\033[0m"; // Reset color codes
+ }
+
+ protected function fgColor($color)
+ {
+ if (! \array_key_exists($color, static::FG_COLORS)) {
+ throw new InvalidArgumentException(
+ "There is no such foreground color: $color"
+ );
+ }
+
+ return static::FG_COLORS[$color];
+ }
+
+ protected function bgColor($color)
+ {
+ if (! \array_key_exists($color, static::BG_COLORS)) {
+ throw new InvalidArgumentException(
+ "There is no such background color: $color"
+ );
+ }
+
+ return static::BG_COLORS[$color];
+ }
+
+ protected function startColor($fgColor = null, $bgColor = null)
+ {
+ $parts = [];
+ if ($fgColor !== null
+ && $bgColor !== null
+ && ! \array_key_exists($bgColor, static::BG_COLORS)
+ && \array_key_exists($bgColor, static::FG_COLORS)
+ && \array_key_exists($fgColor, static::BG_COLORS)
+ ) {
+ $parts[] = '7'; // reverse video, negative image
+ $parts[] = $this->bgColor($fgColor);
+ $parts[] = $this->fgColor($bgColor);
+ } else {
+ if ($fgColor !== null) {
+ $parts[] = $this->fgColor($fgColor);
+ }
+ if ($bgColor !== null) {
+ $parts[] = $this->bgColor($bgColor);
+ }
+ }
+ if (empty($parts)) {
+ return '';
+ }
+
+ return "\033[" . \implode(';', $parts) . 'm';
+ }
+}
diff --git a/vendor/gipfl/cli/src/Process.php b/vendor/gipfl/cli/src/Process.php
new file mode 100644
index 0000000..45c67b5
--- /dev/null
+++ b/vendor/gipfl/cli/src/Process.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace gipfl\Cli;
+
+class Process
+{
+ /** @var string|null */
+ protected static $initialCwd;
+
+ /**
+ * Set the command/process title for this process
+ *
+ * @param $title
+ */
+ public static function setTitle($title)
+ {
+ if (function_exists('cli_set_process_title')) {
+ \cli_set_process_title($title);
+ }
+ }
+
+ /**
+ * Replace this process with a new instance of itself by executing the
+ * very same binary with the very same parameters
+ */
+ public static function restart()
+ {
+ // _ is only available when executed via shell
+ $binary = static::getEnv('_');
+ $argv = $_SERVER['argv'];
+ if (\strlen($binary) === 0) {
+ // Problem: this doesn't work if we changed working directory and
+ // called the binary with a relative path. Something that doesn't
+ // happen when started as a daemon, and when started manually we
+ // should have $_ from our shell.
+ $binary = static::absoluteFilename(\array_shift($argv));
+ } else {
+ \array_shift($argv);
+ }
+ \pcntl_exec($binary, $argv, static::getEnv());
+ }
+
+ /**
+ * Get the given ENV variable, null if not available
+ *
+ * Returns an array with all ENV variables if no $key is given
+ *
+ * @param string|null $key
+ * @return array|string|null
+ */
+ public static function getEnv($key = null)
+ {
+ if ($key !== null) {
+ return \getenv($key);
+ }
+
+ if (PHP_VERSION_ID > 70100) {
+ return \getenv();
+ } else {
+ $env = $_SERVER;
+ unset($env['argv'], $env['argc']);
+
+ return $env;
+ }
+ }
+
+ /**
+ * Get the path to the executed binary when starting this command
+ *
+ * This fails if we changed working directory and called the binary with a
+ * relative path. Something that doesn't happen when started as a daemon.
+ * When started manually we should have $_ from our shell.
+ *
+ * To be always on the safe side please call Process::getInitialCwd() once
+ * after starting your process and before switching directory. That way we
+ * preserve our initial working directory.
+ *
+ * @return mixed|string
+ */
+ public static function getBinaryPath()
+ {
+ if (isset($_SERVER['_'])) {
+ return $_SERVER['_'];
+ } else {
+ global $argv;
+
+ return static::absoluteFilename($argv[0]);
+ }
+ }
+
+ /**
+ * The working directory as given by getcwd() the very first time we
+ * called this method
+ *
+ * @return string
+ */
+ public static function getInitialCwd()
+ {
+ if (self::$initialCwd === null) {
+ self::$initialCwd = \getcwd();
+ }
+
+ return self::$initialCwd;
+ }
+
+ /**
+ * Returns the absolute filename for the given file
+ *
+ * If relative, it's calculated in relation to the given working directory.
+ * The current working directory is being used if null is given.
+ *
+ * @param $filename
+ * @param null $cwd
+ * @return string
+ */
+ public static function absoluteFilename($filename, $cwd = null)
+ {
+ $filename = \str_replace(
+ DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR,
+ DIRECTORY_SEPARATOR,
+ $filename
+ );
+ if ($filename[0] === '.') {
+ $filename = ($cwd ?: \getcwd()) . DIRECTORY_SEPARATOR . $filename;
+ }
+ $parts = \explode(DIRECTORY_SEPARATOR, $filename);
+ $result = [];
+ foreach ($parts as $part) {
+ if ($part === '.') {
+ continue;
+ }
+ if ($part === '..') {
+ \array_pop($result);
+ continue;
+ }
+ $result[] = $part;
+ }
+
+ return \implode(DIRECTORY_SEPARATOR, $result);
+ }
+}
diff --git a/vendor/gipfl/cli/src/Screen.php b/vendor/gipfl/cli/src/Screen.php
new file mode 100644
index 0000000..cb05a3f
--- /dev/null
+++ b/vendor/gipfl/cli/src/Screen.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace gipfl\Cli;
+
+/**
+ * Base class providing minimal CLI Screen functionality. While classes
+ * extending this one (read: AnsiScreen) should implement all the fancy cool
+ * things, this base class makes sure that your code will still run in
+ * environments with no ANSI or similar support
+ *
+ * ```php
+ * $screen = Screen::instance();
+ * echo $screen->center($screen->underline('Hello world'));
+ * ```
+ */
+class Screen
+{
+ protected $isUtf8;
+
+ /**
+ * Get a new Screen instance.
+ *
+ * For now this is limited to either a very basic Screen implementation as
+ * a fall-back or an AnsiScreen implementation with more functionality
+ *
+ * @return AnsiScreen|Screen
+ */
+ public static function factory()
+ {
+ if (! defined('STDOUT')) {
+ return new Screen();
+ }
+ if (\function_exists('posix_isatty') && \posix_isatty(STDOUT)) {
+ return new AnsiScreen();
+ } else {
+ return new Screen();
+ }
+ }
+
+ /**
+ * Center the given string horizontally on the current screen
+ *
+ * @param $string
+ * @return string
+ */
+ public function center($string)
+ {
+ $len = $this->strlen($string);
+ $width = (int) \floor(($this->getColumns() + $len) / 2) - $len;
+
+ return \str_repeat(' ', $width) . $string;
+ }
+
+ /**
+ * Clear the screen
+ *
+ * Impossible for non-ANSI screens, so let's output a newline for now
+ *
+ * @return string
+ */
+ public function clear()
+ {
+ return "\n";
+ }
+
+ /**
+ * Colorize the given text. Has no effect on a basic Screen, all colors
+ * will be accepted. It's prefectly legal to provide background or foreground
+ * only
+ *
+ * Returns the very same string, eventually enriched with related ANSI codes
+ *
+ * @param $text
+ * @param null $fgColor
+ * @param null $bgColor
+ *
+ * @return mixed
+ */
+ public function colorize($text, $fgColor = null, $bgColor = null)
+ {
+ return $text;
+ }
+
+ /**
+ * Generate $count newline characters
+ *
+ * @param int $count
+ * @return string
+ */
+ public function newlines($count = 1)
+ {
+ return \str_repeat(PHP_EOL, $count);
+ }
+
+ /**
+ * Calculate the visible length of a given string. While this is simple on
+ * a non-ANSI-screen, such implementation will be required to strip control
+ * characters to get the correct result
+ *
+ * @param $string
+ * @return int
+ */
+ public function strlen($string)
+ {
+ if ($this->isUtf8()) {
+ return \mb_strlen($string, 'UTF-8');
+ } else {
+ return \strlen($string);
+ }
+ }
+
+ /**
+ * Underline the given text - if possible
+ *
+ * @return string
+ */
+ public function underline($text)
+ {
+ return $text;
+ }
+
+ /**
+ * Get the number of currently available columns. Please note that this
+ * might chance at any time while your program is running
+ *
+ * @return int
+ */
+ public function getColumns()
+ {
+ $cols = (int) \getenv('COLUMNS');
+ if (! $cols) {
+ // stty -a ?
+ $cols = (int) \exec('tput cols');
+ }
+ if (! $cols) {
+ $cols = 80;
+ }
+
+ return $cols;
+ }
+
+ /**
+ * Get the number of currently available rows. Please note that this
+ * might chance at any time while your program is running
+ *
+ * @return int
+ */
+ public function getRows()
+ {
+ $rows = (int) \getenv('ROWS');
+ if (! $rows) {
+ // stty -a ?
+ $rows = (int) \exec('tput lines');
+ }
+ if (! $rows) {
+ $rows = 25;
+ }
+
+ return $rows;
+ }
+
+ /**
+ * Whether we're on a UTF-8 screen. We assume latin1 otherwise, there is no
+ * support for additional encodings
+ *
+ * @return bool
+ */
+ public function isUtf8()
+ {
+ if ($this->isUtf8 === null) {
+ // null should equal 0 here, however seems to equal '' on some systems:
+ $current = \setlocale(LC_ALL, 0);
+
+ $parts = explode(';', $current);
+ $lc_parts = [];
+ foreach ($parts as $part) {
+ if (\strpos($part, '=') === false) {
+ continue;
+ }
+ list($key, $val) = explode('=', $part, 2);
+ $lc_parts[$key] = $val;
+ }
+
+ $this->isUtf8 = \array_key_exists('LC_CTYPE', $lc_parts)
+ && \preg_match('~\.UTF-8$~i', $lc_parts['LC_CTYPE']);
+ }
+
+ return $this->isUtf8;
+ }
+}
diff --git a/vendor/gipfl/cli/src/Spinner.php b/vendor/gipfl/cli/src/Spinner.php
new file mode 100644
index 0000000..b949526
--- /dev/null
+++ b/vendor/gipfl/cli/src/Spinner.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace gipfl\Cli;
+
+use React\EventLoop\LoopInterface;
+use React\Promise\Deferred;
+use React\Promise\ExtendedPromiseInterface;
+
+class Spinner
+{
+ const ASCII_SLASH = ['/', '-', '\\', '|'];
+ const ASCII_BOUNCING_CIRCLE = ['.', 'o', 'O', '°', 'O', 'o'];
+ const ROTATING_HALF_CIRCLE = ['◑', '◒', '◐', '◓'];
+ const ROTATING_EARTH = ['🌎', '🌏', '🌍'];
+ const ROTATING_MOON = ['🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘'];
+ const UP_DOWN_BAR = [' ', '_', '▁', '▃', '▄', '▅', '▆', '▇', '▆', '▅', '▄', '▃', '▁'];
+ const CLOCK = ['🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚', '🕛'];
+ const WAVING_DOTS = ['⢄', '⢂', '⢁', '⡁', '⡈', '⡐', '⡠', '⡐', '⡈', '⡁', '⢁', '⢂'];
+ const ROTATING_DOTS = ['⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽', '⣾'];
+
+ /** @var LoopInterface */
+ protected $loop;
+
+ protected $frames;
+
+ protected $frame = -1;
+
+ protected $count;
+
+ protected $delay;
+
+ public function __construct(LoopInterface $loop, array $frames = self::ASCII_SLASH)
+ {
+ $this->loop = $loop;
+ $this->frames = $frames;
+ $this->count = \count($frames);
+ $this->delay = ((int) (2 * 100 / $this->count)) / 100;
+ }
+
+ protected function getNextFrame()
+ {
+ $first = $this->frame === -1;
+ $this->frame++;
+ if ($this->frame >= $this->count) {
+ $this->frame = 0;
+ }
+
+ return $this->frames[$this->frame];
+ }
+
+ public function spinWhile(ExtendedPromiseInterface $promise, callable $renderer)
+ {
+ $next = function () use ($renderer) {
+ $renderer($this->getNextFrame());
+ };
+ $spinTimer = $this->loop->addPeriodicTimer($this->delay, $next);
+ $deferred = new Deferred(function () use ($spinTimer) {
+ $this->loop->cancelTimer($spinTimer);
+ });
+ $this->loop->futureTick($next);
+ $wait = $deferred->promise();
+ $cancel = function () use ($wait) {
+ $wait->cancel();
+ };
+ $promise->otherwise($cancel)->then($cancel);
+
+ return $promise;
+ }
+}
diff --git a/vendor/gipfl/cli/src/Tty.php b/vendor/gipfl/cli/src/Tty.php
new file mode 100644
index 0000000..efe5924
--- /dev/null
+++ b/vendor/gipfl/cli/src/Tty.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace gipfl\Cli;
+
+use InvalidArgumentException;
+use React\EventLoop\LoopInterface;
+use React\Stream\ReadableResourceStream;
+use React\Stream\WritableResourceStream;
+use RuntimeException;
+use function defined;
+use function fstat;
+use function function_exists;
+use function is_bool;
+use function is_resource;
+use function is_string;
+use function posix_isatty;
+use function register_shutdown_function;
+use function stream_isatty;
+use function stream_set_blocking;
+use function strlen;
+use function var_export;
+
+class Tty
+{
+ protected $stdin;
+
+ protected $stdout;
+
+ protected $loop;
+
+ protected $echo = true;
+
+ /** @var TtyMode */
+ protected $ttyMode;
+
+ public function __construct(LoopInterface $loop)
+ {
+ $this->loop = $loop;
+ register_shutdown_function([$this, 'restore']);
+ $loop->futureTick(function () {
+ $this->initialize();
+ });
+ }
+
+ public function setEcho($echo)
+ {
+ if (! is_bool($echo) && ! is_string($echo) && strlen($echo) !== 1) {
+ throw new InvalidArgumentException(
+ "\$echo must be boolean or a single character, got " . var_export($echo, 1)
+ );
+ }
+ $this->echo = $echo;
+ if ($this->ttyMode) {
+ if ($echo) {
+ $this->ttyMode->enableFeature('echo');
+ } else {
+ $this->ttyMode->disableFeature('echo');
+ }
+ }
+
+ return $this;
+ }
+
+ public function stdin()
+ {
+ if ($this->stdin === null) {
+ $this->assertValidStdin();
+ $this->stdin = new ReadableResourceStream(STDIN, $this->loop);
+ }
+
+ return $this->stdin;
+ }
+
+ protected function hasStdin()
+ {
+ return defined('STDIN') && is_resource(STDIN) && fstat(STDIN) !== false;
+ }
+
+ protected function assertValidStdin()
+ {
+ if (! $this->hasStdin()) {
+ throw new RuntimeException('I have no STDIN');
+ }
+ }
+
+ public function stdout()
+ {
+ if ($this->stdout === null) {
+ $this->assertValidStdout();
+ $this->stdout = new WritableResourceStream(STDOUT, $this->loop);
+ }
+
+ return $this->stdout;
+ }
+
+ protected function hasStdout()
+ {
+ return defined('STDOUT') && is_resource(STDOUT) && fstat(STDOUT) !== false;
+ }
+
+ protected function assertValidStdout()
+ {
+ if (! $this->hasStdout()) {
+ throw new RuntimeException('I have no STDOUT');
+ }
+ }
+
+ protected function initialize()
+ {
+ $this->ttyMode = new TtyMode();
+ $this->ttyMode->setPreferredMode($this->echo);
+ }
+
+ public static function isSupported()
+ {
+ if (PHP_VERSION_ID >= 70200) {
+ return stream_isatty(STDIN);
+ } elseif (function_exists('posix_isatty')) {
+ return posix_isatty(STDIN);
+ } else {
+ return false;
+ }
+ }
+
+ public function restore()
+ {
+ if ($this->hasStdin()) {
+ // ReadableResourceStream sets blocking to false, let's restore this
+ stream_set_blocking(STDIN, true);
+ }
+ }
+}
diff --git a/vendor/gipfl/cli/src/TtyMode.php b/vendor/gipfl/cli/src/TtyMode.php
new file mode 100644
index 0000000..8b9c884
--- /dev/null
+++ b/vendor/gipfl/cli/src/TtyMode.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace gipfl\Cli;
+
+use function escapeshellarg;
+use function register_shutdown_function;
+use function rtrim;
+use function shell_exec;
+
+class TtyMode
+{
+ protected $originalMode;
+
+ public function enableCanonicalMode()
+ {
+ $this->enableFeature('icanon');
+
+ return $this;
+ }
+
+ public function disableCanonicalMode()
+ {
+ $this->disableFeature('icanon');
+
+ return $this;
+ }
+
+ public function enableFeature(...$feature)
+ {
+ $this->preserve();
+ $cmd = 'stty ';
+ foreach ($feature as $f) {
+ $cmd .= escapeshellarg($f);
+ }
+
+ shell_exec($cmd);
+ }
+
+ public function disableFeature(...$feature)
+ {
+ $this->preserve();
+ $cmd = 'stty';
+ foreach ($feature as $f) {
+ $cmd .= ' -' . escapeshellarg($f);
+ }
+
+ shell_exec($cmd);
+ }
+
+ public function getCurrentMode()
+ {
+ return rtrim(shell_exec('stty -g'), PHP_EOL);
+ }
+
+ /**
+ * Helper allowing to call stty only once for the mose used flags, icanon and echo
+ * @param bool $echo
+ * @return $this
+ */
+ public function setPreferredMode($echo = true)
+ {
+ $this->preserve();
+ if ($echo) {
+ $this->disableFeature('icanon');
+ } else {
+ $this->disableFeature('icanon', 'echo');
+ }
+
+ return $this;
+ }
+
+ /**
+ * @internal
+ */
+ public function preserve($force = false)
+ {
+ if ($force || $this->originalMode === null) {
+ $this->originalMode = $this->getCurrentMode();
+ register_shutdown_function([$this, 'restore']);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @internal
+ */
+ public function restore()
+ {
+ if ($this->originalMode) {
+ shell_exec('stty ' . escapeshellarg($this->originalMode));
+ $this->originalMode = null;
+ }
+ }
+}