From 24fee2e423fd28592c1dad5b75547141c3226d2e Mon Sep 17 00:00:00 2001
From: yunwuxin <448901948@qq.com>
Date: Mon, 8 Aug 2016 17:42:24 +0800
Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9Bconsole?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
library/think/Console.php | 312 ++------------
.../think/console/{command => }/Command.php | 47 +--
library/think/console/Output.php | 103 +++--
library/think/console/command/Build.php | 1 +
library/think/console/command/Clear.php | 2 +-
library/think/console/command/Help.php | 5 +-
library/think/console/command/Lists.php | 7 +-
library/think/console/command/Make.php | 1 +
.../console/command/optimize/Autoload.php | 5 +-
.../think/console/command/optimize/Config.php | 2 +-
.../think/console/command/optimize/Route.php | 2 +-
library/think/console/helper/Debug.php | 114 -----
library/think/console/helper/Descriptor.php | 54 ---
library/think/console/helper/Formatter.php | 74 ----
library/think/console/helper/Helper.php | 121 ------
library/think/console/helper/Process.php | 118 ------
library/think/console/helper/Question.php | 394 ------------------
library/think/console/helper/Set.php | 99 -----
.../descriptor => output}/Descriptor.php | 6 +-
library/think/console/output/Nothing.php | 108 -----
library/think/console/output/Stream.php | 195 ---------
.../{helper => output}/descriptor/Console.php | 6 +-
.../think/console/output/driver/Buffer.php | 53 +++
.../think/console/output/driver/Console.php | 366 ++++++++++++++++
.../think/console/output/driver/Nothing.php | 33 ++
.../{helper => output}/question/Choice.php | 0
.../question/Confirmation.php | 0
.../{helper => output}/question/Question.php | 0
28 files changed, 569 insertions(+), 1659 deletions(-)
rename library/think/console/{command => }/Command.php (92%)
delete mode 100644 library/think/console/helper/Debug.php
delete mode 100644 library/think/console/helper/Descriptor.php
delete mode 100644 library/think/console/helper/Formatter.php
delete mode 100644 library/think/console/helper/Helper.php
delete mode 100644 library/think/console/helper/Process.php
delete mode 100644 library/think/console/helper/Question.php
delete mode 100644 library/think/console/helper/Set.php
rename library/think/console/{helper/descriptor => output}/Descriptor.php (98%)
delete mode 100644 library/think/console/output/Nothing.php
delete mode 100644 library/think/console/output/Stream.php
rename library/think/console/{helper => output}/descriptor/Console.php (96%)
create mode 100644 library/think/console/output/driver/Buffer.php
create mode 100644 library/think/console/output/driver/Console.php
create mode 100644 library/think/console/output/driver/Nothing.php
rename library/think/console/{helper => output}/question/Choice.php (100%)
rename library/think/console/{helper => output}/question/Confirmation.php (100%)
rename library/think/console/{helper => output}/question/Question.php (100%)
diff --git a/library/think/Console.php b/library/think/Console.php
index 7859974f..b0700bd8 100644
--- a/library/think/Console.php
+++ b/library/think/Console.php
@@ -9,20 +9,14 @@
namespace think;
-use think\console\command\Command;
+use think\console\Command;
use think\console\command\Help as HelpCommand;
-use think\console\helper\Debug as DebugFormatterHelper;
-use think\console\helper\Formatter as FormatterHelper;
-use think\console\helper\Process as ProcessHelper;
-use think\console\helper\Question as QuestionHelper;
-use think\console\helper\Set as HelperSet;
use think\console\Input;
use think\console\input\Argument as InputArgument;
use think\console\input\Definition as InputDefinition;
use think\console\input\Option as InputOption;
use think\console\Output;
-use think\console\output\Nothing;
-use think\console\output\Stream;
+use think\console\output\driver\Buffer;
class Console
{
@@ -35,14 +29,9 @@ class Console
private $wantHelps = false;
- /** @var Command */
- private $runningCommand;
-
private $catchExceptions = true;
private $autoExit = true;
private $definition;
- private $helperSet;
- private $terminalDimensions;
private $defaultCommand;
private static $defaultCommands = [
@@ -63,7 +52,6 @@ class Console
$this->version = $version;
$this->defaultCommand = 'list';
- $this->helperSet = $this->getDefaultHelperSet();
$this->definition = $this->getDefaultInputDefinition();
foreach ($this->getDefaultCommands() as $command) {
@@ -98,15 +86,24 @@ class Console
}
}
+ /**
+ * @param $command
+ * @param array $parameters
+ * @return Output|Buffer
+ */
public static function call($command, array $parameters = [])
{
$console = self::init(false);
array_unshift($parameters, $command);
- $input = new Input($parameters);
+ $input = new Input($parameters);
+ $output = new Output('buffer');
- $console->find($command)->run($input, new Nothing());
+ $console->setCatchExceptions(false);
+ $console->find($command)->run($input, $output);
+
+ return $output;
}
/**
@@ -129,11 +126,11 @@ class Console
throw $e;
}
- $this->renderException($e, $output->getErrorOutput());
+ $output->renderException($e);
$exitCode = $e->getCode();
if (is_numeric($exitCode)) {
- $exitCode = (int) $exitCode;
+ $exitCode = (int)$exitCode;
if (0 === $exitCode) {
$exitCode = 1;
}
@@ -155,8 +152,8 @@ class Console
/**
* 执行指令
- * @param Input $input
- * @param Output $output
+ * @param Input $input
+ * @param Output $output
* @return int
*/
public function doRun(Input $input, Output $output)
@@ -185,31 +182,11 @@ class Console
$command = $this->find($name);
- $this->runningCommand = $command;
- $exitCode = $this->doRunCommand($command, $input, $output);
- $this->runningCommand = null;
+ $exitCode = $this->doRunCommand($command, $input, $output);
return $exitCode;
}
- /**
- * 设置助手集
- * @param HelperSet $helperSet
- */
- public function setHelperSet(HelperSet $helperSet)
- {
- $this->helperSet = $helperSet;
- }
-
- /**
- * 获取助手集
- * @return HelperSet
- */
- public function getHelperSet()
- {
- return $this->helperSet;
- }
-
/**
* 设置输入参数定义
* @param InputDefinition $definition
@@ -244,7 +221,7 @@ class Console
*/
public function setCatchExceptions($boolean)
{
- $this->catchExceptions = (bool) $boolean;
+ $this->catchExceptions = (bool)$boolean;
}
/**
@@ -254,7 +231,7 @@ class Console
*/
public function setAutoExit($boolean)
{
- $this->autoExit = (bool) $boolean;
+ $this->autoExit = (bool)$boolean;
}
/**
@@ -422,7 +399,7 @@ class Console
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
return preg_quote($matches[1]) . '[^:]*';
}, $namespace);
- $namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
+ $namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
if (empty($namespaces)) {
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
@@ -460,7 +437,7 @@ class Console
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
return preg_quote($matches[1]) . '[^:]*';
}, $name);
- $commands = preg_grep('{^' . $expr . '}', $allCommands);
+ $commands = preg_grep('{^' . $expr . '}', $allCommands);
if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
if (false !== $pos = strrpos($name, ':')) {
@@ -547,144 +524,14 @@ class Console
*/
public function renderException(\Exception $e, Stream $output)
{
- do {
- $title = sprintf(' [%s] ', get_class($e));
- $len = $this->stringWidth($title);
-
- $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
-
- if (defined('HHVM_VERSION') && $width > 1 << 31) {
- $width = 1 << 31;
- }
- $formatter = $output->getFormatter();
- $lines = [];
- foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
- foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
-
- $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
- $lines[] = [$line, $lineLength];
-
- $len = max($lineLength, $len);
- }
- }
-
- $messages = ['', ''];
- $messages[] = $emptyLine = $formatter->format(sprintf('%s', str_repeat(' ', $len)));
- $messages[] = $formatter->format(sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
- foreach ($lines as $line) {
- $messages[] = $formatter->format(sprintf(' %s %s', $line[0], str_repeat(' ', $len - $line[1])));
- }
- $messages[] = $emptyLine;
- $messages[] = '';
- $messages[] = '';
-
- $output->writeln($messages, Output::OUTPUT_RAW);
-
- if (Output::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
- $output->writeln('Exception trace:');
-
- // exception related properties
- $trace = $e->getTrace();
- array_unshift($trace, [
- 'function' => '',
- 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a',
- 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a',
- 'args' => [],
- ]);
-
- for ($i = 0, $count = count($trace); $i < $count; ++$i) {
- $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
- $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
- $function = $trace[$i]['function'];
- $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
- $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
-
- $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line));
- }
-
- $output->writeln('');
- $output->writeln('');
- }
- } while ($e = $e->getPrevious());
-
- if (null !== $this->runningCommand) {
- $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName())));
- $output->writeln('');
- $output->writeln('');
- }
}
- /**
- * 获取终端宽度
- * @return int|null
- */
- protected function getTerminalWidth()
- {
- $dimensions = $this->getTerminalDimensions();
-
- return $dimensions[0];
- }
-
- /**
- * 获取终端高度
- * @return int|null
- */
- protected function getTerminalHeight()
- {
- $dimensions = $this->getTerminalDimensions();
-
- return $dimensions[1];
- }
-
- /**
- * 获取当前终端的尺寸
- * @return array
- */
- public function getTerminalDimensions()
- {
- if ($this->terminalDimensions) {
- return $this->terminalDimensions;
- }
-
- if ('\\' === DS) {
- if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
- return [(int) $matches[1], (int) $matches[2]];
- }
- if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) {
- return [(int) $matches[1], (int) $matches[2]];
- }
- }
-
- if ($sttyString = $this->getSttyColumns()) {
- if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
- return [(int) $matches[2], (int) $matches[1]];
- }
- if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
- return [(int) $matches[2], (int) $matches[1]];
- }
- }
-
- return [null, null];
- }
-
- /**
- * 设置终端尺寸
- * @param int $width
- * @param int $height
- * @return Console
- */
- public function setTerminalDimensions($width, $height)
- {
- $this->terminalDimensions = [$width, $height];
-
- return $this;
- }
/**
* 配置基于用户的参数和选项的输入和输出实例。
- * @param Input $input 输入实例
- * @param Output $output 输出实例
+ * @param Input $input 输入实例
+ * @param Output $output 输出实例
*/
protected function configureIO(Input $input, Output $output)
{
@@ -718,9 +565,9 @@ class Console
/**
* 执行指令
- * @param Command $command 指令实例
- * @param Input $input 输入实例
- * @param Output $output 输出实例
+ * @param Command $command 指令实例
+ * @param Input $input 输入实例
+ * @param Output $output 输出实例
* @return int
* @throws \Exception
*/
@@ -779,68 +626,6 @@ class Console
self::$defaultCommands = array_merge(self::$defaultCommands, $classnames);
}
- /**
- * 设置默认助手
- * @return HelperSet
- */
- protected function getDefaultHelperSet()
- {
- return new HelperSet([
- new FormatterHelper(),
- new DebugFormatterHelper(),
- new ProcessHelper(),
- new QuestionHelper(),
- ]);
- }
-
- /**
- * 获取stty列数
- * @return string
- */
- private function getSttyColumns()
- {
- if (!function_exists('proc_open')) {
- return null;
- }
-
- $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
- $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
- if (is_resource($process)) {
- $info = stream_get_contents($pipes[1]);
- fclose($pipes[1]);
- fclose($pipes[2]);
- proc_close($process);
-
- return $info;
- }
- return null;
- }
-
- /**
- * 获取终端模式
- * @return string x 或 null
- */
- private function getConsoleMode()
- {
- if (!function_exists('proc_open')) {
- return null;
- }
-
- $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
- $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
- if (is_resource($process)) {
- $info = stream_get_contents($pipes[1]);
- fclose($pipes[1]);
- fclose($pipes[2]);
- proc_close($process);
-
- if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
- return $matches[2] . 'x' . $matches[1];
- }
- }
- return null;
- }
-
/**
* 获取可能的建议
* @param array $abbrevs
@@ -924,49 +709,6 @@ class Console
$this->defaultCommand = $commandName;
}
- private function stringWidth($string)
- {
- if (!function_exists('mb_strwidth')) {
- return strlen($string);
- }
-
- if (false === $encoding = mb_detect_encoding($string)) {
- return strlen($string);
- }
-
- return mb_strwidth($string, $encoding);
- }
-
- private function splitStringByWidth($string, $width)
- {
- if (!function_exists('mb_strwidth')) {
- return str_split($string, $width);
- }
-
- if (false === $encoding = mb_detect_encoding($string)) {
- return str_split($string, $width);
- }
-
- $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
- $lines = [];
- $line = '';
- foreach (preg_split('//u', $utf8String) as $char) {
- if (mb_strwidth($line . $char, 'utf8') <= $width) {
- $line .= $char;
- continue;
- }
- $lines[] = str_pad($line, $width);
- $line = $char;
- }
- if (strlen($line)) {
- $lines[] = count($lines) ? str_pad($line, $width) : $line;
- }
-
- mb_convert_variables($encoding, 'utf8', $lines);
-
- return $lines;
- }
-
/**
* 返回所有的命名空间
* @param string $name
diff --git a/library/think/console/command/Command.php b/library/think/console/Command.php
similarity index 92%
rename from library/think/console/command/Command.php
rename to library/think/console/Command.php
index 82237593..fd3a16d5 100644
--- a/library/think/console/command/Command.php
+++ b/library/think/console/Command.php
@@ -9,15 +9,12 @@
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
-namespace think\console\command;
+namespace think\console;
use think\Console;
-use think\console\Input;
use think\console\input\Argument;
use think\console\input\Definition;
-use think\console\helper\Set as HelperSet;
use think\console\input\Option;
-use think\console\Output;
class Command
{
@@ -36,8 +33,11 @@ class Command
private $synopsis = [];
private $usages = [];
- /** @var HelperSet */
- private $helperSet;
+ /** @var Input */
+ protected $input;
+
+ /** @var Output */
+ protected $output;
/**
* 构造方法
@@ -75,29 +75,6 @@ class Command
public function setConsole(Console $console = null)
{
$this->console = $console;
- if ($console) {
- $this->setHelperSet($console->getHelperSet());
- } else {
- $this->helperSet = null;
- }
- }
-
- /**
- * 设置帮助集
- * @param HelperSet $helperSet
- */
- public function setHelperSet(HelperSet $helperSet)
- {
- $this->helperSet = $helperSet;
- }
-
- /**
- * 获取帮助集
- * @return HelperSet
- */
- public function getHelperSet()
- {
- return $this->helperSet;
}
/**
@@ -455,6 +432,7 @@ class Command
/**
* 添加用法介绍
* @param string $usage
+ * @return $this
*/
public function addUsage($usage)
{
@@ -476,17 +454,6 @@ class Command
return $this->usages;
}
- /**
- * 获取助手
- * @param string $name
- * @return mixed
- * @throws \InvalidArgumentException
- */
- public function getHelper($name)
- {
- return $this->helperSet->get($name);
- }
-
/**
* 验证指令名称
* @param string $name
diff --git a/library/think/console/Output.php b/library/think/console/Output.php
index 44eba7a5..38fe9051 100644
--- a/library/think/console/Output.php
+++ b/library/think/console/Output.php
@@ -11,43 +11,52 @@
namespace think\console;
-use think\console\output\Formatter;
-use think\console\output\Stream;
+use Exception;
+use think\console\output\Descriptor;
+use think\console\output\driver\Buffer;
+use think\console\output\driver\Console;
+use think\console\output\driver\Nothing;
-class Output extends Stream
+class Output
{
+ const VERBOSITY_QUIET = 0;
+ const VERBOSITY_NORMAL = 1;
+ const VERBOSITY_VERBOSE = 2;
+ const VERBOSITY_VERY_VERBOSE = 3;
+ const VERBOSITY_DEBUG = 4;
- /** @var Stream */
- private $stderr;
+ const OUTPUT_NORMAL = 0;
+ const OUTPUT_RAW = 1;
+ const OUTPUT_PLAIN = 2;
- public function __construct()
+ private $verbosity = self::VERBOSITY_NORMAL;
+
+ /** @var Buffer|Console|Nothing */
+ private $handle = null;
+
+ public function __construct($driver = 'console')
{
- $outputStream = 'php://stdout';
- if (!$this->hasStdoutSupport()) {
- $outputStream = 'php://output';
- }
+ $class = '\\think\\console\\output\\driver\\' . ucwords($driver);
- parent::__construct(fopen($outputStream, 'w'));
+ $this->handle = new $class($this);
+ }
- $this->stderr = new Stream(fopen('php://stderr', 'w'), $this->getFormatter());
+ public function writeln($messages, $type = self::OUTPUT_NORMAL)
+ {
+ $this->write($messages, true, $type);
}
/**
* {@inheritdoc}
*/
- public function setDecorated($decorated)
+ public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
{
- parent::setDecorated($decorated);
- $this->stderr->setDecorated($decorated);
+ $this->handle->write($messages, $newline, $type);
}
- /**
- * {@inheritdoc}
- */
- public function setFormatter(Formatter $formatter)
+ public function renderException(\Exception $e)
{
- parent::setFormatter($formatter);
- $this->stderr->setFormatter($formatter);
+ $this->handle->renderException($e);
}
/**
@@ -55,32 +64,54 @@ class Output extends Stream
*/
public function setVerbosity($level)
{
- parent::setVerbosity($level);
- $this->stderr->setVerbosity($level);
+ $this->verbosity = (int)$level;
}
/**
* {@inheritdoc}
*/
- public function getErrorOutput()
+ public function getVerbosity()
{
- return $this->stderr;
+ return $this->verbosity;
}
- /**
- * {@inheritdoc}
- */
- public function setErrorOutput(Output $error)
+ public function isQuiet()
{
- $this->stderr = $error;
+ return self::VERBOSITY_QUIET === $this->verbosity;
}
- /**
- * 检查当前环境是否支持控制台输出写入标准输出。
- * @return bool
- */
- protected function hasStdoutSupport()
+ public function isVerbose()
{
- return ('OS400' != php_uname('s'));
+ return self::VERBOSITY_VERBOSE <= $this->verbosity;
}
+
+ public function isVeryVerbose()
+ {
+ return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
+ }
+
+ public function isDebug()
+ {
+ return self::VERBOSITY_DEBUG <= $this->verbosity;
+ }
+
+ public function describe($object, array $options = [])
+ {
+ $descriptor = new Descriptor();
+ $options = array_merge([
+ 'raw_text' => false
+ ], $options);
+
+ $descriptor->describe($this, $object, $options);
+ }
+
+ public function __call($method, $args)
+ {
+ if ($this->handle && method_exists($this->handle, $method)) {
+ return call_user_func_array([$this->handle, $method], $args);
+ } else {
+ throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
+ }
+ }
+
}
\ No newline at end of file
diff --git a/library/think/console/command/Build.php b/library/think/console/command/Build.php
index 9c86060c..0523ac93 100644
--- a/library/think/console/command/Build.php
+++ b/library/think/console/command/Build.php
@@ -11,6 +11,7 @@
namespace think\console\command;
+use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
diff --git a/library/think/console/command/Clear.php b/library/think/console/command/Clear.php
index 072ce6ec..60f3a478 100644
--- a/library/think/console/command/Clear.php
+++ b/library/think/console/command/Clear.php
@@ -10,7 +10,7 @@
// +----------------------------------------------------------------------
namespace think\console\command;
-use think\console\command\Command;
+use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
diff --git a/library/think/console/command/Help.php b/library/think/console/command/Help.php
index a7bcaade..eb0858a3 100644
--- a/library/think/console/command/Help.php
+++ b/library/think/console/command/Help.php
@@ -11,6 +11,7 @@
namespace think\console\command;
+use think\console\Command;
use think\console\Input;
use think\console\input\Argument as InputArgument;
use think\console\input\Option as InputOption;
@@ -60,9 +61,7 @@ EOF
$this->command = $this->getConsole()->find($input->getArgument('command_name'));
}
-
- $helper = new DescriptorHelper();
- $helper->describe($output, $this->command, [
+ $output->describe($this->command, [
'raw_text' => $input->getOption('raw'),
]);
diff --git a/library/think/console/command/Lists.php b/library/think/console/command/Lists.php
index 0eac10d5..ffbee07c 100644
--- a/library/think/console/command/Lists.php
+++ b/library/think/console/command/Lists.php
@@ -11,13 +11,12 @@
namespace think\console\command;
-
+use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\console\input\Argument as InputArgument;
use think\console\input\Option as InputOption;
use think\console\input\Definition as InputDefinition;
-use think\console\helper\Descriptor as DescriptorHelper;
class Lists extends Command
{
@@ -56,9 +55,7 @@ EOF
*/
protected function execute(Input $input, Output $output)
{
-
- $helper = new DescriptorHelper();
- $helper->describe($output, $this->getConsole(), [
+ $output->describe($this->getConsole(), [
'raw_text' => $input->getOption('raw'),
'namespace' => $input->getArgument('namespace'),
]);
diff --git a/library/think/console/command/Make.php b/library/think/console/command/Make.php
index 2829ee3c..74b06053 100644
--- a/library/think/console/command/Make.php
+++ b/library/think/console/command/Make.php
@@ -12,6 +12,7 @@
namespace think\console\command;
use think\Config;
+use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;
diff --git a/library/think/console/command/optimize/Autoload.php b/library/think/console/command/optimize/Autoload.php
index 8079c1b4..1fa28a44 100644
--- a/library/think/console/command/optimize/Autoload.php
+++ b/library/think/console/command/optimize/Autoload.php
@@ -11,14 +11,12 @@
namespace think\console\command\optimize;
use think\App;
-use think\console\command\Command;
+use think\console\Command;
use think\console\Input;
use think\console\Output;
class Autoload extends Command
{
- /** @var Output */
- protected $output;
protected function configure()
{
@@ -28,7 +26,6 @@ class Autoload extends Command
protected function execute(Input $input, Output $output)
{
- $this->output = $output;
$classmapFile = <<
-// +----------------------------------------------------------------------
-
-namespace think\console\helper;
-
-class Debug extends Helper
-{
-
- private $colors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'];
- private $started = [];
- private $count = -1;
-
- /**
- * 启动调试会话的格式
- * @param string $id 会话的 id
- * @param string $message 要显示的消息
- * @param string $prefix 要使用的前缀
- * @return string
- */
- public function start($id, $message, $prefix = 'RUN')
- {
- $this->started[$id] = ['border' => ++$this->count % count($this->colors)];
-
- return sprintf("%s %s > %s>\n", $this->getBorder($id), $prefix, $message);
- }
-
- /**
- * 添加设置会话的进度条格式
- * @param string $id 会话的 id
- * @param string $buffer 要显示的消息
- * @param bool $error 是否输出错误
- * @param string $prefix 输出的前缀
- * @param string $errorPrefix 输出错误的前缀
- * @return string
- */
- public function progress($id, $buffer, $error = false, $prefix = 'OUT', $errorPrefix = 'ERR')
- {
- $message = '';
-
- if ($error) {
- if (isset($this->started[$id]['out'])) {
- $message .= "\n";
- unset($this->started[$id]['out']);
- }
- if (!isset($this->started[$id]['err'])) {
- $message .= sprintf("%s %s > ", $this->getBorder($id), $errorPrefix);
- $this->started[$id]['err'] = true;
- }
-
- $message .= str_replace("\n", sprintf("\n%s %s > ", $this->getBorder($id), $errorPrefix), $buffer);
- } else {
- if (isset($this->started[$id]['err'])) {
- $message .= "\n";
- unset($this->started[$id]['err']);
- }
- if (!isset($this->started[$id]['out'])) {
- $message .= sprintf("%s %s > ", $this->getBorder($id), $prefix);
- $this->started[$id]['out'] = true;
- }
-
- $message .= str_replace("\n", sprintf("\n%s %s > ", $this->getBorder($id), $prefix), $buffer);
- }
-
- return $message;
- }
-
- /**
- * 停止一个会话
- * @param string $id 会话的 id
- * @param string $message 要显示的消息
- * @param bool $successful 是否显示成功消息
- * @param string $prefix 前缀
- * @return string
- */
- public function stop($id, $message, $successful, $prefix = 'RES')
- {
- $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : '';
-
- if ($successful) {
- return sprintf("%s%s %s > %s>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
- }
-
- $message = sprintf("%s%s %s > %s>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
-
- unset($this->started[$id]['out'], $this->started[$id]['err']);
-
- return $message;
- }
-
- /**
- * @param string $id
- * @return string
- */
- private function getBorder($id)
- {
- return sprintf(' >', $this->colors[$this->started[$id]['border']]);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getName()
- {
- return 'debug_formatter';
- }
-}
\ No newline at end of file
diff --git a/library/think/console/helper/Descriptor.php b/library/think/console/helper/Descriptor.php
deleted file mode 100644
index 18c0eab8..00000000
--- a/library/think/console/helper/Descriptor.php
+++ /dev/null
@@ -1,54 +0,0 @@
-
-// +----------------------------------------------------------------------
-
-namespace think\console\helper;
-
-use think\console\helper\descriptor\Descriptor as OutputDescriptor;
-use think\console\Output;
-
-class Descriptor extends Helper
-{
-
- /**
- * @var OutputDescriptor
- */
- private $descriptor;
-
- /**
- * 构造方法
- */
- public function __construct()
- {
- $this->descriptor = new OutputDescriptor();
- }
-
- /**
- * 描述
- * @param Output $output
- * @param object $object
- * @param array $options
- * @throws \InvalidArgumentException
- */
- public function describe(Output $output, $object, array $options = [])
- {
- $options = array_merge([
- 'raw_text' => false
- ], $options);
-
- $this->descriptor->describe($output, $object, $options);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getName()
- {
- return 'descriptor';
- }
-}
diff --git a/library/think/console/helper/Formatter.php b/library/think/console/helper/Formatter.php
deleted file mode 100644
index 18ce5742..00000000
--- a/library/think/console/helper/Formatter.php
+++ /dev/null
@@ -1,74 +0,0 @@
-
-// +----------------------------------------------------------------------
-
-namespace think\console\helper;
-
-use think\console\output\Formatter as OutputFormatter;
-
-class Formatter extends Helper
-{
-
- /**
- * 设置消息在某一节的格式
- * @param string $section 节名称
- * @param string $message 消息
- * @param string $style 样式
- * @return string
- */
- public function formatSection($section, $message, $style = 'info')
- {
- return sprintf('<%s>[%s]%s> %s', $style, $section, $style, $message);
- }
-
- /**
- * 设置消息作为文本块的格式
- * @param string|array $messages 消息
- * @param string $style 样式
- * @param bool $large 是否返回一个大段文本
- * @return string The formatter message
- */
- public function formatBlock($messages, $style, $large = false)
- {
- if (!is_array($messages)) {
- $messages = [$messages];
- }
-
- $len = 0;
- $lines = [];
- foreach ($messages as $message) {
- $message = OutputFormatter::escape($message);
- $lines[] = sprintf($large ? ' %s ' : ' %s ', $message);
- $len = max($this->strlen($message) + ($large ? 4 : 2), $len);
- }
-
- $messages = $large ? [str_repeat(' ', $len)] : [];
- for ($i = 0; isset($lines[$i]); ++$i) {
- $messages[] = $lines[$i] . str_repeat(' ', $len - $this->strlen($lines[$i]));
- }
- if ($large) {
- $messages[] = str_repeat(' ', $len);
- }
-
- for ($i = 0; isset($messages[$i]); ++$i) {
- $messages[$i] = sprintf('<%s>%s%s>', $style, $messages[$i], $style);
- }
-
- return implode("\n", $messages);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getName()
- {
- return 'formatter';
- }
-}
\ No newline at end of file
diff --git a/library/think/console/helper/Helper.php b/library/think/console/helper/Helper.php
deleted file mode 100644
index 7b327bdd..00000000
--- a/library/think/console/helper/Helper.php
+++ /dev/null
@@ -1,121 +0,0 @@
-
-// +----------------------------------------------------------------------
-
-namespace think\console\helper;
-
-use think\console\helper\Set as HelperSet;
-use think\console\output\Formatter;
-
-abstract class Helper
-{
-
- protected $helperSet = null;
-
- /**
- * 设置与此助手关联的助手集。
- * @param HelperSet $helperSet
- */
- public function setHelperSet(HelperSet $helperSet = null)
- {
- $this->helperSet = $helperSet;
- }
-
- /**
- * 获取与此助手关联的助手集。
- * @return HelperSet
- */
- public function getHelperSet()
- {
- return $this->helperSet;
- }
-
- /**
- * 获取名称
- * @return string
- */
- abstract public function getName();
-
- /**
- * 返回字符串的长度
- * @param string $string
- * @return int
- */
- public static function strlen($string)
- {
- if (!function_exists('mb_strwidth')) {
- return strlen($string);
- }
-
- if (false === $encoding = mb_detect_encoding($string)) {
- return strlen($string);
- }
-
- return mb_strwidth($string, $encoding);
- }
-
- public static function formatTime($secs)
- {
- static $timeFormats = [
- [0, '< 1 sec'],
- [2, '1 sec'],
- [59, 'secs', 1],
- [60, '1 min'],
- [3600, 'mins', 60],
- [5400, '1 hr'],
- [86400, 'hrs', 3600],
- [129600, '1 day'],
- [604800, 'days', 86400],
- ];
-
- foreach ($timeFormats as $format) {
- if ($secs >= $format[0]) {
- continue;
- }
-
- if (2 == count($format)) {
- return $format[1];
- }
-
- return ceil($secs / $format[2]) . ' ' . $format[1];
- }
- return null;
- }
-
- public static function formatMemory($memory)
- {
- if ($memory >= 1024 * 1024 * 1024) {
- return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024);
- }
-
- if ($memory >= 1024 * 1024) {
- return sprintf('%.1f MiB', $memory / 1024 / 1024);
- }
-
- if ($memory >= 1024) {
- return sprintf('%d KiB', $memory / 1024);
- }
-
- return sprintf('%d B', $memory);
- }
-
- public static function strlenWithoutDecoration(Formatter $formatter, $string)
- {
- $isDecorated = $formatter->isDecorated();
- $formatter->setDecorated(false);
- // remove <...> formatting
- $string = $formatter->format($string);
- // remove already formatted characters
- $string = preg_replace("/\033\[[^m]*m/", '', $string);
- $formatter->setDecorated($isDecorated);
-
- return self::strlen($string);
- }
-}
diff --git a/library/think/console/helper/Process.php b/library/think/console/helper/Process.php
deleted file mode 100644
index 4252bfe3..00000000
--- a/library/think/console/helper/Process.php
+++ /dev/null
@@ -1,118 +0,0 @@
-
-// +----------------------------------------------------------------------
-
-namespace think\console\helper;
-
-use think\console\Output;
-use think\Process as ThinkProcess;
-use think\process\Builder as ProcessBuilder;
-use think\process\exception\Failed as ProcessFailedException;
-
-class Process extends Helper
-{
-
- /**
- * 运行一个外部进程。
- * @param Output $output 一个Output实例
- * @param string|array|ThinkProcess $cmd 指令
- * @param string|null $error 错误信息
- * @param callable|null $callback 回调
- * @param int $verbosity
- * @return ThinkProcess
- */
- public function run(Output $output, $cmd, $error = null, $callback = null, $verbosity = Output::VERBOSITY_VERY_VERBOSE)
- {
- /** @var Debug $formatter */
- $formatter = $this->getHelperSet()->get('debug_formatter');
-
- if (is_array($cmd)) {
- $process = ProcessBuilder::create($cmd)->getProcess();
- } elseif ($cmd instanceof ThinkProcess) {
- $process = $cmd;
- } else {
- $process = new ThinkProcess($cmd);
- }
-
- if ($verbosity <= $output->getVerbosity()) {
- $output->write($formatter->start(spl_object_hash($process), $this->escapeString($process->getCommandLine())));
- }
-
- if ($output->isDebug()) {
- $callback = $this->wrapCallback($output, $process, $callback);
- }
-
- $process->run($callback);
-
- if ($verbosity <= $output->getVerbosity()) {
- $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode());
- $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful()));
- }
-
- if (!$process->isSuccessful() && null !== $error) {
- $output->writeln(sprintf('%s', $this->escapeString($error)));
- }
-
- return $process;
- }
-
- /**
- * 运行指令
- * @param Output $output
- * @param string|ThinkProcess $cmd
- * @param string|null $error
- * @param callable|null $callback
- * @return ThinkProcess
- */
- public function mustRun(Output $output, $cmd, $error = null, $callback = null)
- {
- $process = $this->run($output, $cmd, $error, $callback);
-
- if (!$process->isSuccessful()) {
- throw new ProcessFailedException($process);
- }
-
- return $process;
- }
-
- /**
- * 包装过程回调来添加调试输出
- * @param Output $output
- * @param ThinkProcess $process
- * @param callable|null $callback
- * @return callable
- */
- public function wrapCallback(Output $output, ThinkProcess $process, $callback = null)
- {
- /** @var Debug $formatter */
- $formatter = $this->getHelperSet()->get('debug_formatter');
-
- return function ($type, $buffer) use ($output, $process, $callback, $formatter) {
- $output->write($formatter->progress(spl_object_hash($process), $this->escapeString($buffer), ThinkProcess::ERR === $type));
-
- if (null !== $callback) {
- call_user_func($callback, $type, $buffer);
- }
- };
- }
-
- private function escapeString($str)
- {
- return str_replace('<', '\\<', $str);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getName()
- {
- return 'process';
- }
-}
diff --git a/library/think/console/helper/Question.php b/library/think/console/helper/Question.php
deleted file mode 100644
index e1820834..00000000
--- a/library/think/console/helper/Question.php
+++ /dev/null
@@ -1,394 +0,0 @@
-
-// +----------------------------------------------------------------------
-
-namespace think\console\helper;
-
-use think\console\helper\question\Choice as ChoiceQuestion;
-use think\console\helper\question\Question as OutputQuestion;
-use think\console\Input;
-use think\console\Output;
-use think\console\output\formatter\Style as OutputFormatterStyle;
-
-class Question extends Helper
-{
-
- private $inputStream;
- private static $shell;
- private static $stty;
-
- /**
- * 向用户提问
- * @param Input $input
- * @param Output $output
- * @param OutputQuestion $question
- * @return string
- */
- public function ask(Input $input, Output $output, OutputQuestion $question)
- {
- if (!$input->isInteractive()) {
- return $question->getDefault();
- }
-
- if (!$question->getValidator()) {
- return $this->doAsk($output, $question);
- }
-
- $interviewer = function () use ($output, $question) {
- return $this->doAsk($output, $question);
- };
-
- return $this->validateAttempts($interviewer, $output, $question);
- }
-
- /**
- * 设置输入流
- * @param resource $stream
- * @throws \InvalidArgumentException
- */
- public function setInputStream($stream)
- {
- if (!is_resource($stream)) {
- throw new \InvalidArgumentException('Input stream must be a valid resource.');
- }
-
- $this->inputStream = $stream;
- }
-
- /**
- * 获取输入流
- * @return resource
- */
- public function getInputStream()
- {
- return $this->inputStream;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getName()
- {
- return 'question';
- }
-
- /**
- * 提问
- * @param Output $output
- * @param OutputQuestion $question
- * @return bool|mixed|null|string
- * @throws \Exception
- * @throws \RuntimeException
- */
- private function doAsk(Output $output, OutputQuestion $question)
- {
- $this->writePrompt($output, $question);
-
- $inputStream = $this->inputStream ?: STDIN;
- $autocomplete = $question->getAutocompleterValues();
-
- if (null === $autocomplete || !$this->hasSttyAvailable()) {
- $ret = false;
- if ($question->isHidden()) {
- try {
- $ret = trim($this->getHiddenResponse($output, $inputStream));
- } catch (\RuntimeException $e) {
- if (!$question->isHiddenFallback()) {
- throw $e;
- }
- }
- }
-
- if (false === $ret) {
- $ret = fgets($inputStream, 4096);
- if (false === $ret) {
- throw new \RuntimeException('Aborted');
- }
- $ret = trim($ret);
- }
- } else {
- $ret = trim($this->autocomplete($output, $question, $inputStream));
- }
-
- $ret = strlen($ret) > 0 ? $ret : $question->getDefault();
-
- if ($normalizer = $question->getNormalizer()) {
- return $normalizer($ret);
- }
-
- return $ret;
- }
-
- /**
- * 显示提示
- * @param Output $output
- * @param OutputQuestion $question
- */
- protected function writePrompt(Output $output, OutputQuestion $question)
- {
- $message = $question->getQuestion();
-
- if ($question instanceof ChoiceQuestion) {
- $width = max(array_map('strlen', array_keys($question->getChoices())));
-
- $messages = (array) $question->getQuestion();
- foreach ($question->getChoices() as $key => $value) {
- $messages[] = sprintf(" [%-${width}s] %s", $key, $value);
- }
-
- $output->writeln($messages);
-
- $message = $question->getPrompt();
- }
-
- $output->write($message);
- }
-
- /**
- * 输出错误
- * @param Output $output
- * @param \Exception $error
- */
- protected function writeError(Output $output, \Exception $error)
- {
- if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) {
- $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error');
- } else {
- $message = '' . $error->getMessage() . '';
- }
-
- $output->writeln($message);
- }
-
- /**
- * 自动完成问题
- * @param Output $output
- * @param OutputQuestion $question
- * @param $inputStream
- * @return string
- */
- private function autocomplete(Output $output, OutputQuestion $question, $inputStream)
- {
- $autocomplete = $question->getAutocompleterValues();
- $ret = '';
-
- $i = 0;
- $ofs = -1;
- $matches = $autocomplete;
- $numMatches = count($matches);
-
- $sttyMode = shell_exec('stty -g');
-
- shell_exec('stty -icanon -echo');
-
- $output->getFormatter()->setStyle('hl', new OutputFormatterStyle('black', 'white'));
-
- while (!feof($inputStream)) {
- $c = fread($inputStream, 1);
-
- if ("\177" === $c) {
- if (0 === $numMatches && 0 !== $i) {
- $i--;
- $output->write("\033[1D");
- }
-
- if (0 === $i) {
- $ofs = -1;
- $matches = $autocomplete;
- $numMatches = count($matches);
- } else {
- $numMatches = 0;
- }
-
- $ret = substr($ret, 0, $i);
- } elseif ("\033" === $c) {
- $c .= fread($inputStream, 2);
-
- if (isset($c[2]) && ('A' === $c[2] || 'B' === $c[2])) {
- if ('A' === $c[2] && -1 === $ofs) {
- $ofs = 0;
- }
-
- if (0 === $numMatches) {
- continue;
- }
-
- $ofs += ('A' === $c[2]) ? -1 : 1;
- $ofs = ($numMatches + $ofs) % $numMatches;
- }
- } elseif (ord($c) < 32) {
- if ("\t" === $c || "\n" === $c) {
- if ($numMatches > 0 && -1 !== $ofs) {
- $ret = $matches[$ofs];
- $output->write(substr($ret, $i));
- $i = strlen($ret);
- }
-
- if ("\n" === $c) {
- $output->write($c);
- break;
- }
-
- $numMatches = 0;
- }
-
- continue;
- } else {
- $output->write($c);
- $ret .= $c;
- $i++;
-
- $numMatches = 0;
- $ofs = 0;
-
- foreach ($autocomplete as $value) {
- if (0 === strpos($value, $ret) && strlen($value) !== $i) {
- $matches[$numMatches++] = $value;
- }
- }
- }
-
- $output->write("\033[K");
-
- if ($numMatches > 0 && -1 !== $ofs) {
- $output->write("\0337");
- $output->write('' . substr($matches[$ofs], $i) . '');
- $output->write("\0338");
- }
- }
-
- shell_exec(sprintf('stty %s', $sttyMode));
-
- return $ret;
- }
-
- /**
- * 从用户获取隐藏的响应
- * @param Output $output
- * @return string
- * @throws \RuntimeException
- */
- private function getHiddenResponse(Output $output, $inputStream)
- {
- if ('\\' === DS) {
- $exe = __DIR__ . '/../bin/hiddeninput.exe';
-
- if ('phar:' === substr(__FILE__, 0, 5)) {
- $tmpExe = sys_get_temp_dir() . '/hiddeninput.exe';
- copy($exe, $tmpExe);
- $exe = $tmpExe;
- }
-
- $value = rtrim(shell_exec($exe));
- $output->writeln('');
-
- if (isset($tmpExe)) {
- unlink($tmpExe);
- }
-
- return $value;
- }
-
- if ($this->hasSttyAvailable()) {
- $sttyMode = shell_exec('stty -g');
-
- shell_exec('stty -echo');
- $value = fgets($inputStream, 4096);
- shell_exec(sprintf('stty %s', $sttyMode));
-
- if (false === $value) {
- throw new \RuntimeException('Aborted');
- }
-
- $value = trim($value);
- $output->writeln('');
-
- return $value;
- }
-
- if (false !== $shell = $this->getShell()) {
- $readCmd = 'csh' === $shell ? 'set mypassword = $<' : 'read -r mypassword';
- $command = sprintf("/usr/bin/env %s -c 'stty -echo; %s; stty echo; echo \$mypassword'", $shell, $readCmd);
- $value = rtrim(shell_exec($command));
- $output->writeln('');
-
- return $value;
- }
-
- throw new \RuntimeException('Unable to hide the response.');
- }
-
- /**
- * 验证重试次数
- * @param callable $interviewer
- * @param Output $output
- * @param OutputQuestion $question
- * @return string
- * @throws null
- */
- private function validateAttempts($interviewer, Output $output, OutputQuestion $question)
- {
- $error = null;
- $attempts = $question->getMaxAttempts();
- while (null === $attempts || $attempts--) {
- if (null !== $error) {
- $this->writeError($output, $error);
- }
-
- try {
- return call_user_func($question->getValidator(), $interviewer());
- } catch (\Exception $error) {
- }
- }
-
- throw $error;
- }
-
- /**
- * 获取一个有效的 unix 终端。
- * @return string|bool
- */
- private function getShell()
- {
- if (null !== self::$shell) {
- return self::$shell;
- }
-
- self::$shell = false;
-
- if (file_exists('/usr/bin/env')) {
- // handle other OSs with bash/zsh/ksh/csh if available to hide the answer
- $test = "/usr/bin/env %s -c 'echo OK' 2> /dev/null";
- foreach (['bash', 'zsh', 'ksh', 'csh'] as $sh) {
- if ('OK' === rtrim(shell_exec(sprintf($test, $sh)))) {
- self::$shell = $sh;
- break;
- }
- }
- }
-
- return self::$shell;
- }
-
- /**
- * 检查有用的stty
- * @return bool
- */
- private function hasSttyAvailable()
- {
- if (null !== self::$stty) {
- return self::$stty;
- }
-
- exec('stty 2>&1', $output, $exitcode);
-
- return self::$stty = 0 === $exitcode;
- }
-}
diff --git a/library/think/console/helper/Set.php b/library/think/console/helper/Set.php
deleted file mode 100644
index 63ea58e4..00000000
--- a/library/think/console/helper/Set.php
+++ /dev/null
@@ -1,99 +0,0 @@
-
-// +----------------------------------------------------------------------
-
-namespace think\console\helper;
-
-use think\console\command\Command;
-
-class Set implements \IteratorAggregate
-{
-
- private $helpers = [];
- private $command;
-
- /**
- * 构造方法
- * @param Helper[] $helpers 助手实例数组
- */
- public function __construct(array $helpers = [])
- {
- /**
- * @var int|string $alias
- * @var Helper $helper
- */
- foreach ($helpers as $alias => $helper) {
- $this->set($helper, is_int($alias) ? null : $alias);
- }
- }
-
- /**
- * 添加一个助手
- * @param Helper $helper 助手实例
- * @param string $alias 别名
- */
- public function set(Helper $helper, $alias = null)
- {
- $this->helpers[$helper->getName()] = $helper;
- if (null !== $alias) {
- $this->helpers[$alias] = $helper;
- }
-
- $helper->setHelperSet($this);
- }
-
- /**
- * 是否有某个助手
- * @param string $name 助手名称
- * @return bool
- */
- public function has($name)
- {
- return isset($this->helpers[$name]);
- }
-
- /**
- * 获取助手
- * @param string $name 助手名称
- * @return Helper
- * @throws \InvalidArgumentException
- */
- public function get($name)
- {
- if (!$this->has($name)) {
- throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
- }
-
- return $this->helpers[$name];
- }
-
- /**
- * 设置与这个助手关联的命令集
- * @param Command $command
- */
- public function setCommand(Command $command = null)
- {
- $this->command = $command;
- }
-
- /**
- * 获取与这个助手关联的命令集
- * @return Command
- */
- public function getCommand()
- {
- return $this->command;
- }
-
- public function getIterator()
- {
- return new \ArrayIterator($this->helpers);
- }
-}
diff --git a/library/think/console/helper/descriptor/Descriptor.php b/library/think/console/output/Descriptor.php
similarity index 98%
rename from library/think/console/helper/descriptor/Descriptor.php
rename to library/think/console/output/Descriptor.php
index b9a96d87..fff31e6b 100644
--- a/library/think/console/helper/descriptor/Descriptor.php
+++ b/library/think/console/output/Descriptor.php
@@ -9,15 +9,15 @@
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
-namespace think\console\helper\descriptor;
+namespace think\console\output;
use think\console\Output;
use think\console\input\Argument as InputArgument;
use think\console\input\Option as InputOption;
use think\console\input\Definition as InputDefinition;
-use think\console\command\Command;
+use think\console\Command;
use think\Console;
-use think\console\helper\descriptor\Console as ConsoleDescription;
+use think\console\output\descriptor\Console as ConsoleDescription;
class Descriptor
{
diff --git a/library/think/console/output/Nothing.php b/library/think/console/output/Nothing.php
deleted file mode 100644
index c1550270..00000000
--- a/library/think/console/output/Nothing.php
+++ /dev/null
@@ -1,108 +0,0 @@
-
-// +----------------------------------------------------------------------
-
-namespace think\console\output;
-
-use think\console\Output;
-
-class Nothing extends Output
-{
- /** @noinspection PhpMissingParentConstructorInspection */
- public function __construct()
- {
-
- }
-
- /**
- * {@inheritdoc}
- */
- public function setFormatter(Formatter $formatter)
- {
- // do nothing
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFormatter()
- {
- // to comply with the interface we must return a OutputFormatterInterface
- return new Formatter();
- }
-
- /**
- * {@inheritdoc}
- */
- public function setDecorated($decorated)
- {
- // do nothing
- }
-
- /**
- * {@inheritdoc}
- */
- public function isDecorated()
- {
- return false;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setVerbosity($level)
- {
- // do nothing
- }
-
- /**
- * {@inheritdoc}
- */
- public function getVerbosity()
- {
- return self::VERBOSITY_QUIET;
- }
-
- public function isQuiet()
- {
- return true;
- }
-
- public function isVerbose()
- {
- return false;
- }
-
- public function isVeryVerbose()
- {
- return false;
- }
-
- public function isDebug()
- {
- return false;
- }
-
- /**
- * {@inheritdoc}
- */
- public function writeln($messages, $options = self::OUTPUT_NORMAL)
- {
- // do nothing
- }
-
- /**
- * {@inheritdoc}
- */
- public function write($messages, $newline = false, $options = self::OUTPUT_NORMAL)
- {
- // do nothing
- }
-}
\ No newline at end of file
diff --git a/library/think/console/output/Stream.php b/library/think/console/output/Stream.php
deleted file mode 100644
index fec94bdf..00000000
--- a/library/think/console/output/Stream.php
+++ /dev/null
@@ -1,195 +0,0 @@
-
-// +----------------------------------------------------------------------
-
-namespace think\console\output;
-
-class Stream
-{
-
- const VERBOSITY_QUIET = 0;
- const VERBOSITY_NORMAL = 1;
- const VERBOSITY_VERBOSE = 2;
- const VERBOSITY_VERY_VERBOSE = 3;
- const VERBOSITY_DEBUG = 4;
-
- const OUTPUT_NORMAL = 0;
- const OUTPUT_RAW = 1;
- const OUTPUT_PLAIN = 2;
-
- private $verbosity = self::VERBOSITY_NORMAL;
- private $formatter;
-
-
- private $stream;
-
- /**
- * 构造方法
- * @param $stream
- * @param Formatter $formatter
- */
- public function __construct($stream, Formatter $formatter = null)
- {
- if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
- throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.');
- }
-
- $this->stream = $stream;
-
- $decorated = $this->hasColorSupport();
-
- $this->formatter = $formatter ?: new Formatter();
- $this->formatter->setDecorated($decorated);
- }
-
- /**
- * {@inheritdoc}
- */
- public function setFormatter(Formatter $formatter)
- {
- $this->formatter = $formatter;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getFormatter()
- {
- return $this->formatter;
- }
-
- /**
- * {@inheritdoc}
- */
- public function setDecorated($decorated)
- {
- $this->formatter->setDecorated($decorated);
- }
-
- /**
- * {@inheritdoc}
- */
- public function isDecorated()
- {
- return $this->formatter->isDecorated();
- }
-
- /**
- * {@inheritdoc}
- */
- public function setVerbosity($level)
- {
- $this->verbosity = (int)$level;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getVerbosity()
- {
- return $this->verbosity;
- }
-
- public function isQuiet()
- {
- return self::VERBOSITY_QUIET === $this->verbosity;
- }
-
- public function isVerbose()
- {
- return self::VERBOSITY_VERBOSE <= $this->verbosity;
- }
-
- public function isVeryVerbose()
- {
- return self::VERBOSITY_VERY_VERBOSE <= $this->verbosity;
- }
-
- public function isDebug()
- {
- return self::VERBOSITY_DEBUG <= $this->verbosity;
- }
-
- /**
- * {@inheritdoc}
- */
- public function writeln($messages, $type = self::OUTPUT_NORMAL)
- {
- $this->write($messages, true, $type);
- }
-
- /**
- * {@inheritdoc}
- */
- public function write($messages, $newline = false, $type = self::OUTPUT_NORMAL)
- {
- if (self::VERBOSITY_QUIET === $this->verbosity) {
- return;
- }
-
- $messages = (array)$messages;
-
- foreach ($messages as $message) {
- switch ($type) {
- case self::OUTPUT_NORMAL:
- $message = $this->formatter->format($message);
- break;
- case self::OUTPUT_RAW:
- break;
- case self::OUTPUT_PLAIN:
- $message = strip_tags($this->formatter->format($message));
- break;
- default:
- throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type));
- }
-
- $this->doWrite($message, $newline);
- }
- }
-
- /**
- * 将消息写入到输出。
- * @param string $message 消息
- * @param bool $newline 是否另起一行
- */
- protected function doWrite($message, $newline)
- {
- if (false === @fwrite($this->stream, $message . ($newline ? PHP_EOL : ''))) {
- throw new \RuntimeException('Unable to write output.');
- }
-
- fflush($this->stream);
- }
-
- /**
- * @return resource
- */
- public function getStream()
- {
- return $this->stream;
- }
-
- /**
- * 是否支持着色
- * @return bool
- */
- protected function hasColorSupport()
- {
- if (DIRECTORY_SEPARATOR === '\\') {
- return
- 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR . '.' . PHP_WINDOWS_VERSION_MINOR . '.' . PHP_WINDOWS_VERSION_BUILD)
- || false !== getenv('ANSICON')
- || 'ON' === getenv('ConEmuANSI')
- || 'xterm' === getenv('TERM');
- }
-
- return function_exists('posix_isatty') && @posix_isatty($this->stream);
- }
-}
\ No newline at end of file
diff --git a/library/think/console/helper/descriptor/Console.php b/library/think/console/output/descriptor/Console.php
similarity index 96%
rename from library/think/console/helper/descriptor/Console.php
rename to library/think/console/output/descriptor/Console.php
index 168c31fd..60867ec7 100644
--- a/library/think/console/helper/descriptor/Console.php
+++ b/library/think/console/output/descriptor/Console.php
@@ -9,10 +9,10 @@
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
-namespace think\console\helper\descriptor;
+namespace think\console\output\descriptor;
-use think\console\command\Command;
+use think\console\Command;
use think\Console as ThinkConsole;
class Console
@@ -132,7 +132,7 @@ class Console
foreach ($commands as $name => $command) {
$key = $this->console->extractNamespace($name, 1);
if (!$key) {
- $key = '_global';
+ $key = self::GLOBAL_NAMESPACE;
}
$namespacedCommands[$key][$name] = $command;
diff --git a/library/think/console/output/driver/Buffer.php b/library/think/console/output/driver/Buffer.php
new file mode 100644
index 00000000..a720732a
--- /dev/null
+++ b/library/think/console/output/driver/Buffer.php
@@ -0,0 +1,53 @@
+
+// +----------------------------------------------------------------------
+
+namespace think\console\output\driver;
+
+
+use think\console\Output;
+
+class Buffer
+{
+ /**
+ * @var string
+ */
+ private $buffer = '';
+
+ public function __construct(Output $output)
+ {
+ // do nothing
+ }
+
+ public function fetch()
+ {
+ $content = $this->buffer;
+ $this->buffer = '';
+ return $content;
+ }
+
+ public function write($messages, $newline = false, $options = Output::OUTPUT_NORMAL)
+ {
+ $messages = (array)$messages;
+
+ foreach ($messages as $message) {
+ $this->buffer .= $message;
+ }
+ if ($newline) {
+ $this->buffer .= "\n";
+ }
+ }
+
+ public function renderException(\Exception $e)
+ {
+ // do nothing
+ }
+
+}
\ No newline at end of file
diff --git a/library/think/console/output/driver/Console.php b/library/think/console/output/driver/Console.php
new file mode 100644
index 00000000..7134b3f1
--- /dev/null
+++ b/library/think/console/output/driver/Console.php
@@ -0,0 +1,366 @@
+
+// +----------------------------------------------------------------------
+
+namespace think\console\output\driver;
+
+use think\console\Output;
+use think\console\output\Formatter;
+
+class Console
+{
+
+ /** @var Resource */
+ private $stdout;
+
+ /** @var Formatter */
+ private $formatter;
+
+ private $terminalDimensions;
+
+ /** @var Output */
+ private $output;
+
+
+ public function __construct(Output $output)
+ {
+ $this->output = $output;
+ $this->formatter = new Formatter();
+ $this->stdout = $this->openOutputStream();
+ $decorated = $this->hasColorSupport($this->stdout);
+ $this->formatter->setDecorated($decorated);
+ }
+
+ public function write($messages, $newline = false, $type = Output::OUTPUT_NORMAL, $stream = null)
+ {
+ if (Output::VERBOSITY_QUIET === $this->output->getVerbosity()) {
+ return;
+ }
+
+ $messages = (array)$messages;
+
+ foreach ($messages as $message) {
+ switch ($type) {
+ case Output::OUTPUT_NORMAL:
+ $message = $this->formatter->format($message);
+ break;
+ case Output::OUTPUT_RAW:
+ break;
+ case Output::OUTPUT_PLAIN:
+ $message = strip_tags($this->formatter->format($message));
+ break;
+ default:
+ throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type));
+ }
+
+ $this->doWrite($message, $newline, $stream);
+ }
+ }
+
+ public function renderException(\Exception $e)
+ {
+ $stderr = $this->openErrorStream();
+ $decorated = $this->hasColorSupport($stderr);
+ $this->formatter->setDecorated($decorated);
+
+ do {
+ $title = sprintf(' [%s] ', get_class($e));
+
+ $len = $this->stringWidth($title);
+
+ $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
+
+ if (defined('HHVM_VERSION') && $width > 1 << 31) {
+ $width = 1 << 31;
+ }
+ $lines = [];
+ foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
+ foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
+
+ $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $line)) + 4;
+ $lines[] = [$line, $lineLength];
+
+ $len = max($lineLength, $len);
+ }
+ }
+
+ $messages = ['', ''];
+ $messages[] = $emptyLine = sprintf('%s', str_repeat(' ', $len));
+ $messages[] = sprintf('%s%s', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title))));
+ foreach ($lines as $line) {
+ $messages[] = sprintf(' %s %s', $line[0], str_repeat(' ', $len - $line[1]));
+ }
+ $messages[] = $emptyLine;
+ $messages[] = '';
+ $messages[] = '';
+
+ $this->write($messages, true, Output::OUTPUT_NORMAL, $stderr);
+
+ if (Output::VERBOSITY_VERBOSE <= $this->output->getVerbosity()) {
+ $this->write('Exception trace:', true, Output::OUTPUT_NORMAL, $stderr);
+
+ // exception related properties
+ $trace = $e->getTrace();
+ array_unshift($trace, [
+ 'function' => '',
+ 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a',
+ 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a',
+ 'args' => [],
+ ]);
+
+ for ($i = 0, $count = count($trace); $i < $count; ++$i) {
+ $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
+ $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
+ $function = $trace[$i]['function'];
+ $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
+ $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
+
+ $this->write(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line), true, Output::OUTPUT_NORMAL, $stderr);
+ }
+
+ $this->write('', true, Output::OUTPUT_NORMAL, $stderr);
+ $this->write('', true, Output::OUTPUT_NORMAL, $stderr);
+ }
+ } while ($e = $e->getPrevious());
+
+ }
+
+
+ /**
+ * 获取终端宽度
+ * @return int|null
+ */
+ protected function getTerminalWidth()
+ {
+ $dimensions = $this->getTerminalDimensions();
+
+ return $dimensions[0];
+ }
+
+ /**
+ * 获取终端高度
+ * @return int|null
+ */
+ protected function getTerminalHeight()
+ {
+ $dimensions = $this->getTerminalDimensions();
+
+ return $dimensions[1];
+ }
+
+ /**
+ * 获取当前终端的尺寸
+ * @return array
+ */
+ public function getTerminalDimensions()
+ {
+ if ($this->terminalDimensions) {
+ return $this->terminalDimensions;
+ }
+
+ if ('\\' === DS) {
+ if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
+ return [(int)$matches[1], (int)$matches[2]];
+ }
+ if (preg_match('/^(\d+)x(\d+)$/', $this->getMode(), $matches)) {
+ return [(int)$matches[1], (int)$matches[2]];
+ }
+ }
+
+ if ($sttyString = $this->getSttyColumns()) {
+ if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
+ return [(int)$matches[2], (int)$matches[1]];
+ }
+ if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
+ return [(int)$matches[2], (int)$matches[1]];
+ }
+ }
+
+ return [null, null];
+ }
+
+ /**
+ * 获取stty列数
+ * @return string
+ */
+ private function getSttyColumns()
+ {
+ if (!function_exists('proc_open')) {
+ return null;
+ }
+
+ $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
+ $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
+ if (is_resource($process)) {
+ $info = stream_get_contents($pipes[1]);
+ fclose($pipes[1]);
+ fclose($pipes[2]);
+ proc_close($process);
+
+ return $info;
+ }
+ return null;
+ }
+
+ /**
+ * 获取终端模式
+ * @return string x 或 null
+ */
+ private function getMode()
+ {
+ if (!function_exists('proc_open')) {
+ return null;
+ }
+
+ $descriptorspec = [1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
+ $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
+ if (is_resource($process)) {
+ $info = stream_get_contents($pipes[1]);
+ fclose($pipes[1]);
+ fclose($pipes[2]);
+ proc_close($process);
+
+ if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
+ return $matches[2] . 'x' . $matches[1];
+ }
+ }
+ return null;
+ }
+
+ private function stringWidth($string)
+ {
+ if (!function_exists('mb_strwidth')) {
+ return strlen($string);
+ }
+
+ if (false === $encoding = mb_detect_encoding($string)) {
+ return strlen($string);
+ }
+
+ return mb_strwidth($string, $encoding);
+ }
+
+ private function splitStringByWidth($string, $width)
+ {
+ if (!function_exists('mb_strwidth')) {
+ return str_split($string, $width);
+ }
+
+ if (false === $encoding = mb_detect_encoding($string)) {
+ return str_split($string, $width);
+ }
+
+ $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
+ $lines = [];
+ $line = '';
+ foreach (preg_split('//u', $utf8String) as $char) {
+ if (mb_strwidth($line . $char, 'utf8') <= $width) {
+ $line .= $char;
+ continue;
+ }
+ $lines[] = str_pad($line, $width);
+ $line = $char;
+ }
+ if (strlen($line)) {
+ $lines[] = count($lines) ? str_pad($line, $width) : $line;
+ }
+
+ mb_convert_variables($encoding, 'utf8', $lines);
+
+ return $lines;
+ }
+
+ private function isRunningOS400()
+ {
+ $checks = [
+ function_exists('php_uname') ? php_uname('s') : '',
+ getenv('OSTYPE'),
+ PHP_OS,
+ ];
+ return false !== stripos(implode(';', $checks), 'OS400');
+ }
+
+ /**
+ * 当前环境是否支持写入控制台输出到stdout.
+ *
+ * @return bool
+ */
+ protected function hasStdoutSupport()
+ {
+ return false === $this->isRunningOS400();
+ }
+
+ /**
+ * 当前环境是否支持写入控制台输出到stderr.
+ *
+ * @return bool
+ */
+ protected function hasStderrSupport()
+ {
+ return false === $this->isRunningOS400();
+ }
+
+
+ /**
+ * @return resource
+ */
+ private function openOutputStream()
+ {
+ if (!$this->hasStdoutSupport()) {
+ return fopen('php://output', 'w');
+ }
+ return @fopen('php://stdout', 'w') ?: fopen('php://output', 'w');
+ }
+
+ /**
+ * @return resource
+ */
+ private function openErrorStream()
+ {
+ return fopen($this->hasStderrSupport() ? 'php://stderr' : 'php://output', 'w');
+ }
+
+ /**
+ * 将消息写入到输出。
+ * @param string $message 消息
+ * @param bool $newline 是否另起一行
+ * @param null $stream
+ */
+ protected function doWrite($message, $newline, $stream = null)
+ {
+ if (null === $stream) {
+ $stream = $this->stdout;
+ }
+ if (false === @fwrite($stream, $message . ($newline ? PHP_EOL : ''))) {
+ throw new \RuntimeException('Unable to write output.');
+ }
+
+ fflush($stream);
+ }
+
+ /**
+ * 是否支持着色
+ * @param $stream
+ * @return bool
+ */
+ protected function hasColorSupport($stream)
+ {
+ if (DIRECTORY_SEPARATOR === '\\') {
+ return
+ 0 >= version_compare('10.0.10586', PHP_WINDOWS_VERSION_MAJOR . '.' . PHP_WINDOWS_VERSION_MINOR . '.' . PHP_WINDOWS_VERSION_BUILD)
+ || false !== getenv('ANSICON')
+ || 'ON' === getenv('ConEmuANSI')
+ || 'xterm' === getenv('TERM');
+ }
+
+ return function_exists('posix_isatty') && @posix_isatty($stream);
+ }
+
+}
\ No newline at end of file
diff --git a/library/think/console/output/driver/Nothing.php b/library/think/console/output/driver/Nothing.php
new file mode 100644
index 00000000..673521d7
--- /dev/null
+++ b/library/think/console/output/driver/Nothing.php
@@ -0,0 +1,33 @@
+
+// +----------------------------------------------------------------------
+
+namespace think\console\output\driver;
+
+use think\console\Output;
+
+class Nothing
+{
+
+ public function __construct(Output $output)
+ {
+ // do nothing
+ }
+
+ public function write($messages, $newline = false, $options = Output::OUTPUT_NORMAL)
+ {
+ // do nothing
+ }
+
+ public function renderException(\Exception $e)
+ {
+ // do nothing
+ }
+}
\ No newline at end of file
diff --git a/library/think/console/helper/question/Choice.php b/library/think/console/output/question/Choice.php
similarity index 100%
rename from library/think/console/helper/question/Choice.php
rename to library/think/console/output/question/Choice.php
diff --git a/library/think/console/helper/question/Confirmation.php b/library/think/console/output/question/Confirmation.php
similarity index 100%
rename from library/think/console/helper/question/Confirmation.php
rename to library/think/console/output/question/Confirmation.php
diff --git a/library/think/console/helper/question/Question.php b/library/think/console/output/question/Question.php
similarity index 100%
rename from library/think/console/helper/question/Question.php
rename to library/think/console/output/question/Question.php