diff --git a/src/Bean/Annotation/Command.php b/src/Bean/Annotation/Command.php
index 04054ee..f175998 100644
--- a/src/Bean/Annotation/Command.php
+++ b/src/Bean/Annotation/Command.php
@@ -15,6 +15,11 @@ class Command
*/
private $name = '';
+ /**
+ * @var bool
+ */
+ private $enabled = true;
+
/**
* @var bool
*/
@@ -35,12 +40,19 @@ public function __construct(array $values)
if (isset($values['value'])) {
$this->name = $values['value'];
}
+
if (isset($values['name'])) {
$this->name = $values['name'];
}
+
if (isset($values['coroutine'])) {
$this->coroutine = $values['coroutine'];
}
+
+ if (isset($values['enabled'])) {
+ $this->enabled = (bool)$values['enabled'];
+ }
+
if (isset($values['server'])) {
$this->server = $values['server'];
}
@@ -69,4 +81,12 @@ public function isServer(): bool
{
return $this->server;
}
-}
\ No newline at end of file
+
+ /**
+ * @return bool
+ */
+ public function isEnabled(): bool
+ {
+ return $this->enabled;
+ }
+}
diff --git a/src/Bean/Collector/CommandCollector.php b/src/Bean/Collector/CommandCollector.php
index d9e9b90..19b3b82 100644
--- a/src/Bean/Collector/CommandCollector.php
+++ b/src/Bean/Collector/CommandCollector.php
@@ -20,7 +20,7 @@ class CommandCollector implements CollectorInterface
* collect
*
* @param string $className
- * @param object $objectAnnotation
+ * @param mixed $objectAnnotation
* @param string $propertyName
* @param string $methodName
* @param null $propertyValue
@@ -54,6 +54,7 @@ private static function collectCommand(string $className, Command $objectAnnotat
$server = $objectAnnotation->isServer();
self::$commandMapping[$className]['name'] = $commandName;
+ self::$commandMapping[$className]['enabled'] = $objectAnnotation->isEnabled();
self::$commandMapping[$className]['coroutine'] = $coroutine;
self::$commandMapping[$className]['server'] = $server;
}
@@ -94,4 +95,4 @@ public static function getCollector(): array
{
return self::$commandMapping;
}
-}
\ No newline at end of file
+}
diff --git a/src/Bean/Parser/CommandParser.php b/src/Bean/Parser/CommandParser.php
index 8b923a3..ba731cb 100644
--- a/src/Bean/Parser/CommandParser.php
+++ b/src/Bean/Parser/CommandParser.php
@@ -19,20 +19,21 @@
class CommandParser extends AbstractParser
{
/**
- * @param string $className
+ * @param string $className
* @param Command $objectAnnotation
- * @param string $propertyName
- * @param string $methodName
+ * @param string $propertyName
+ * @param string $methodName
*
+ * @param null $propertyValue
* @return mixed
*/
- public function parser(string $className, $objectAnnotation = null, string $propertyName = "", string $methodName = "", $propertyValue = null)
+ public function parser(string $className, $objectAnnotation = null, string $propertyName = '', string $methodName = '', $propertyValue = null)
{
$beanName = $className;
$scope = Scope::SINGLETON;
CommandCollector::collect($className, $objectAnnotation, $propertyName, $methodName, $propertyValue);
- return [$beanName, $scope, ""];
+ return [$beanName, $scope, ''];
}
-}
\ No newline at end of file
+}
diff --git a/src/Bean/Parser/MappingParser.php b/src/Bean/Parser/MappingParser.php
index 1860e40..7135745 100644
--- a/src/Bean/Parser/MappingParser.php
+++ b/src/Bean/Parser/MappingParser.php
@@ -22,15 +22,16 @@ class MappingParser extends AbstractParser
* @param Mapping $objectAnnotation
* @param string $propertyName
* @param string $methodName
+ * @param null $propertyValue
* @return void
*/
public function parser(
string $className,
$objectAnnotation = null,
- string $propertyName = "",
- string $methodName = "",
+ string $propertyName = '',
+ string $methodName = '',
$propertyValue = null
) {
CommandCollector::collect($className, $objectAnnotation, $propertyName, $methodName, $propertyValue);
}
-}
\ No newline at end of file
+}
diff --git a/src/Command.php b/src/Command.php
index e18ced2..1b5120d 100644
--- a/src/Command.php
+++ b/src/Command.php
@@ -6,7 +6,6 @@
use Swoft\Bean\Annotation\Bean;
use Swoft\Console\Bean\Collector\CommandCollector;
use Swoft\Console\Helper\DocBlockHelper;
-use Swoft\Helper\DocumentHelper;
use Swoft\Console\Router\HandlerAdapter;
use Swoft\Console\Router\HandlerMapping;
@@ -37,6 +36,7 @@ public function annotationVars(): array
/**
* @return void
+ * @throws \InvalidArgumentException
* @throws \ReflectionException
*/
public function run()
@@ -55,7 +55,7 @@ public function run()
list($className, $method) = $handler;
if ($router->isDefaultCommand($method)) {
- $this->indexComamnd($className);
+ $this->indexCommand($className);
return;
}
@@ -77,7 +77,7 @@ public function run()
* @throws \ReflectionException
* @return void
*/
- private function indexComamnd(string $className)
+ private function indexCommand(string $className)
{
/* @var HandlerMapping $router */
$router = App::getBean('commandRoute');
@@ -87,7 +87,7 @@ private function indexComamnd(string $className)
$reflectionClass = new \ReflectionClass($className);
$classDocument = $reflectionClass->getDocComment();
- $classDocAry = DocumentHelper::tagList($classDocument);
+ $classDocAry = DocBlockHelper::getTags($classDocument);
$classDesc = $classDocAry['Description'];
$methodCommands = [];
@@ -106,7 +106,7 @@ private function indexComamnd(string $className)
}
$reflectionMethod = $reflectionClass->getMethod($methodName);
$methodDocument = $reflectionMethod->getDocComment();
- $methodDocAry = DocumentHelper::tagList($methodDocument);
+ $methodDocAry = DocBlockHelper::getTags($methodDocument);
$methodCommands[$mappedName] = $methodDocAry['Description'];
}
@@ -137,7 +137,6 @@ private function showCommandHelp(string $controllerClass, string $commandMethod)
$reflectionMethod = $reflectionClass->getMethod($commandMethod);
$document = $reflectionMethod->getDocComment();
$document = $this->parseAnnotationVars($document, $this->annotationVars());
- // $docs = DocumentHelper::tagList($document);
$docs = DocBlockHelper::getTags($document);
$commands = [];
@@ -173,7 +172,7 @@ private function showCommandHelp(string $controllerClass, string $commandMethod)
}
/**
- * help list
+ * show all commands for the console app
*
* @throws \ReflectionException
*/
@@ -182,19 +181,19 @@ private function showCommandList()
$commands = $this->parserCmdAndDesc();
$commandList = [];
- $script = input()->getFullScript();
- $commandList['Usage:'] = ["php $script"];
+ $script = \input()->getFullScript();
+ $commandList['Usage:'] = ["php $script {command} [arguments] [options]"];
$commandList['Commands:'] = $commands;
$commandList['Options:'] = [
- '-h, --help' => 'show help information',
- '-v, --version' => 'show version',
+ '-h, --help' => 'Display help information',
+ '-v, --version' => 'Display version information',
];
// show logo
- output()->writeLogo();
+ \output()->writeLogo();
// output list
- output()->writeList($commandList, 'comment', 'info');
+ \output()->writeList($commandList, 'comment', 'info');
}
/**
@@ -208,9 +207,11 @@ private function showVersion()
$swooleVersion = SWOOLE_VERSION;
// 显示面板
- output()->writeLogo();
- output()->writeln("swoft: $swoftVersion, php: $phpVersion, swoole: $swooleVersion", true);
- output()->writeln('');
+ \output()->writeLogo();
+ \output()->writeln(
+ "swoft: $swoftVersion, php: $phpVersion, swoole: $swooleVersion\n",
+ true
+ );
}
/**
@@ -227,17 +228,23 @@ private function parserCmdAndDesc(): array
/* @var \Swoft\Console\Router\HandlerMapping $route */
$route = App::getBean('commandRoute');
- foreach ($collector as $className => $comamnd) {
+ foreach ($collector as $className => $command) {
+ if (!$command['enabled']) {
+ continue;
+ }
+
$rc = new \ReflectionClass($className);
$docComment = $rc->getDocComment();
- $docAry = DocumentHelper::tagList($docComment);
- $desc = $docAry['Description'];
+ $docAry = DocBlockHelper::getTags($docComment);
- $prefix = $comamnd['name'];
+ $prefix = $command['name'];
$prefix = $route->getPrefix($prefix, $className);
- $commands[$prefix] = $desc;
+ $commands[$prefix] = \ucfirst($docAry['Description']);
}
+ // sort commands
+ ksort($commands);
+
return $commands;
}
@@ -258,26 +265,6 @@ private function baseCommand()
$this->showCommandList();
}
- /**
- * 解析命令key和描述
- *
- * @param string $document 注解文档
- * @return array
- */
- private function parserKeyAndDesc(string $document): array
- {
- $keyAndDesc = [];
- $items = explode("\n", $document);
- foreach ($items as $item) {
- $pos = strpos($item, ' ');
- $key = substr($item, 0, $pos);
- $desc = substr($item, $pos + 1);
- $keyAndDesc[$key] = $desc;
- }
-
- return $keyAndDesc;
- }
-
/**
* 替换注解中的变量为对应的值
* @param string $str
diff --git a/src/Console.php b/src/Console.php
index 1d9e374..0b592ce 100644
--- a/src/Console.php
+++ b/src/Console.php
@@ -30,8 +30,8 @@ public function run()
$command = App::getBean('command');
$command->run();
} catch (\Throwable $e) {
- output()->writeln(sprintf('%s', $e->getMessage()), true, false);
- output()->writeln(sprintf('%s', $e->getTraceAsString()), true, true);
+ \output()->writeln(sprintf('%s', $e->getMessage()), true, false);
+ \output()->writeln(sprintf("Trace:\n%s", $e->getTraceAsString()), true, true);
}
}
diff --git a/src/Helper/CommandHelper.php b/src/Helper/CommandHelper.php
index 4d18fd0..b68f3b6 100644
--- a/src/Helper/CommandHelper.php
+++ b/src/Helper/CommandHelper.php
@@ -178,4 +178,49 @@ public static function nextIsValue($val): bool
// it isn't option or named argument
return $val{0} !== '-' && false === strpos($val, '=');
}
+
+
+ /**
+ * Returns true if STDOUT supports colorization.
+ * This code has been copied and adapted from
+ * \Symfony\Component\Console\Output\OutputStream.
+ * @return boolean
+ */
+ public static function supportColor(): bool
+ {
+ if (DIRECTORY_SEPARATOR === '\\') {
+ return
+ '10.0.10586' === PHP_WINDOWS_VERSION_MAJOR . '.' . PHP_WINDOWS_VERSION_MINOR . '.' . PHP_WINDOWS_VERSION_BUILD ||
+ // 0 == strpos(PHP_WINDOWS_VERSION_MAJOR . '.' . PHP_WINDOWS_VERSION_MINOR . PHP_WINDOWS_VERSION_BUILD, '10.') ||
+ false !== getenv('ANSICON') ||
+ 'ON' === getenv('ConEmuANSI') ||
+ 'xterm' === getenv('TERM')// || 'cygwin' === getenv('TERM')
+ ;
+ }
+
+ if (!\defined('STDOUT')) {
+ return false;
+ }
+
+ return self::isInteractive(STDOUT);
+ }
+
+ /**
+ * @return bool
+ */
+ public static function isSupport256Color(): bool
+ {
+ return DIRECTORY_SEPARATOR === '/' && strpos(getenv('TERM'), '256color') !== false;
+ }
+
+ /**
+ * Returns if the file descriptor is an interactive terminal or not.
+ * @param int|resource $fileDescriptor
+ * @return boolean
+ */
+ public static function isInteractive($fileDescriptor): bool
+ {
+ return \function_exists('posix_isatty') && @posix_isatty($fileDescriptor);
+ }
+
}
diff --git a/src/Helper/ConsoleUtil.php b/src/Helper/ConsoleUtil.php
index 8ec323d..7f354cd 100644
--- a/src/Helper/ConsoleUtil.php
+++ b/src/Helper/ConsoleUtil.php
@@ -14,6 +14,79 @@
*/
class ConsoleUtil
{
+ /**
+ * 与文本进度条相比,没有 total
+ *
+ * ```php
+ * $total = 120;
+ * $ctt = ConsoleUtil::counterTxt('doing ...', 'completed.');
+ * $this->write('Counter:');
+ * while ($total - 1) {
+ * $ctt->send(1);
+ * usleep(30000);
+ * $total--;
+ * }
+ * // end of the counter.
+ * $ctt->send(-1);
+ * ```
+ * @param string $msg
+ * @param string|null $doneMsg
+ * @return \Generator
+ */
+ public static function counterTxt(string $msg, $doneMsg = null)
+ {
+ $counter = 0;
+ $finished = false;
+ $tpl = (CommandHelper::supportColor() ? "\x0D\x1B[2K" : "\x0D\r") . '%d %s';
+ $msg = style()->t($msg);
+ $doneMsg = $doneMsg ? style()->t($doneMsg) : null;
+ while (true) {
+ if ($finished) {
+ return;
+ }
+
+ $step = yield;
+
+ if ((int)$step <= 0) {
+ $counter++;
+ $finished = true;
+ $msg = $doneMsg ?: $msg;
+ } else {
+ $counter += $step;
+ }
+
+ printf($tpl, $counter, $msg);
+
+ if ($finished) {
+ echo "\n";
+ break;
+ }
+ }
+
+ yield false;
+ }
+
+ /**
+ * read CLI input
+ * @param mixed $message
+ * @param bool $nl
+ * @param array $opts
+ * [
+ * 'stream' => \STDIN
+ * ]
+ * @return string
+ */
+ public static function read($message = null, $nl = false, array $opts = []): string
+ {
+ if ($message) {
+ \output()->writeln($message, $nl);
+ }
+
+ $stream = $opts['stream'] ?? \STDIN;
+
+ return trim(fgets($stream));
+ }
+
/**
* 确认, 发出信息要求确认
* @param string $question 发出的信息
diff --git a/src/Output/Output.php b/src/Output/Output.php
index 2163560..dea714f 100644
--- a/src/Output/Output.php
+++ b/src/Output/Output.php
@@ -23,14 +23,18 @@ class Output implements OutputInterface
/**
* 输出一行数据
*
- * @param string $messages 信息
+ * @param string|array $messages 信息
* @param bool $newline 是否换行
* @param bool $quit 是否退出
*/
public function writeln($messages = '', $newline = true, $quit = false)
{
+ if (\is_array($messages)) {
+ $messages = \implode($newline ? PHP_EOL : '', $messages);
+ }
+
// 文字里面颜色标签翻译
- $messages = \style()->t($messages);
+ $messages = \style()->t((string)$messages);
// 输出文字
echo $messages;
@@ -40,7 +44,7 @@ public function writeln($messages = '', $newline = true, $quit = false)
// 是否退出
if ($quit) {
- exit();
+ exit;
}
}
@@ -49,14 +53,23 @@ public function writeln($messages = '', $newline = true, $quit = false)
*/
public function writeLogo()
{
- $logo = "
+ $logo = "
____ __ _
/ ___|_ _____ / _| |_
\___ \ \ /\ / / _ \| |_| __|
___) \ V V / (_) | _| |_
|____/ \_/\_/ \___/|_| \__|
-";
- $this->writeln($logo);
+";
+ $this->colored(' ' . \ltrim($logo));
+ }
+
+ /**
+ * @param string $text
+ * @param string $tag
+ */
+ public function colored(string $text, string $tag = 'info')
+ {
+ $this->writeln(\sprintf('<%s>%s%s>', $tag, $text, $tag));
}
/**
@@ -98,7 +111,7 @@ private function writeItems(array $items, string $cmdStyle)
// 命令和描述
$maxLength = $this->getCmdMaxLength(array_keys($items));
- $cmd = str_pad($cmd, $maxLength, ' ');
+ $cmd = \str_pad($cmd, $maxLength, ' ');
$cmd = "<$cmdStyle>$cmd$cmdStyle>";
$message = self::LEFT_CHAR . $cmd . self::GAP_CHAR . $desc;
diff --git a/src/Router/HandlerAdapter.php b/src/Router/HandlerAdapter.php
index bc064bd..2bf021f 100644
--- a/src/Router/HandlerAdapter.php
+++ b/src/Router/HandlerAdapter.php
@@ -84,7 +84,7 @@ private function getBindParams(string $className, string $methodName): array
/**
* execute command by coroutine
*
- * @param object $class
+ * @param mixed $class
* @param string $method
* @param bool $server
* @param array $bindParams
@@ -92,7 +92,7 @@ private function getBindParams(string $className, string $methodName): array
private function executeCommandByCoroutine($class, string $method, bool $server, $bindParams)
{
Coroutine::create(function () use ($class, $method, $server, $bindParams) {
- $this->beforeCommand(get_parent_class($class), $method, $server);
+ $this->beforeCommand(\get_parent_class($class), $method, $server);
PhpHelper::call([$class, $method], $bindParams);
$this->afterCommand($method, $server);
});
@@ -101,14 +101,14 @@ private function executeCommandByCoroutine($class, string $method, bool $server,
/**
* execute command
*
- * @param object $class
+ * @param mixed $class
* @param string $method
* @param bool $server
* @param array $bindParams
*/
private function executeCommand($class, string $method, bool $server, $bindParams)
{
- $this->beforeCommand(get_parent_class($class), $method, $server);
+ $this->beforeCommand(\get_parent_class($class), $method, $server);
PhpHelper::call([$class, $method], $bindParams);
$this->afterCommand($method, $server);
}
@@ -177,4 +177,4 @@ private function bootstrap()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Router/HandlerMapping.php b/src/Router/HandlerMapping.php
index 2c25087..5679156 100644
--- a/src/Router/HandlerMapping.php
+++ b/src/Router/HandlerMapping.php
@@ -32,7 +32,7 @@ class HandlerMapping implements HandlerMappingInterface
/**
* the default command
*/
- private $deaultCommand = 'index';
+ private $defaultCommand = 'index';
/**
* the delimiter
@@ -83,7 +83,7 @@ public function register(array $commandMapping)
*/
public function isDefaultCommand(string $comamnd): bool
{
- return $comamnd === $this->deaultCommand;
+ return $comamnd === $this->defaultCommand;
}
/**
@@ -109,7 +109,7 @@ private function getGroupAndCommand(): array
}
if (empty($command)) {
- $command = $this->deaultCommand;
+ $command = $this->defaultCommand;
}
return [$group, $command];
@@ -153,8 +153,8 @@ private function registerRoute(string $className, array $routes, string $prefix,
$this->routes[$commandKey] = [$className, $methodName, $coroutine, $server];
}
- $commandKey = $this->getCommandString($prefix, $this->deaultCommand);
- $this->routes[$commandKey] = [$className, $this->deaultCommand];
+ $commandKey = $this->getCommandString($prefix, $this->defaultCommand);
+ $this->routes[$commandKey] = [$className, $this->defaultCommand];
}
/**
@@ -191,4 +191,4 @@ private function getCommandString(string $group, string $command): string
{
return sprintf('%s%s%s', $group, $this->delimiter, $command);
}
-}
\ No newline at end of file
+}
diff --git a/src/Style/Color.php b/src/Style/Color.php
index c5f5be1..157c596 100644
--- a/src/Style/Color.php
+++ b/src/Style/Color.php
@@ -93,7 +93,7 @@ final class Color
* @return Color
* @throws \InvalidArgumentException
*/
- public static function make(string $fg = '', string $bg = '', array $options = [])
+ public static function make(string $fg = '', string $bg = '', array $options = []): Color
{
return new self($fg, $bg, $options);
}
@@ -112,7 +112,7 @@ private function __construct(string $fg = '', string $bg = '', array $options =
$fgNotExist = ! empty($fg) && ! array_key_exists($fg, self::COLORS);
$bgNotExist = ! empty($bg) && ! array_key_exists($bg, self::COLORS);
if ($fgNotExist || $bgNotExist) {
- throw new \InvalidArgumentException("Foreground和Background参数值,不存在,检查后再试!");
+ throw new \InvalidArgumentException('Foreground和Background参数值,不存在,检查后再试!');
}
// 前景色
@@ -128,7 +128,7 @@ private function __construct(string $fg = '', string $bg = '', array $options =
foreach ($options as $option) {
// 选项不存在
if (! array_key_exists($option, self::OPTIONS)) {
- throw new \InvalidArgumentException("选项参数不存在,option=" . $option);
+ throw new \InvalidArgumentException('选项参数不存在,option=' . $option);
}
$this->options[] = self::OPTIONS[$option];
}