diff --git a/README.md b/README.md
index 962aa1a..1c58f7a 100644
--- a/README.md
+++ b/README.md
@@ -553,7 +553,7 @@ Show::table($data, 'a table', $opts);
> 渲染效果请看下面的预览
-![table-show](images/table-show.png)
+![table-show](docs/screenshots/table-show.png)
### 快速的渲染一个帮助信息面板
diff --git a/composer.json b/composer.json
index 260a0f4..1e0cea8 100644
--- a/composer.json
+++ b/composer.json
@@ -26,6 +26,6 @@
}
},
"suggest": {
- "inhere/simple-print-tool": "Very lightweight data printing tools"
+ "symfony/process": "php process operation"
}
}
diff --git a/images/table-show.png b/docs/screenshots/table-show.png
similarity index 100%
rename from images/table-show.png
rename to docs/screenshots/table-show.png
diff --git a/examples/Controllers/HomeController.php b/examples/Controllers/HomeController.php
index 8f64b31..d9581fc 100644
--- a/examples/Controllers/HomeController.php
+++ b/examples/Controllers/HomeController.php
@@ -2,13 +2,14 @@
namespace Inhere\Console\Examples\Controllers;
-use Inhere\Console\Components\AnsiCode;
+use Inhere\Console\Components\Terminal;
use Inhere\Console\Components\ArtFont;
use Inhere\Console\Components\Download;
use Inhere\Console\Controller;
use Inhere\Console\IO\Input;
use Inhere\Console\Utils\Helper;
use Inhere\Console\Utils\Interact;
+use Inhere\Console\Utils\ProgressBar;
use Inhere\Console\Utils\Show;
/**
@@ -157,6 +158,8 @@ public function spinnerCommand()
Show::spinner();
usleep(100);
}
+
+ Show::spinner('Done', true);
}
/**
@@ -164,16 +167,18 @@ public function spinnerCommand()
*/
public function pendingCommand()
{
- $total = 5000;
+ $total = 8000;
while ($total--) {
- Show::spinner();
- usleep(100);
+ Show::pending();
+ usleep(200);
}
+
+ Show::pending('Done', true);
}
/**
- * a progress bar example show
+ * a progress bar example show, by Show::progressBar()
* @options
* --type the progress type, allow: bar,txt. txt
* --done-char the done show char. =
@@ -212,6 +217,26 @@ public function progressCommand($input)
return 0;
}
+ /**
+ * a progress bar example show, by class ProgressBar
+ * @throws \LogicException
+ */
+ public function prgCommand()
+ {
+ $i = 0;
+ $total = 120;
+ $bar = new ProgressBar();
+ $bar->start(120);
+
+ while ($i <= $total) {
+ $bar->advance();
+ usleep(50000);
+ $i++;
+ }
+
+ $bar->finish();
+ }
+
/**
* output format message: title
*/
@@ -592,39 +617,36 @@ public function downCommand()
}
/**
- * This is a demo for show cursor move on the screen
+ * This is a demo for show cursor move on the Terminal screen
*/
public function cursorCommand()
{
$this->write('hello, this in ' . __METHOD__);
-
- // $this->output->panel($_SERVER, 'Server information', '');
-
$this->write('this is a message text.', false);
sleep(1);
- AnsiCode::make()->cursor(AnsiCode::CURSOR_BACKWARD, 6);
+ Terminal::make()->cursor(Terminal::CURSOR_BACKWARD, 6);
sleep(1);
- AnsiCode::make()->cursor(AnsiCode::CURSOR_FORWARD, 3);
+ Terminal::make()->cursor(Terminal::CURSOR_FORWARD, 3);
sleep(1);
- AnsiCode::make()->cursor(AnsiCode::CURSOR_BACKWARD, 2);
+ Terminal::make()->cursor(Terminal::CURSOR_BACKWARD, 2);
sleep(2);
- AnsiCode::make()->screen(AnsiCode::CLEAR_LINE, 3);
+ Terminal::make()->screen(Terminal::CLEAR_LINE, 3);
$this->write('after 2s scroll down 3 row.');
sleep(2);
- AnsiCode::make()->screen(AnsiCode::SCROLL_DOWN, 3);
+ Terminal::make()->screen(Terminal::SCROLL_DOWN, 3);
$this->write('after 3s clear screen.');
sleep(3);
- AnsiCode::make()->screen(AnsiCode::CLEAR);
+ Terminal::make()->screen(Terminal::CLEAR);
}
}
diff --git a/examples/Controllers/ProcessController.php b/examples/Controllers/ProcessController.php
new file mode 100644
index 0000000..7639fcd
--- /dev/null
+++ b/examples/Controllers/ProcessController.php
@@ -0,0 +1,22 @@
+command(DemoCommand::class);
$app->command('exam', function (Input $in, Output $out) {
$cmd = $in->getCommand();
$out->info('hello, this is a test command: ' . $cmd);
-});
+}, 'a description message');
$app->command('test', TestCommand::class, [
'aliases' => ['t']
]);
-$app->command('prg', function () {
- $i = 0;
- $total = 120;
- $bar = new ProgressBar();
- $bar->start(120);
-
- while ($i <= $total) {
- $bar->advance();
- usleep(50000);
- $i++;
- }
-
- $bar->finish();
-
-}, 'a description message');
+$app->controller(PharController::class);
$app->controller('home', HomeController::class, [
'aliases' => ['h']
]);
-$app->controller(PharController::class);
+$app->controller(ProcessController::class, null, [
+ 'prc'
+]);
diff --git a/src/Components/Notify/Pending.php b/src/Components/Notify/Pending.php
index 0e393bf..314d3dc 100644
--- a/src/Components/Notify/Pending.php
+++ b/src/Components/Notify/Pending.php
@@ -16,5 +16,6 @@
*/
class Pending extends NotifyMessage
{
-
+ /** @var int Speed value. allow 1 - 100 */
+ private $speed = 20;
}
\ No newline at end of file
diff --git a/src/Components/AnsiCode.php b/src/Components/Terminal.php
similarity index 96%
rename from src/Components/AnsiCode.php
rename to src/Components/Terminal.php
index 33180aa..06a62e6 100644
--- a/src/Components/AnsiCode.php
+++ b/src/Components/Terminal.php
@@ -11,14 +11,14 @@
use Inhere\Console\Utils\Show;
/**
- * Class AnsiCode terminal
+ * Class Terminal - terminal control by ansiCode
* @package Inhere\Console\Components
*
* 2K 清除本行
* \x0D = \r = 13 回车,回到行首
* ESC = \x1B = 27
*/
-final class AnsiCode
+final class Terminal
{
const BEGIN_CHAR = "\033[";
@@ -133,8 +133,8 @@ public static function make()
* build ansi code string
*
* ```
- * AnsiCode::build(null, 'u'); // "\033[s" Saves the current cursor position
- * AnsiCode::build(0); // "\033[0m" Build end char, Resets any ANSI format
+ * Terminal::build(null, 'u'); // "\033[s" Saves the current cursor position
+ * Terminal::build(0); // "\033[0m" Build end char, Resets any ANSI format
* ```
*
* @param mixed $format
diff --git a/src/Utils/CliUtil.php b/src/Utils/CliUtil.php
index 6cfb1e8..4cb39f8 100644
--- a/src/Utils/CliUtil.php
+++ b/src/Utils/CliUtil.php
@@ -142,6 +142,62 @@ public static function getTempDir()
return $tmp;
}
+ /**
+ * get screen size
+ *
+ * ```php
+ * list($width, $height) = Helper::getScreenSize();
+ * ```
+ * @from Yii2
+ * @param boolean $refresh whether to force checking and not re-use cached size value.
+ * This is useful to detect changing window size while the application is running but may
+ * not get up to date values on every terminal.
+ * @return array|boolean An array of ($width, $height) or false when it was not able to determine size.
+ */
+ public static function getScreenSize($refresh = false)
+ {
+ static $size;
+ if ($size !== null && !$refresh) {
+ return $size;
+ }
+
+ if (self::bashIsAvailable()) {
+ // try stty if available
+ $stty = [];
+
+ if (
+ exec('stty -a 2>&1', $stty) &&
+ preg_match('/rows\s+(\d+);\s*columns\s+(\d+);/mi', implode(' ', $stty), $matches)
+ ) {
+ return ($size = [$matches[2], $matches[1]]);
+ }
+
+ // fallback to tput, which may not be updated on terminal resize
+ if (($width = (int)exec('tput cols 2>&1')) > 0 && ($height = (int)exec('tput lines 2>&1')) > 0) {
+ return ($size = [$width, $height]);
+ }
+
+ // fallback to ENV variables, which may not be updated on terminal resize
+ if (($width = (int)getenv('COLUMNS')) > 0 && ($height = (int)getenv('LINES')) > 0) {
+ return ($size = [$width, $height]);
+ }
+ }
+
+ if (Helper::isOnWindows()) {
+ $output = [];
+ exec('mode con', $output);
+
+ if (isset($output[1]) && strpos($output[1], 'CON') !== false) {
+ return ($size = [
+ (int)preg_replace('~\D~', '', $output[3]),
+ (int)preg_replace('~\D~', '', $output[4])
+ ]);
+ }
+ }
+
+ return ($size = false);
+ }
+
/**
* @param string $program
* @return int|string
diff --git a/src/Utils/Helper.php b/src/Utils/Helper.php
index 0daafea..8432c34 100644
--- a/src/Utils/Helper.php
+++ b/src/Utils/Helper.php
@@ -63,6 +63,14 @@ public static function isRoot(): bool
return getmyuid() === 0;
}
+ /**
+ * @return bool
+ */
+ public static function supportColor()
+ {
+ return self::isSupportColor();
+ }
+
/**
* Returns true if STDOUT supports colorization.
* This code has been copied and adapted from
@@ -382,62 +390,6 @@ public static function spliceKeyValue(array $data, array $opts = [])
return $text;
}
- /**
- * get screen size
- *
- * ```php
- * list($width, $height) = Helper::getScreenSize();
- * ```
- * @from Yii2
- * @param boolean $refresh whether to force checking and not re-use cached size value.
- * This is useful to detect changing window size while the application is running but may
- * not get up to date values on every terminal.
- * @return array|boolean An array of ($width, $height) or false when it was not able to determine size.
- */
- public static function getScreenSize($refresh = false)
- {
- static $size;
- if ($size !== null && !$refresh) {
- return $size;
- }
-
- if (CliUtil::bashIsAvailable()) {
- // try stty if available
- $stty = [];
-
- if (
- exec('stty -a 2>&1', $stty) &&
- preg_match('/rows\s+(\d+);\s*columns\s+(\d+);/mi', implode(' ', $stty), $matches)
- ) {
- return ($size = [$matches[2], $matches[1]]);
- }
-
- // fallback to tput, which may not be updated on terminal resize
- if (($width = (int)exec('tput cols 2>&1')) > 0 && ($height = (int)exec('tput lines 2>&1')) > 0) {
- return ($size = [$width, $height]);
- }
-
- // fallback to ENV variables, which may not be updated on terminal resize
- if (($width = (int)getenv('COLUMNS')) > 0 && ($height = (int)getenv('LINES')) > 0) {
- return ($size = [$width, $height]);
- }
- }
-
- if (self::isOnWindows()) {
- $output = [];
- exec('mode con', $output);
-
- if (isset($output[1]) && strpos($output[1], 'CON') !== false) {
- return ($size = [
- (int)preg_replace('~\D~', '', $output[3]),
- (int)preg_replace('~\D~', '', $output[4])
- ]);
- }
- }
-
- return ($size = false);
- }
-
/**
* Word wrap text with indentation to fit the screen size
*
@@ -465,7 +417,7 @@ public static function wrapText($text, $indent = 0, $width = 0)
}
if ((int)$width <= 0) {
- $size = static::getScreenSize();
+ $size = CliUtil::getScreenSize();
if ($size === false || $size[0] <= $indent) {
return $text;
diff --git a/src/Utils/ProcessUtil.php b/src/Utils/ProcessUtil.php
index cfb9e1f..cd37131 100644
--- a/src/Utils/ProcessUtil.php
+++ b/src/Utils/ProcessUtil.php
@@ -14,6 +14,14 @@
*/
class ProcessUtil
{
+ /**
+ * @return bool
+ */
+ public static function isSupported()
+ {
+ return !Helper::isWindows() && \function_exists('pcntl_fork');
+ }
+
/**
* Daemon, detach and run in the background
* @param \Closure|null $beforeQuit
@@ -21,6 +29,10 @@ class ProcessUtil
*/
public static function daemonRun(\Closure $beforeQuit = null)
{
+ if (!self::isSupported()) {
+ return 0;
+ }
+
// umask(0);
$pid = pcntl_fork();
@@ -65,42 +77,74 @@ public static function runInBackground($cmd)
}
/**
- * fork multi child processes.
+ * @see ProcessUtil::forks()
* @param int $number
- * @param callable|null $childHandler
- * @return array|int
+ * @param callable|null $onStart
+ * @param callable|null $onError
+ * @return array|false
*/
- public static function forks($number, callable $childHandler = null)
+ public static function multi(int $number, callable $onStart = null, callable $onError = null)
{
- $num = (int)$number > 0 ? (int)$number : 0;
+ return self::forks($number, $onStart, $onError);
+ }
- if ($num <= 0) {
+ /**
+ * fork/create multi child processes.
+ * @param int $number
+ * @param callable|null $onStart Will running on the child processes.
+ * @param callable|null $onError
+ * @return array|false
+ */
+ public static function forks(int $number, callable $onStart = null, callable $onError = null)
+ {
+ if ($number <= 0) {
+ return false;
+ }
+
+ if (!self::isSupported()) {
return false;
}
$pidAry = [];
- for ($id = 0; $id < $num; $id++) {
- $child = self::fork($id, $childHandler);
- $pidAry[$child['pid']] = $child;
+ for ($id = 0; $id < $number; $id++) {
+ $info = self::fork($onStart, $onError, $id);
+ $pidAry[$info['pid']] = $info;
}
return $pidAry;
}
/**
- * fork a child process.
+ * @see ProcessUtil::fork()
* @param int $id
- * @param callable|null $childHandler
- * param bool $first
- * @return array
+ * @param callable|null $onStart
+ * @param callable|null $onError
+ * @return array|false
+ */
+ public static function create($id = 0, callable $onStart = null, callable $onError = null)
+ {
+ return self::fork($id, $onStart, $onError);
+ }
+
+ /**
+ * fork/create a child process.
+ * @param callable|null $onStart Will running on the child process start.
+ * @param int $id The process index number. will use `forks()`
+ * @param callable|null $onError
+ * @return array|false
*/
- public static function fork($id = 0, callable $childHandler = null)
+ public static function fork(callable $onStart = null, callable $onError = null, $id = 0)
{
+ if (!self::isSupported()) {
+ return false;
+ }
+
$info = [];
$pid = pcntl_fork();
- if ($pid > 0) {// at parent, get forked child info
+ // at parent, get forked child info
+ if ($pid > 0) {
$info = [
'id' => $id,
'pid' => $pid,
@@ -109,11 +153,14 @@ public static function fork($id = 0, callable $childHandler = null)
} elseif ($pid === 0) { // at child
$pid = getmypid();
- if ($childHandler) {
- $childHandler($id, $pid);
+ if ($onStart) {
+ $onStart($id, $pid);
}
-
} else {
+ if ($onError) {
+ $onError($pid);
+ }
+
Show::error('Fork child process failed! exiting.');
}
@@ -127,6 +174,10 @@ public static function fork($id = 0, callable $childHandler = null)
*/
public static function wait(callable $onExit)
{
+ if (!self::isSupported()) {
+ return false;
+ }
+
$status = null;
//pid<0:子进程都没了
@@ -171,6 +222,10 @@ public static function stopChildren(array $children, $signal = SIGTERM, array $e
return false;
}
+ if (!self::isSupported()) {
+ return false;
+ }
+
$events = array_merge([
'beforeStops' => null,
'beforeStop' => null,
@@ -273,6 +328,29 @@ public static function killByName($name, $sigNo = 9)
return exec($cmd);
}
+
+ /**
+ * @param int $pid
+ * @return bool
+ */
+ public static function isRunning($pid)
+ {
+ return ($pid > 0) && @posix_kill($pid, 0);
+ }
+
+ /**
+ * exit
+ * @param int $code
+ */
+ public static function quit($code = 0)
+ {
+ exit((int)$code);
+ }
+
+ /**************************************************************************************
+ * process signal handle
+ *************************************************************************************/
+
/**
* send signal to the process
* @param int $pid
@@ -286,6 +364,10 @@ public static function sendSignal($pid, $signal, $timeout = 0)
return false;
}
+ if (!self::isSupported()) {
+ return false;
+ }
+
// do send
if ($ret = posix_kill($pid, $signal)) {
return true;
@@ -322,21 +404,24 @@ public static function sendSignal($pid, $signal, $timeout = 0)
}
/**
- * @param int $pid
+ * install signal
+ * @param int $signal e.g: SIGTERM SIGINT(Ctrl+C) SIGUSR1 SIGUSR2 SIGHUP
+ * @param callable $handler
* @return bool
*/
- public static function isRunning($pid)
+ public static function installSignal($signal, callable $handler)
{
- return ($pid > 0) && @posix_kill($pid, 0);
+ return pcntl_signal($signal, $handler, false);
}
/**
- * exit
- * @param int $code
+ * dispatch signal
+ * @return bool
*/
- public static function quit($code = 0)
+ public static function dispatchSignal()
{
- exit((int)$code);
+ // receive and dispatch sig
+ return pcntl_signal_dispatch();
}
/**************************************************************************************
@@ -404,27 +489,6 @@ public static function afterDo($seconds, callable $handler)
pcntl_alarm($seconds);
}
- /**
- * install signal
- * @param int $signal e.g: SIGTERM SIGINT(Ctrl+C) SIGUSR1 SIGUSR2 SIGHUP
- * @param callable $handler
- * @return bool
- */
- public static function installSignal($signal, callable $handler)
- {
- return pcntl_signal($signal, $handler, false);
- }
-
- /**
- * dispatch signal
- * @return bool
- */
- public static function dispatchSignal()
- {
- // receive and dispatch sig
- return pcntl_signal_dispatch();
- }
-
/**
* Set process title.
* @param string $title
diff --git a/src/Utils/Show.php b/src/Utils/Show.php
index 450b445..a6494ea 100644
--- a/src/Utils/Show.php
+++ b/src/Utils/Show.php
@@ -686,7 +686,7 @@ public static function table(array $data, $title = 'Data Table', array $opts = [
$hasHead = false;
$rowIndex = 0;
- $head = $table = [];
+ $head = [];
$tableHead = $opts['columns'];
$leftIndent = $opts['leftIndent'];
$showBorder = $opts['showBorder'];
@@ -814,59 +814,94 @@ public static function table(array $data, $title = 'Data Table', array $opts = [
***********************************************************************************/
/**
- * @todo un-completed
+ * show a spinner icon message
+ * ```php
+ * $total = 5000;
+ * while ($total--) {
+ * Show::spinner();
+ * usleep(100);
+ * }
+ * Show::spinner('Done', true);
+ * ```
+ * @param string $msg
+ * @param bool $ended
*/
- public static function spinner()
+ public static function spinner($msg = '', $ended = false)
{
- static $spinner = 0;
- static $lastTime = null;
static $chars = '-\|/';
- // static $chars = '-.*.-';
+ static $counter = 0;
+ static $lastTime = null;
+
+ $tpl = (Helper::supportColor() ? "\x0D\x1B[2K" : "\x0D\r") . '%s';
+
+ if ($ended) {
+ printf($tpl, $msg);
+
+ return;
+ }
- $tpl = (Helper::isSupportColor() ? "\x0D\x1B[2K" : "\x0D\r") . '%s';
$now = microtime(true);
if (null === $lastTime || ($lastTime < $now - 0.1)) {
$lastTime = $now;
- // echo $chars[$spinner];
- printf($tpl, $chars[$spinner]);
- $spinner++;
+ // echo $chars[$counter];
+ printf($tpl, $chars[$counter] . $msg);
+ $counter++;
- if ($spinner > \strlen($chars) - 1) {
- $spinner = 0;
+ if ($counter > \strlen($chars) - 1) {
+ $counter = 0;
}
}
}
/**
- * '.'
- * '..'
- * '...'
- * '.'
- * @param null $msg
+ * alias of the pending()
+ * @param string $msg
+ * @param bool $ended
*/
- public static function loading($msg = null)
+ public static function loading($msg = 'Loading ', $ended = false)
{
-
+ self::pending($msg, $ended);
}
- public static function pending($msg = 'pending')
+ /**
+ * show a pending message
+ * ```php
+ * $total = 8000;
+ *
+ * while ($total--) {
+ * Show::pending();
+ * usleep(200);
+ * }
+ *
+ * Show::pending('Done', true);
+ * ```
+ * @param string $msg
+ * @param bool $ended
+ */
+ public static function pending($msg = 'Pending ', $ended = false)
{
- static $spinner = 0;
+ static $counter = 0;
static $lastTime = null;
- static $chars = '...';
+ static $chars = ['', '.', '..', '...'];
+
+ $tpl = (Helper::supportColor() ? "\x0D\x1B[2K" : "\x0D\r") . '%s';
+
+ if ($ended) {
+ printf($tpl, $msg);
+
+ return;
+ }
- $tpl = (Helper::isSupportColor() ? "\x0D\x1B[2K" : "\x0D\r") . '%s';
$now = microtime(true);
- if (null === $lastTime || ($lastTime < $now - 0.1)) {
+ if (null === $lastTime || ($lastTime < $now - 0.8)) {
$lastTime = $now;
- // echo $chars[$spinner];
- printf($tpl, $chars[$spinner]);
- $spinner++;
+ printf($tpl, $msg . $chars[$counter]);
+ $counter++;
- if ($spinner > \strlen($chars) - 1) {
- $spinner = 0;
+ if ($counter > \count($chars) - 1) {
+ $counter = 0;
}
}
}