去除Console类
This commit is contained in:
@@ -1,987 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | TopThink [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2015 http://www.topthink.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\console\command\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 as ConsoleInput;
|
||||
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;
|
||||
|
||||
class Console
|
||||
{
|
||||
|
||||
private $name;
|
||||
private $version;
|
||||
|
||||
/** @var Command[] */
|
||||
private $commands = [];
|
||||
|
||||
private $wantHelps = false;
|
||||
|
||||
/** @var Command */
|
||||
private $runningCommand;
|
||||
|
||||
private $catchExceptions = true;
|
||||
private $autoExit = true;
|
||||
private $definition;
|
||||
private $helperSet;
|
||||
private $terminalDimensions;
|
||||
private $defaultCommand;
|
||||
|
||||
private static $defaultCommands = [
|
||||
"think\\console\\command\\Help",
|
||||
"think\\console\\command\\Lists",
|
||||
"think\\console\\command\\Build",
|
||||
"think\\console\\command\\make\\Controller",
|
||||
"think\\console\\command\\make\\Model",
|
||||
];
|
||||
|
||||
public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->version = $version;
|
||||
|
||||
$this->defaultCommand = 'list';
|
||||
$this->helperSet = $this->getDefaultHelperSet();
|
||||
$this->definition = $this->getDefaultInputDefinition();
|
||||
|
||||
foreach ($this->getDefaultCommands() as $command) {
|
||||
$this->add($command);
|
||||
}
|
||||
}
|
||||
|
||||
public static function init($run = true)
|
||||
{
|
||||
static $console;
|
||||
if (!$console) {
|
||||
// 实例化console
|
||||
$console = new self('Think Console', '0.1');
|
||||
// 读取指令集
|
||||
if (is_file(CONF_PATH . 'command' . EXT)) {
|
||||
$commands = include CONF_PATH . 'command' . EXT;
|
||||
if (is_array($commands)) {
|
||||
foreach ($commands as $command) {
|
||||
if (class_exists($command) && is_subclass_of($command, "\\think\\console\\command\\Command")) {
|
||||
// 注册指令
|
||||
$console->add(new $command());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($run) {
|
||||
// 运行
|
||||
$console->run();
|
||||
} else {
|
||||
return $console;
|
||||
}
|
||||
}
|
||||
|
||||
public static function call($command, array $parameters = [])
|
||||
{
|
||||
$console = self::init(false);
|
||||
|
||||
array_unshift($parameters, $command);
|
||||
|
||||
$input = new ConsoleInput($parameters);
|
||||
|
||||
$console->find($command)->run($input, new Nothing());
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行当前的指令
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
* @api
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$input = new ConsoleInput();
|
||||
$output = new Output();
|
||||
|
||||
$this->configureIO($input, $output);
|
||||
|
||||
try {
|
||||
$exitCode = $this->doRun($input, $output);
|
||||
} catch (\Exception $e) {
|
||||
if (!$this->catchExceptions) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->renderException($e, $output->getErrorOutput());
|
||||
|
||||
$exitCode = $e->getCode();
|
||||
if (is_numeric($exitCode)) {
|
||||
$exitCode = (int)$exitCode;
|
||||
if (0 === $exitCode) {
|
||||
$exitCode = 1;
|
||||
}
|
||||
} else {
|
||||
$exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->autoExit) {
|
||||
if ($exitCode > 255) {
|
||||
$exitCode = 255;
|
||||
}
|
||||
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行指令
|
||||
* @param ConsoleInput $input
|
||||
* @param Output $output
|
||||
* @return int
|
||||
*/
|
||||
public function doRun(ConsoleInput $input, Output $output)
|
||||
{
|
||||
if (true === $input->hasParameterOption(['--version', '-V'])) {
|
||||
$output->writeln($this->getLongVersion());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$name = $this->getCommandName($input);
|
||||
|
||||
if (true === $input->hasParameterOption(['--help', '-h'])) {
|
||||
if (!$name) {
|
||||
$name = 'help';
|
||||
$input = new ConsoleInput(['help']);
|
||||
} else {
|
||||
$this->wantHelps = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$name) {
|
||||
$name = $this->defaultCommand;
|
||||
$input = new ConsoleInput([$this->defaultCommand]);
|
||||
}
|
||||
|
||||
$command = $this->find($name);
|
||||
|
||||
$this->runningCommand = $command;
|
||||
$exitCode = $this->doRunCommand($command, $input, $output);
|
||||
$this->runningCommand = null;
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置助手集
|
||||
* @param HelperSet $helperSet
|
||||
*/
|
||||
public function setHelperSet(HelperSet $helperSet)
|
||||
{
|
||||
$this->helperSet = $helperSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取助手集
|
||||
* @return HelperSet
|
||||
*/
|
||||
public function getHelperSet()
|
||||
{
|
||||
return $this->helperSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入参数定义
|
||||
* @param InputDefinition $definition
|
||||
*/
|
||||
public function setDefinition(InputDefinition $definition)
|
||||
{
|
||||
$this->definition = $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输入参数定义
|
||||
* @return InputDefinition The InputDefinition instance
|
||||
*/
|
||||
public function getDefinition()
|
||||
{
|
||||
return $this->definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the help message.
|
||||
* @return string A help message.
|
||||
*/
|
||||
public function getHelp()
|
||||
{
|
||||
return $this->getLongVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否捕获异常
|
||||
* @param bool $boolean
|
||||
* @api
|
||||
*/
|
||||
public function setCatchExceptions($boolean)
|
||||
{
|
||||
$this->catchExceptions = (bool)$boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否自动退出
|
||||
* @param bool $boolean
|
||||
* @api
|
||||
*/
|
||||
public function setAutoExit($boolean)
|
||||
{
|
||||
$this->autoExit = (bool)$boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取名称
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置名称
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本
|
||||
* @return string
|
||||
* @api
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置版本
|
||||
* @param string $version
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的版本号
|
||||
* @return string
|
||||
*/
|
||||
public function getLongVersion()
|
||||
{
|
||||
if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
|
||||
return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
|
||||
}
|
||||
|
||||
return '<info>Console Tool</info>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册一个指令
|
||||
* @param string $name
|
||||
* @return Command
|
||||
*/
|
||||
public function register($name)
|
||||
{
|
||||
return $this->add(new Command($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加指令
|
||||
* @param Command[] $commands
|
||||
*/
|
||||
public function addCommands(array $commands)
|
||||
{
|
||||
foreach ($commands as $command) {
|
||||
$this->add($command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个指令
|
||||
* @param Command $command
|
||||
* @return Command
|
||||
*/
|
||||
public function add(Command $command)
|
||||
{
|
||||
$command->setConsole($this);
|
||||
|
||||
if (!$command->isEnabled()) {
|
||||
$command->setConsole(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null === $command->getDefinition()) {
|
||||
throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
|
||||
}
|
||||
|
||||
$this->commands[$command->getName()] = $command;
|
||||
|
||||
foreach ($command->getAliases() as $alias) {
|
||||
$this->commands[$alias] = $command;
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指令
|
||||
* @param string $name 指令名称
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function get($name)
|
||||
{
|
||||
if (!isset($this->commands[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
$command = $this->commands[$name];
|
||||
|
||||
if ($this->wantHelps) {
|
||||
$this->wantHelps = false;
|
||||
|
||||
/** @var HelpCommand $helpCommand */
|
||||
$helpCommand = $this->get('help');
|
||||
$helpCommand->setCommand($command);
|
||||
|
||||
return $helpCommand;
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* 某个指令是否存在
|
||||
* @param string $name 指令民初
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return isset($this->commands[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的命名空间
|
||||
* @return array
|
||||
*/
|
||||
public function getNamespaces()
|
||||
{
|
||||
$namespaces = [];
|
||||
foreach ($this->commands as $command) {
|
||||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
|
||||
|
||||
foreach ($command->getAliases() as $alias) {
|
||||
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique(array_filter($namespaces)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找注册命名空间中的名称或缩写。
|
||||
* @param string $namespace
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function findNamespace($namespace)
|
||||
{
|
||||
$allNamespaces = $this->getNamespaces();
|
||||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
||||
return preg_quote($matches[1]) . '[^:]*';
|
||||
}, $namespace);
|
||||
$namespaces = preg_grep('{^' . $expr . '}', $allNamespaces);
|
||||
|
||||
if (empty($namespaces)) {
|
||||
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
|
||||
|
||||
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
|
||||
if (1 == count($alternatives)) {
|
||||
$message .= "\n\nDid you mean this?\n ";
|
||||
} else {
|
||||
$message .= "\n\nDid you mean one of these?\n ";
|
||||
}
|
||||
|
||||
$message .= implode("\n ", $alternatives);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
$exact = in_array($namespace, $namespaces, true);
|
||||
if (count($namespaces) > 1 && !$exact) {
|
||||
throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
|
||||
}
|
||||
|
||||
return $exact ? $namespace : reset($namespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指令
|
||||
* @param string $name 名称或者别名
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function find($name)
|
||||
{
|
||||
$allCommands = array_keys($this->commands);
|
||||
$expr = preg_replace_callback('{([^:]+|)}', function ($matches) {
|
||||
return preg_quote($matches[1]) . '[^:]*';
|
||||
}, $name);
|
||||
$commands = preg_grep('{^' . $expr . '}', $allCommands);
|
||||
|
||||
if (empty($commands) || count(preg_grep('{^' . $expr . '$}', $commands)) < 1) {
|
||||
if (false !== $pos = strrpos($name, ':')) {
|
||||
$this->findNamespace(substr($name, 0, $pos));
|
||||
}
|
||||
|
||||
$message = sprintf('Command "%s" is not defined.', $name);
|
||||
|
||||
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
|
||||
if (1 == count($alternatives)) {
|
||||
$message .= "\n\nDid you mean this?\n ";
|
||||
} else {
|
||||
$message .= "\n\nDid you mean one of these?\n ";
|
||||
}
|
||||
$message .= implode("\n ", $alternatives);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException($message);
|
||||
}
|
||||
|
||||
if (count($commands) > 1) {
|
||||
$commandList = $this->commands;
|
||||
$commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
|
||||
$commandName = $commandList[$nameOrAlias]->getName();
|
||||
|
||||
return $commandName === $nameOrAlias || !in_array($commandName, $commands);
|
||||
});
|
||||
}
|
||||
|
||||
$exact = in_array($name, $commands, true);
|
||||
if (count($commands) > 1 && !$exact) {
|
||||
$suggestions = $this->getAbbreviationSuggestions(array_values($commands));
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
|
||||
}
|
||||
|
||||
return $this->get($exact ? $name : reset($commands));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的指令
|
||||
* @param string $namespace 命名空间
|
||||
* @return Command[]
|
||||
* @api
|
||||
*/
|
||||
public function all($namespace = null)
|
||||
{
|
||||
if (null === $namespace) {
|
||||
return $this->commands;
|
||||
}
|
||||
|
||||
$commands = [];
|
||||
foreach ($this->commands as $name => $command) {
|
||||
if ($this->extractNamespace($name, substr_count($namespace, ':') + 1) === $namespace) {
|
||||
$commands[$name] = $command;
|
||||
}
|
||||
}
|
||||
|
||||
return $commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可能的指令名
|
||||
* @param array $names
|
||||
* @return array
|
||||
*/
|
||||
public static function getAbbreviations($names)
|
||||
{
|
||||
$abbrevs = [];
|
||||
foreach ($names as $name) {
|
||||
for ($len = strlen($name); $len > 0; --$len) {
|
||||
$abbrev = substr($name, 0, $len);
|
||||
$abbrevs[$abbrev][] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $abbrevs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 呈现捕获的异常
|
||||
* @param \Exception $e
|
||||
* @param Stream $output
|
||||
*/
|
||||
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('<error>%s</error>', str_repeat(' ', $len)));
|
||||
$messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
|
||||
foreach ($lines as $line) {
|
||||
$messages[] = $formatter->format(sprintf('<error> %s %s</error>', $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('<comment>Exception trace:</comment>');
|
||||
|
||||
// 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 <info>%s:%s</info>', $class, $type, $function, $file, $line));
|
||||
}
|
||||
|
||||
$output->writeln('');
|
||||
$output->writeln('');
|
||||
}
|
||||
} while ($e = $e->getPrevious());
|
||||
|
||||
if (null !== $this->runningCommand) {
|
||||
$output->writeln(sprintf('<info>%s</info>', 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 ConsoleInput $input 输入实例
|
||||
* @param Output $output 输出实例
|
||||
*/
|
||||
protected function configureIO(ConsoleInput $input, Output $output)
|
||||
{
|
||||
if (true === $input->hasParameterOption(['--ansi'])) {
|
||||
$output->setDecorated(true);
|
||||
} elseif (true === $input->hasParameterOption(['--no-ansi'])) {
|
||||
$output->setDecorated(false);
|
||||
}
|
||||
|
||||
if (true === $input->hasParameterOption(['--no-interaction', '-n'])) {
|
||||
$input->setInteractive(false);
|
||||
} elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) {
|
||||
$inputStream = $this->getHelperSet()->get('question')->getInputStream();
|
||||
if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) {
|
||||
$input->setInteractive(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (true === $input->hasParameterOption(['--quiet', '-q'])) {
|
||||
$output->setVerbosity(Output::VERBOSITY_QUIET);
|
||||
} else {
|
||||
if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
|
||||
$output->setVerbosity(Output::VERBOSITY_DEBUG);
|
||||
} elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
|
||||
$output->setVerbosity(Output::VERBOSITY_VERY_VERBOSE);
|
||||
} elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
|
||||
$output->setVerbosity(Output::VERBOSITY_VERBOSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行指令
|
||||
* @param Command $command 指令实例
|
||||
* @param ConsoleInput $input 输入实例
|
||||
* @param Output $output 输出实例
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function doRunCommand(Command $command, ConsoleInput $input, Output $output)
|
||||
{
|
||||
return $command->run($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指令的基础名称
|
||||
* @param ConsoleInput $input
|
||||
* @return string
|
||||
*/
|
||||
protected function getCommandName(ConsoleInput $input)
|
||||
{
|
||||
return $input->getFirstArgument();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认输入定义
|
||||
* @return InputDefinition
|
||||
*/
|
||||
protected function getDefaultInputDefinition()
|
||||
{
|
||||
return new InputDefinition([
|
||||
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
|
||||
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
|
||||
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this console version'),
|
||||
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
|
||||
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
|
||||
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
|
||||
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
|
||||
new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认命令
|
||||
* @return Command[] An array of default Command instances
|
||||
*/
|
||||
protected function getDefaultCommands()
|
||||
{
|
||||
$defaultCommands = [];
|
||||
|
||||
foreach (self::$defaultCommands as $classname) {
|
||||
if (class_exists($classname) && is_subclass_of($classname, "think\\console\\command\\Command")) {
|
||||
$defaultCommands[] = new $classname();
|
||||
}
|
||||
}
|
||||
|
||||
return $defaultCommands;
|
||||
}
|
||||
|
||||
public static function addDefaultCommands(array $classnames)
|
||||
{
|
||||
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 <width>x<height> 或 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
|
||||
* @return string
|
||||
*/
|
||||
private function getAbbreviationSuggestions($abbrevs)
|
||||
{
|
||||
return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回命名空间部分
|
||||
* @param string $name 指令
|
||||
* @param string $limit 部分的命名空间的最大数量
|
||||
* @return string
|
||||
*/
|
||||
public function extractNamespace($name, $limit = null)
|
||||
{
|
||||
$parts = explode(':', $name);
|
||||
array_pop($parts);
|
||||
|
||||
return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找可替代的建议
|
||||
* @param string $name
|
||||
* @param array|\Traversable $collection
|
||||
* @return array
|
||||
*/
|
||||
private function findAlternatives($name, $collection)
|
||||
{
|
||||
$threshold = 1e3;
|
||||
$alternatives = [];
|
||||
|
||||
$collectionParts = [];
|
||||
foreach ($collection as $item) {
|
||||
$collectionParts[$item] = explode(':', $item);
|
||||
}
|
||||
|
||||
foreach (explode(':', $name) as $i => $subname) {
|
||||
foreach ($collectionParts as $collectionName => $parts) {
|
||||
$exists = isset($alternatives[$collectionName]);
|
||||
if (!isset($parts[$i]) && $exists) {
|
||||
$alternatives[$collectionName] += $threshold;
|
||||
continue;
|
||||
} elseif (!isset($parts[$i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lev = levenshtein($subname, $parts[$i]);
|
||||
if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
|
||||
$alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
|
||||
} elseif ($exists) {
|
||||
$alternatives[$collectionName] += $threshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$lev = levenshtein($name, $item);
|
||||
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
|
||||
$alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
|
||||
}
|
||||
}
|
||||
|
||||
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) {
|
||||
return $lev < 2 * $threshold;
|
||||
});
|
||||
asort($alternatives);
|
||||
|
||||
return array_keys($alternatives);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认的指令
|
||||
* @param string $commandName The Command name
|
||||
*/
|
||||
public function setDefaultCommand($commandName)
|
||||
{
|
||||
$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
|
||||
* @return array
|
||||
*/
|
||||
private function extractAllNamespaces($name)
|
||||
{
|
||||
$parts = explode(':', $name, -1);
|
||||
$namespaces = [];
|
||||
|
||||
foreach ($parts as $part) {
|
||||
if (count($namespaces)) {
|
||||
$namespaces[] = end($namespaces) . ':' . $part;
|
||||
} else {
|
||||
$namespaces[] = $part;
|
||||
}
|
||||
}
|
||||
|
||||
return $namespaces;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,471 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console;
|
||||
|
||||
use think\console\input\Definition;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
|
||||
class Input
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Definition
|
||||
*/
|
||||
protected $definition;
|
||||
|
||||
/**
|
||||
* @var Option[]
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @var Argument[]
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
protected $interactive = true;
|
||||
|
||||
private $tokens;
|
||||
private $parsed;
|
||||
|
||||
|
||||
public function __construct($argv = null)
|
||||
{
|
||||
if (null === $argv) {
|
||||
$argv = $_SERVER['argv'];
|
||||
// 去除命令名
|
||||
array_shift($argv);
|
||||
}
|
||||
|
||||
$this->tokens = $argv;
|
||||
|
||||
$this->definition = new Definition();
|
||||
}
|
||||
|
||||
|
||||
protected function setTokens(array $tokens)
|
||||
{
|
||||
$this->tokens = $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定实例
|
||||
* @param Definition $definition A InputDefinition instance
|
||||
*/
|
||||
public function bind(Definition $definition)
|
||||
{
|
||||
$this->arguments = [];
|
||||
$this->options = [];
|
||||
$this->definition = $definition;
|
||||
|
||||
$this->parse();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析参数
|
||||
*/
|
||||
protected function parse()
|
||||
{
|
||||
$parseOptions = true;
|
||||
$this->parsed = $this->tokens;
|
||||
while (null !== $token = array_shift($this->parsed)) {
|
||||
if ($parseOptions && '' == $token) {
|
||||
$this->parseArgument($token);
|
||||
} elseif ($parseOptions && '--' == $token) {
|
||||
$parseOptions = false;
|
||||
} elseif ($parseOptions && 0 === strpos($token, '--')) {
|
||||
$this->parseLongOption($token);
|
||||
} elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
|
||||
$this->parseShortOption($token);
|
||||
} else {
|
||||
$this->parseArgument($token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析短选项
|
||||
* @param string $token 当前的指令.
|
||||
*/
|
||||
private function parseShortOption($token)
|
||||
{
|
||||
$name = substr($token, 1);
|
||||
|
||||
if (strlen($name) > 1) {
|
||||
if ($this->definition->hasShortcut($name[0])
|
||||
&& $this->definition->getOptionForShortcut($name[0])->acceptValue()
|
||||
) {
|
||||
$this->addShortOption($name[0], substr($name, 1));
|
||||
} else {
|
||||
$this->parseShortOptionSet($name);
|
||||
}
|
||||
} else {
|
||||
$this->addShortOption($name, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析短选项
|
||||
* @param string $name 当前指令
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function parseShortOptionSet($name)
|
||||
{
|
||||
$len = strlen($name);
|
||||
for ($i = 0; $i < $len; ++$i) {
|
||||
if (!$this->definition->hasShortcut($name[$i])) {
|
||||
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
|
||||
}
|
||||
|
||||
$option = $this->definition->getOptionForShortcut($name[$i]);
|
||||
if ($option->acceptValue()) {
|
||||
$this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1));
|
||||
|
||||
break;
|
||||
} else {
|
||||
$this->addLongOption($option->getName(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析完整选项
|
||||
* @param string $token 当前指令
|
||||
*/
|
||||
private function parseLongOption($token)
|
||||
{
|
||||
$name = substr($token, 2);
|
||||
|
||||
if (false !== $pos = strpos($name, '=')) {
|
||||
$this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1));
|
||||
} else {
|
||||
$this->addLongOption($name, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析参数
|
||||
* @param string $token 当前指令
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function parseArgument($token)
|
||||
{
|
||||
$c = count($this->arguments);
|
||||
|
||||
if ($this->definition->hasArgument($c)) {
|
||||
$arg = $this->definition->getArgument($c);
|
||||
|
||||
$this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
|
||||
|
||||
} elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
|
||||
$arg = $this->definition->getArgument($c - 1);
|
||||
|
||||
$this->arguments[$arg->getName()][] = $token;
|
||||
} else {
|
||||
throw new \RuntimeException('Too many arguments.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加一个短选项的值
|
||||
* @param string $shortcut 短名称
|
||||
* @param mixed $value 值
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function addShortOption($shortcut, $value)
|
||||
{
|
||||
if (!$this->definition->hasShortcut($shortcut)) {
|
||||
throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
|
||||
}
|
||||
|
||||
$this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个完整选项的值
|
||||
* @param string $name 选项名
|
||||
* @param mixed $value 值
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function addLongOption($name, $value)
|
||||
{
|
||||
if (!$this->definition->hasOption($name)) {
|
||||
throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name));
|
||||
}
|
||||
|
||||
$option = $this->definition->getOption($name);
|
||||
|
||||
if (false === $value) {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
if (null !== $value && !$option->acceptValue()) {
|
||||
throw new \RuntimeException(sprintf('The "--%s" option does not accept a value.', $name, $value));
|
||||
}
|
||||
|
||||
if (null === $value && $option->acceptValue() && count($this->parsed)) {
|
||||
$next = array_shift($this->parsed);
|
||||
if (isset($next[0]) && '-' !== $next[0]) {
|
||||
$value = $next;
|
||||
} elseif (empty($next)) {
|
||||
$value = '';
|
||||
} else {
|
||||
array_unshift($this->parsed, $next);
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $value) {
|
||||
if ($option->isValueRequired()) {
|
||||
throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name));
|
||||
}
|
||||
|
||||
if (!$option->isArray()) {
|
||||
$value = $option->isValueOptional() ? $option->getDefault() : true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($option->isArray()) {
|
||||
$this->options[$name][] = $value;
|
||||
} else {
|
||||
$this->options[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取第一个参数
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFirstArgument()
|
||||
{
|
||||
foreach ($this->tokens as $token) {
|
||||
if ($token && '-' === $token[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $token;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查原始参数是否包含某个值
|
||||
* @param string|array $values 需要检查的值
|
||||
* @return bool
|
||||
*/
|
||||
public function hasParameterOption($values)
|
||||
{
|
||||
$values = (array)$values;
|
||||
|
||||
foreach ($this->tokens as $token) {
|
||||
foreach ($values as $value) {
|
||||
if ($token === $value || 0 === strpos($token, $value . '=')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始选项的值
|
||||
* @param string|array $values 需要检查的值
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed The option value
|
||||
*/
|
||||
public function getParameterOption($values, $default = false)
|
||||
{
|
||||
$values = (array)$values;
|
||||
$tokens = $this->tokens;
|
||||
|
||||
while (0 < count($tokens)) {
|
||||
$token = array_shift($tokens);
|
||||
|
||||
foreach ($values as $value) {
|
||||
if ($token === $value || 0 === strpos($token, $value . '=')) {
|
||||
if (false !== $pos = strpos($token, '=')) {
|
||||
return substr($token, $pos + 1);
|
||||
}
|
||||
|
||||
return array_shift($tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证输入
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) {
|
||||
throw new \RuntimeException('Not enough arguments.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查输入是否是交互的
|
||||
* @return bool
|
||||
*/
|
||||
public function isInteractive()
|
||||
{
|
||||
return $this->interactive;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入的交互
|
||||
* @param bool
|
||||
*/
|
||||
public function setInteractive($interactive)
|
||||
{
|
||||
$this->interactive = (bool)$interactive;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的参数
|
||||
* @return Argument[]
|
||||
*/
|
||||
public function getArguments()
|
||||
{
|
||||
return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称获取参数
|
||||
* @param string $name 参数名
|
||||
* @return mixed
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getArgument($name)
|
||||
{
|
||||
if (!$this->definition->hasArgument($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
|
||||
}
|
||||
|
||||
return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)
|
||||
->getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置参数的值
|
||||
* @param string $name 参数名
|
||||
* @param string $value 值
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setArgument($name, $value)
|
||||
{
|
||||
if (!$this->definition->hasArgument($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
|
||||
}
|
||||
|
||||
$this->arguments[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在某个参数
|
||||
* @param string|int $name 参数名或位置
|
||||
* @return bool
|
||||
*/
|
||||
public function hasArgument($name)
|
||||
{
|
||||
return $this->definition->hasArgument($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的选项
|
||||
* @return Option[]
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return array_merge($this->definition->getOptionDefaults(), $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选项值
|
||||
* @param string $name 选项名称
|
||||
* @return mixed
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getOption($name)
|
||||
{
|
||||
if (!$this->definition->hasOption($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
|
||||
}
|
||||
|
||||
return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选项值
|
||||
* @param string $name 选项名
|
||||
* @param string|bool $value 值
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setOption($name, $value)
|
||||
{
|
||||
if (!$this->definition->hasOption($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
|
||||
}
|
||||
|
||||
$this->options[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有某个选项
|
||||
* @param string $name 选项名
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOption($name)
|
||||
{
|
||||
return $this->definition->hasOption($name) && isset($this->options[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义指令
|
||||
* @param string $token
|
||||
* @return string
|
||||
*/
|
||||
public function escapeToken($token)
|
||||
{
|
||||
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回传递给命令的参数的字符串
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$tokens = array_map(function ($token) {
|
||||
if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
|
||||
return $match[1] . $this->escapeToken($match[2]);
|
||||
}
|
||||
|
||||
if ($token && $token[0] !== '-') {
|
||||
return $this->escapeToken($token);
|
||||
}
|
||||
|
||||
return $token;
|
||||
}, $this->tokens);
|
||||
|
||||
return implode(' ', $tokens);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2004-2016 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,86 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console;
|
||||
|
||||
use think\console\output\Formatter;
|
||||
use think\console\output\Stream;
|
||||
|
||||
class Output extends Stream
|
||||
{
|
||||
|
||||
/** @var Stream */
|
||||
private $stderr;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$outputStream = 'php://stdout';
|
||||
if (!$this->hasStdoutSupport()) {
|
||||
$outputStream = 'php://output';
|
||||
}
|
||||
|
||||
parent::__construct(fopen($outputStream, 'w'));
|
||||
|
||||
$this->stderr = new Stream(fopen('php://stderr', 'w'), $this->getFormatter());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setDecorated($decorated)
|
||||
{
|
||||
parent::setDecorated($decorated);
|
||||
$this->stderr->setDecorated($decorated);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setFormatter(Formatter $formatter)
|
||||
{
|
||||
parent::setFormatter($formatter);
|
||||
$this->stderr->setFormatter($formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setVerbosity($level)
|
||||
{
|
||||
parent::setVerbosity($level);
|
||||
$this->stderr->setVerbosity($level);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getErrorOutput()
|
||||
{
|
||||
return $this->stderr;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setErrorOutput(Output $error)
|
||||
{
|
||||
$this->stderr = $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前环境是否支持控制台输出写入标准输出。
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasStdoutSupport()
|
||||
{
|
||||
return ('OS400' != php_uname('s'));
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
console 工具使用 hiddeninput.exe 在 windows 上隐藏密码输入,该二进制文件由第三方提供,相关源码和其他细节可以在 [Hidden Input](https://github.com/Seldaek/hidden-input) 找到。
|
||||
Binary file not shown.
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command;
|
||||
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
|
||||
class Build extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('build')
|
||||
->setDefinition([
|
||||
new Option('config', null, Option::VALUE_OPTIONAL, "build.php path"),
|
||||
new Option('module', null, Option::VALUE_OPTIONAL, "module name")
|
||||
])
|
||||
->setDescription('Build Application Dirs');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
if ($input->hasOption('module')) {
|
||||
\think\Build::module($input->getOption('module'));
|
||||
$output->writeln("Successed");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->hasOption('config')) {
|
||||
$build = include $input->getOption('config');
|
||||
} else {
|
||||
$build = include APP_PATH . 'build.php';
|
||||
}
|
||||
if (empty($build)) {
|
||||
$output->writeln("Build Config Is Empty");
|
||||
return;
|
||||
}
|
||||
\think\Build::run($build);
|
||||
$output->writeln("Successed");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,501 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command;
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
/** @var Console */
|
||||
private $console;
|
||||
private $name;
|
||||
private $aliases = [];
|
||||
private $definition;
|
||||
private $help;
|
||||
private $description;
|
||||
private $ignoreValidationErrors = false;
|
||||
private $consoleDefinitionMerged = false;
|
||||
private $consoleDefinitionMergedWithArgs = false;
|
||||
private $code;
|
||||
private $synopsis = [];
|
||||
private $usages = [];
|
||||
|
||||
/** @var HelperSet */
|
||||
private $helperSet;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string|null $name 命令名称,如果没有设置则比如在 configure() 里设置
|
||||
* @throws \LogicException
|
||||
* @api
|
||||
*/
|
||||
public function __construct($name = null)
|
||||
{
|
||||
$this->definition = new Definition();
|
||||
|
||||
if (null !== $name) {
|
||||
$this->setName($name);
|
||||
}
|
||||
|
||||
$this->configure();
|
||||
|
||||
if (!$this->name) {
|
||||
throw new \LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($this)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略验证错误
|
||||
*/
|
||||
public function ignoreValidationErrors()
|
||||
{
|
||||
$this->ignoreValidationErrors = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置控制台
|
||||
* @param Console $console
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取控制台
|
||||
* @return Console
|
||||
* @api
|
||||
*/
|
||||
public function getConsole()
|
||||
{
|
||||
return $this->console;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有效
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置指令
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行指令
|
||||
* @param Input $input
|
||||
* @param Output $output
|
||||
* @return null|int
|
||||
* @throws \LogicException
|
||||
* @see setCode()
|
||||
*/
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
throw new \LogicException('You must override the execute() method in the concrete command class.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户验证
|
||||
* @param Input $input
|
||||
* @param Output $output
|
||||
*/
|
||||
protected function interact(Input $input, Output $output)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @param Input $input An InputInterface instance
|
||||
* @param Output $output An OutputInterface instance
|
||||
*/
|
||||
protected function initialize(Input $input, Output $output)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行
|
||||
* @param Input $input
|
||||
* @param Output $output
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
* @see setCode()
|
||||
* @see execute()
|
||||
*/
|
||||
public function run(Input $input, Output $output)
|
||||
{
|
||||
$this->getSynopsis(true);
|
||||
$this->getSynopsis(false);
|
||||
|
||||
$this->mergeConsoleDefinition();
|
||||
|
||||
try {
|
||||
$input->bind($this->definition);
|
||||
} catch (\Exception $e) {
|
||||
if (!$this->ignoreValidationErrors) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$this->initialize($input, $output);
|
||||
|
||||
if ($input->isInteractive()) {
|
||||
$this->interact($input, $output);
|
||||
}
|
||||
|
||||
$input->validate();
|
||||
|
||||
if ($this->code) {
|
||||
$statusCode = call_user_func($this->code, $input, $output);
|
||||
} else {
|
||||
$statusCode = $this->execute($input, $output);
|
||||
}
|
||||
|
||||
return is_numeric($statusCode) ? (int)$statusCode : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置执行代码
|
||||
* @param callable $code callable(InputInterface $input, OutputInterface $output)
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
* @see execute()
|
||||
*/
|
||||
public function setCode(callable $code)
|
||||
{
|
||||
if (!is_callable($code)) {
|
||||
throw new \InvalidArgumentException('Invalid callable provided to Command::setCode.');
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID >= 50400 && $code instanceof \Closure) {
|
||||
$r = new \ReflectionFunction($code);
|
||||
if (null === $r->getClosureThis()) {
|
||||
$code = \Closure::bind($code, $this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并参数定义
|
||||
* @param bool $mergeArgs
|
||||
*/
|
||||
public function mergeConsoleDefinition($mergeArgs = true)
|
||||
{
|
||||
if (null === $this->console
|
||||
|| (true === $this->consoleDefinitionMerged
|
||||
&& ($this->consoleDefinitionMergedWithArgs || !$mergeArgs))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($mergeArgs) {
|
||||
$currentArguments = $this->definition->getArguments();
|
||||
$this->definition->setArguments($this->console->getDefinition()->getArguments());
|
||||
$this->definition->addArguments($currentArguments);
|
||||
}
|
||||
|
||||
$this->definition->addOptions($this->console->getDefinition()->getOptions());
|
||||
|
||||
$this->consoleDefinitionMerged = true;
|
||||
if ($mergeArgs) {
|
||||
$this->consoleDefinitionMergedWithArgs = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置参数定义
|
||||
* @param array|Definition $definition
|
||||
* @return Command
|
||||
* @api
|
||||
*/
|
||||
public function setDefinition($definition)
|
||||
{
|
||||
if ($definition instanceof Definition) {
|
||||
$this->definition = $definition;
|
||||
} else {
|
||||
$this->definition->setDefinition($definition);
|
||||
}
|
||||
|
||||
$this->consoleDefinitionMerged = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数定义
|
||||
* @return Definition
|
||||
* @api
|
||||
*/
|
||||
public function getDefinition()
|
||||
{
|
||||
return $this->definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前指令的参数定义
|
||||
* @return Definition
|
||||
*/
|
||||
public function getNativeDefinition()
|
||||
{
|
||||
return $this->getDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加参数
|
||||
* @param string $name 名称
|
||||
* @param int $mode 类型
|
||||
* @param string $description 描述
|
||||
* @param mixed $default 默认值
|
||||
* @return Command
|
||||
*/
|
||||
public function addArgument($name, $mode = null, $description = '', $default = null)
|
||||
{
|
||||
$this->definition->addArgument(new Argument($name, $mode, $description, $default));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加选项
|
||||
* @param string $name 选项名称
|
||||
* @param string $shortcut 别名
|
||||
* @param int $mode 类型
|
||||
* @param string $description 描述
|
||||
* @param mixed $default 默认值
|
||||
* @return Command
|
||||
*/
|
||||
public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null)
|
||||
{
|
||||
$this->definition->addOption(new Option($name, $shortcut, $mode, $description, $default));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置指令名称
|
||||
* @param string $name
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->validateName($name);
|
||||
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指令名称
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置描述
|
||||
* @param string $description
|
||||
* @return Command
|
||||
*/
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取描述
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置帮助信息
|
||||
* @param string $help
|
||||
* @return Command
|
||||
*/
|
||||
public function setHelp($help)
|
||||
{
|
||||
$this->help = $help;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取帮助信息
|
||||
* @return string
|
||||
*/
|
||||
public function getHelp()
|
||||
{
|
||||
return $this->help;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 描述信息
|
||||
* @return string
|
||||
*/
|
||||
public function getProcessedHelp()
|
||||
{
|
||||
$name = $this->name;
|
||||
|
||||
$placeholders = [
|
||||
'%command.name%',
|
||||
'%command.full_name%',
|
||||
];
|
||||
$replacements = [
|
||||
$name,
|
||||
$_SERVER['PHP_SELF'] . ' ' . $name,
|
||||
];
|
||||
|
||||
return str_replace($placeholders, $replacements, $this->getHelp());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置别名
|
||||
* @param string[] $aliases
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setAliases($aliases)
|
||||
{
|
||||
if (!is_array($aliases) && !$aliases instanceof \Traversable) {
|
||||
throw new \InvalidArgumentException('$aliases must be an array or an instance of \Traversable');
|
||||
}
|
||||
|
||||
foreach ($aliases as $alias) {
|
||||
$this->validateName($alias);
|
||||
}
|
||||
|
||||
$this->aliases = $aliases;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取别名
|
||||
* @return array
|
||||
*/
|
||||
public function getAliases()
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取简介
|
||||
* @param bool $short 是否简单的
|
||||
* @return string
|
||||
*/
|
||||
public function getSynopsis($short = false)
|
||||
{
|
||||
$key = $short ? 'short' : 'long';
|
||||
|
||||
if (!isset($this->synopsis[$key])) {
|
||||
$this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short)));
|
||||
}
|
||||
|
||||
return $this->synopsis[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用法介绍
|
||||
* @param string $usage
|
||||
*/
|
||||
public function addUsage($usage)
|
||||
{
|
||||
if (0 !== strpos($usage, $this->name)) {
|
||||
$usage = sprintf('%s %s', $this->name, $usage);
|
||||
}
|
||||
|
||||
$this->usages[] = $usage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用法介绍
|
||||
* @return array
|
||||
*/
|
||||
public function getUsages()
|
||||
{
|
||||
return $this->usages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取助手
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getHelper($name)
|
||||
{
|
||||
return $this->helperSet->get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证指令名称
|
||||
* @param string $name
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function validateName($name)
|
||||
{
|
||||
if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
|
||||
throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command;
|
||||
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument as InputArgument;
|
||||
use think\console\input\Option as InputOption;
|
||||
use think\console\Output;
|
||||
use think\console\helper\Descriptor as DescriptorHelper;
|
||||
|
||||
class Help extends Command
|
||||
{
|
||||
|
||||
private $command;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->ignoreValidationErrors();
|
||||
|
||||
$this->setName('help')->setDefinition([
|
||||
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'),
|
||||
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
|
||||
])->setDescription('Displays help for a command')->setHelp(<<<EOF
|
||||
The <info>%command.name%</info> command displays help for a given command:
|
||||
|
||||
<info>php %command.full_name% list</info>
|
||||
|
||||
To display the list of available commands, please use the <info>list</info> command.
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the command.
|
||||
* @param Command $command The command to set
|
||||
*/
|
||||
public function setCommand(Command $command)
|
||||
{
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
if (null === $this->command) {
|
||||
$this->command = $this->getConsole()->find($input->getArgument('command_name'));
|
||||
}
|
||||
|
||||
|
||||
$helper = new DescriptorHelper();
|
||||
$helper->describe($output, $this->command, [
|
||||
'raw_text' => $input->getOption('raw'),
|
||||
]);
|
||||
|
||||
$this->command = null;
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace 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
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('list')->setDefinition($this->createDefinition())->setDescription('Lists commands')->setHelp(<<<EOF
|
||||
The <info>%command.name%</info> command lists all commands:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
You can also display the commands for a specific namespace:
|
||||
|
||||
<info>php %command.full_name% test</info>
|
||||
|
||||
It's also possible to get raw list of commands (useful for embedding command runner):
|
||||
|
||||
<info>php %command.full_name% --raw</info>
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNativeDefinition()
|
||||
{
|
||||
return $this->createDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
|
||||
$helper = new DescriptorHelper();
|
||||
$helper->describe($output, $this->getConsole(), [
|
||||
'raw_text' => $input->getOption('raw'),
|
||||
'namespace' => $input->getArgument('namespace'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
private function createDefinition()
|
||||
{
|
||||
return new InputDefinition([
|
||||
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
|
||||
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list')
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 刘志淳 <chun@engineer.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command;
|
||||
|
||||
class Make extends Command
|
||||
{
|
||||
// 创建目录
|
||||
protected static function buildDir($dir)
|
||||
{
|
||||
if (!is_dir(APP_PATH . $dir)) {
|
||||
mkdir(APP_PATH . strtolower($dir), 0777, true);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建文件
|
||||
protected static function buildFile($file, $content)
|
||||
{
|
||||
if (is_file(APP_PATH . $file)) {
|
||||
exception('file already exists');
|
||||
}
|
||||
file_put_contents(APP_PATH . $file, $content);
|
||||
}
|
||||
|
||||
protected static function formatNameSpace($namespace)
|
||||
{
|
||||
$namespace = explode('\\', $namespace);
|
||||
|
||||
foreach ($namespace as $key => $value) {
|
||||
if ($key == count($namespace) - 1) {
|
||||
$newNameSpace[1] = $value;
|
||||
} else {
|
||||
$newNameSpace[0][$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $newNameSpace;
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: 刘志淳 <chun@engineer.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command\make;
|
||||
|
||||
use think\App;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
|
||||
class Controller extends \think\console\command\Make
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('make:controller')
|
||||
->setDescription('Create a new controller class')
|
||||
->addArgument('namespace', Argument::OPTIONAL, null)
|
||||
->addOption('module', 'm', Option::VALUE_OPTIONAL, 'Module Name', null)
|
||||
->addOption('extend', 'e', Option::VALUE_OPTIONAL, 'Base on Controller class', null);
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$namespace = $input->getArgument('namespace');
|
||||
$module = $input->getOption('module');
|
||||
|
||||
|
||||
// 处理命名空间
|
||||
if (!empty($module)) {
|
||||
$namespace = App::$namespace . "\\" . $module . "\\" . 'controller' . "\\" . $namespace;
|
||||
}
|
||||
|
||||
// 处理继承
|
||||
$extend = $input->getOption('extend');
|
||||
|
||||
if (empty($extend)) {
|
||||
$extend = "\\think\\Controller";
|
||||
} else {
|
||||
if (!preg_match("/\\\/", $extend)) {
|
||||
if (!empty($module)) {
|
||||
$extend = "\\" . App::$namespace . "\\" . $module . "\\" . 'controller' . "\\" . $extend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$result = $this->build($namespace, $extend);
|
||||
$output->writeln("output:" . $result);
|
||||
}
|
||||
|
||||
private function build($namespace, $extend)
|
||||
{
|
||||
$tpl = file_get_contents(THINK_PATH . 'tpl' . DS . 'make_controller.tpl');
|
||||
|
||||
// comminute namespace
|
||||
$allNamespace = self::formatNameSpace($namespace);
|
||||
$namespace = implode('\\', $allNamespace[0]);
|
||||
$className = ucwords($allNamespace[1]);
|
||||
|
||||
// 处理内容
|
||||
$content = str_replace("{%extend%}", $extend,
|
||||
str_replace("{%className%}", $className,
|
||||
str_replace("{%namespace%}", $namespace, $tpl)
|
||||
)
|
||||
);
|
||||
|
||||
// 处理文件夹
|
||||
$path = '';
|
||||
foreach ($allNamespace[0] as $key => $value) {
|
||||
if ($key >= 1) {
|
||||
self::buildDir($path . $value);
|
||||
$path .= $value . "\\";
|
||||
}
|
||||
}
|
||||
|
||||
// 处理文件
|
||||
$file = $path . $className . '.php';
|
||||
self::buildFile($file, $content);
|
||||
|
||||
return APP_PATH . $file;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\command\make;
|
||||
|
||||
|
||||
use think\console\command\Command;
|
||||
|
||||
|
||||
class Model extends Command
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct("make:model");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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<bg=blue;fg=white> %s </> <fg=blue>%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<bg=red;fg=white> %s </> ", $this->getBorder($id), $errorPrefix);
|
||||
$this->started[$id]['err'] = true;
|
||||
}
|
||||
|
||||
$message .= str_replace("\n", sprintf("\n%s<bg=red;fg=white> %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<bg=green;fg=white> %s </> ", $this->getBorder($id), $prefix);
|
||||
$this->started[$id]['out'] = true;
|
||||
}
|
||||
|
||||
$message .= str_replace("\n", sprintf("\n%s<bg=green;fg=white> %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<bg=green;fg=white> %s </> <fg=green>%s</>\n", $trailingEOL, $this->getBorder($id), $prefix, $message);
|
||||
}
|
||||
|
||||
$message = sprintf("%s%s<bg=red;fg=white> %s </> <fg=red>%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('<bg=%s> </>', $this->colors[$this->started[$id]['border']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'debug_formatter';
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | TopThink [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2015 http://www.topthink.com All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: zhangyajun <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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('<error>%s</error>', $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';
|
||||
}
|
||||
}
|
||||
@@ -1,394 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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(" [<info>%-${width}s</info>] %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>' . $error->getMessage() . '</error>';
|
||||
}
|
||||
|
||||
$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('<hl>' . substr($matches[$ofs], $i) . '</hl>');
|
||||
$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;
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\helper\descriptor;
|
||||
|
||||
|
||||
use think\console\command\Command;
|
||||
use think\Console as ThinkConsole;
|
||||
|
||||
class Console
|
||||
{
|
||||
|
||||
const GLOBAL_NAMESPACE = '_global';
|
||||
|
||||
/**
|
||||
* @var ThinkConsole
|
||||
*/
|
||||
private $console;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private $namespace;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $namespaces;
|
||||
|
||||
/**
|
||||
* @var Command[]
|
||||
*/
|
||||
private $commands;
|
||||
|
||||
/**
|
||||
* @var Command[]
|
||||
*/
|
||||
private $aliases;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param ThinkConsole $console
|
||||
* @param string|null $namespace
|
||||
*/
|
||||
public function __construct(ThinkConsole $console, $namespace = null)
|
||||
{
|
||||
$this->console = $console;
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getNamespaces()
|
||||
{
|
||||
if (null === $this->namespaces) {
|
||||
$this->inspectConsole();
|
||||
}
|
||||
|
||||
return $this->namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Command[]
|
||||
*/
|
||||
public function getCommands()
|
||||
{
|
||||
if (null === $this->commands) {
|
||||
$this->inspectConsole();
|
||||
}
|
||||
|
||||
return $this->commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return Command
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getCommand($name)
|
||||
{
|
||||
if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('Command %s does not exist.', $name));
|
||||
}
|
||||
|
||||
return isset($this->commands[$name]) ? $this->commands[$name] : $this->aliases[$name];
|
||||
}
|
||||
|
||||
private function inspectConsole()
|
||||
{
|
||||
$this->commands = [];
|
||||
$this->namespaces = [];
|
||||
|
||||
$all = $this->console->all($this->namespace ? $this->console->findNamespace($this->namespace) : null);
|
||||
foreach ($this->sortCommands($all) as $namespace => $commands) {
|
||||
$names = [];
|
||||
|
||||
/** @var Command $command */
|
||||
foreach ($commands as $name => $command) {
|
||||
if (!$command->getName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($command->getName() === $name) {
|
||||
$this->commands[$name] = $command;
|
||||
} else {
|
||||
$this->aliases[$name] = $command;
|
||||
}
|
||||
|
||||
$names[] = $name;
|
||||
}
|
||||
|
||||
$this->namespaces[$namespace] = ['id' => $namespace, 'commands' => $names];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $commands
|
||||
* @return array
|
||||
*/
|
||||
private function sortCommands(array $commands)
|
||||
{
|
||||
$namespacedCommands = [];
|
||||
foreach ($commands as $name => $command) {
|
||||
$key = $this->console->extractNamespace($name, 1);
|
||||
if (!$key) {
|
||||
$key = '_global';
|
||||
}
|
||||
|
||||
$namespacedCommands[$key][$name] = $command;
|
||||
}
|
||||
ksort($namespacedCommands);
|
||||
|
||||
foreach ($namespacedCommands as &$commandsSet) {
|
||||
ksort($commandsSet);
|
||||
}
|
||||
// unset reference to keep scope clear
|
||||
unset($commandsSet);
|
||||
|
||||
return $namespacedCommands;
|
||||
}
|
||||
}
|
||||
@@ -1,319 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\helper\descriptor;
|
||||
|
||||
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;
|
||||
use think\console\helper\descriptor\Console as ConsoleDescription;
|
||||
|
||||
class Descriptor
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Output
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function describe(Output $output, $object, array $options = [])
|
||||
{
|
||||
$this->output = $output;
|
||||
|
||||
switch (true) {
|
||||
case $object instanceof InputArgument:
|
||||
$this->describeInputArgument($object, $options);
|
||||
break;
|
||||
case $object instanceof InputOption:
|
||||
$this->describeInputOption($object, $options);
|
||||
break;
|
||||
case $object instanceof InputDefinition:
|
||||
$this->describeInputDefinition($object, $options);
|
||||
break;
|
||||
case $object instanceof Command:
|
||||
$this->describeCommand($object, $options);
|
||||
break;
|
||||
case $object instanceof Console:
|
||||
$this->describeConsole($object, $options);
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出内容
|
||||
* @param string $content
|
||||
* @param bool $decorated
|
||||
*/
|
||||
protected function write($content, $decorated = false)
|
||||
{
|
||||
$this->output->write($content, false, $decorated ? Output::OUTPUT_NORMAL : Output::OUTPUT_RAW);
|
||||
}
|
||||
|
||||
/**
|
||||
* 描述参数
|
||||
* @param InputArgument $argument
|
||||
* @param array $options
|
||||
* @return string|mixed
|
||||
*/
|
||||
protected function describeInputArgument(InputArgument $argument, array $options = [])
|
||||
{
|
||||
if (null !== $argument->getDefault()
|
||||
&& (!is_array($argument->getDefault())
|
||||
|| count($argument->getDefault()))
|
||||
) {
|
||||
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
|
||||
} else {
|
||||
$default = '';
|
||||
}
|
||||
|
||||
$totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName());
|
||||
$spacingWidth = $totalWidth - strlen($argument->getName()) + 2;
|
||||
|
||||
$this->writeText(sprintf(" <info>%s</info>%s%s%s", $argument->getName(), str_repeat(' ', $spacingWidth), // + 17 = 2 spaces + <info> + </info> + 2 spaces
|
||||
preg_replace('/\s*\R\s*/', PHP_EOL . str_repeat(' ', $totalWidth + 17), $argument->getDescription()), $default), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 描述选项
|
||||
* @param InputOption $option
|
||||
* @param array $options
|
||||
* @return string|mixed
|
||||
*/
|
||||
protected function describeInputOption(InputOption $option, array $options = [])
|
||||
{
|
||||
if ($option->acceptValue() && null !== $option->getDefault()
|
||||
&& (!is_array($option->getDefault())
|
||||
|| count($option->getDefault()))
|
||||
) {
|
||||
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
|
||||
} else {
|
||||
$default = '';
|
||||
}
|
||||
|
||||
$value = '';
|
||||
if ($option->acceptValue()) {
|
||||
$value = '=' . strtoupper($option->getName());
|
||||
|
||||
if ($option->isValueOptional()) {
|
||||
$value = '[' . $value . ']';
|
||||
}
|
||||
}
|
||||
|
||||
$totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions([$option]);
|
||||
$synopsis = sprintf('%s%s', $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', sprintf('--%s%s', $option->getName(), $value));
|
||||
|
||||
$spacingWidth = $totalWidth - strlen($synopsis) + 2;
|
||||
|
||||
$this->writeText(sprintf(" <info>%s</info>%s%s%s%s", $synopsis, str_repeat(' ', $spacingWidth), // + 17 = 2 spaces + <info> + </info> + 2 spaces
|
||||
preg_replace('/\s*\R\s*/', "\n" . str_repeat(' ', $totalWidth + 17), $option->getDescription()), $default, $option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 描述输入
|
||||
* @param InputDefinition $definition
|
||||
* @param array $options
|
||||
* @return string|mixed
|
||||
*/
|
||||
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
|
||||
{
|
||||
$totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
|
||||
foreach ($definition->getArguments() as $argument) {
|
||||
$totalWidth = max($totalWidth, strlen($argument->getName()));
|
||||
}
|
||||
|
||||
if ($definition->getArguments()) {
|
||||
$this->writeText('<comment>Arguments:</comment>', $options);
|
||||
$this->writeText("\n");
|
||||
foreach ($definition->getArguments() as $argument) {
|
||||
$this->describeInputArgument($argument, array_merge($options, ['total_width' => $totalWidth]));
|
||||
$this->writeText("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if ($definition->getArguments() && $definition->getOptions()) {
|
||||
$this->writeText("\n");
|
||||
}
|
||||
|
||||
if ($definition->getOptions()) {
|
||||
$laterOptions = [];
|
||||
|
||||
$this->writeText('<comment>Options:</comment>', $options);
|
||||
foreach ($definition->getOptions() as $option) {
|
||||
if (strlen($option->getShortcut()) > 1) {
|
||||
$laterOptions[] = $option;
|
||||
continue;
|
||||
}
|
||||
$this->writeText("\n");
|
||||
$this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
|
||||
}
|
||||
foreach ($laterOptions as $option) {
|
||||
$this->writeText("\n");
|
||||
$this->describeInputOption($option, array_merge($options, ['total_width' => $totalWidth]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 描述指令
|
||||
* @param Command $command
|
||||
* @param array $options
|
||||
* @return string|mixed
|
||||
*/
|
||||
protected function describeCommand(Command $command, array $options = [])
|
||||
{
|
||||
$command->getSynopsis(true);
|
||||
$command->getSynopsis(false);
|
||||
$command->mergeConsoleDefinition(false);
|
||||
|
||||
$this->writeText('<comment>Usage:</comment>', $options);
|
||||
foreach (array_merge([$command->getSynopsis(true)], $command->getAliases(), $command->getUsages()) as $usage) {
|
||||
$this->writeText("\n");
|
||||
$this->writeText(' ' . $usage, $options);
|
||||
}
|
||||
$this->writeText("\n");
|
||||
|
||||
$definition = $command->getNativeDefinition();
|
||||
if ($definition->getOptions() || $definition->getArguments()) {
|
||||
$this->writeText("\n");
|
||||
$this->describeInputDefinition($definition, $options);
|
||||
$this->writeText("\n");
|
||||
}
|
||||
|
||||
if ($help = $command->getProcessedHelp()) {
|
||||
$this->writeText("\n");
|
||||
$this->writeText('<comment>Help:</comment>', $options);
|
||||
$this->writeText("\n");
|
||||
$this->writeText(' ' . str_replace("\n", "\n ", $help), $options);
|
||||
$this->writeText("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 描述控制台
|
||||
* @param Console $console
|
||||
* @param array $options
|
||||
* @return string|mixed
|
||||
*/
|
||||
protected function describeConsole(Console $console, array $options = [])
|
||||
{
|
||||
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
|
||||
$description = new ConsoleDescription($console, $describedNamespace);
|
||||
|
||||
if (isset($options['raw_text']) && $options['raw_text']) {
|
||||
$width = $this->getColumnWidth($description->getCommands());
|
||||
|
||||
foreach ($description->getCommands() as $command) {
|
||||
$this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options);
|
||||
$this->writeText("\n");
|
||||
}
|
||||
} else {
|
||||
if ('' != $help = $console->getHelp()) {
|
||||
$this->writeText("$help\n\n", $options);
|
||||
}
|
||||
|
||||
$this->writeText("<comment>Usage:</comment>\n", $options);
|
||||
$this->writeText(" command [options] [arguments]\n\n", $options);
|
||||
|
||||
$this->describeInputDefinition(new InputDefinition($console->getDefinition()->getOptions()), $options);
|
||||
|
||||
$this->writeText("\n");
|
||||
$this->writeText("\n");
|
||||
|
||||
$width = $this->getColumnWidth($description->getCommands());
|
||||
|
||||
if ($describedNamespace) {
|
||||
$this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
|
||||
} else {
|
||||
$this->writeText('<comment>Available commands:</comment>', $options);
|
||||
}
|
||||
|
||||
// add commands by namespace
|
||||
foreach ($description->getNamespaces() as $namespace) {
|
||||
if (!$describedNamespace && ConsoleDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
|
||||
$this->writeText("\n");
|
||||
$this->writeText(' <comment>' . $namespace['id'] . '</comment>', $options);
|
||||
}
|
||||
|
||||
foreach ($namespace['commands'] as $name) {
|
||||
$this->writeText("\n");
|
||||
$spacingWidth = $width - strlen($name);
|
||||
$this->writeText(sprintf(" <info>%s</info>%s%s", $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)
|
||||
->getDescription()), $options);
|
||||
}
|
||||
}
|
||||
|
||||
$this->writeText("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
private function writeText($content, array $options = [])
|
||||
{
|
||||
$this->write(isset($options['raw_text'])
|
||||
&& $options['raw_text'] ? strip_tags($content) : $content, isset($options['raw_output']) ? !$options['raw_output'] : true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化
|
||||
* @param mixed $default
|
||||
* @return string
|
||||
*/
|
||||
private function formatDefaultValue($default)
|
||||
{
|
||||
return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Command[] $commands
|
||||
* @return int
|
||||
*/
|
||||
private function getColumnWidth(array $commands)
|
||||
{
|
||||
$width = 0;
|
||||
foreach ($commands as $command) {
|
||||
$width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width;
|
||||
}
|
||||
|
||||
return $width + 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param InputOption[] $options
|
||||
* @return int
|
||||
*/
|
||||
private function calculateTotalWidthForOptions($options)
|
||||
{
|
||||
$totalWidth = 0;
|
||||
foreach ($options as $option) {
|
||||
$nameLength = 4 + strlen($option->getName()) + 2; // - + shortcut + , + whitespace + name + --
|
||||
|
||||
if ($option->acceptValue()) {
|
||||
$valueLength = 1 + strlen($option->getName()); // = + value
|
||||
$valueLength += $option->isValueOptional() ? 2 : 0; // [ + ]
|
||||
|
||||
$nameLength += $valueLength;
|
||||
}
|
||||
$totalWidth = max($totalWidth, $nameLength);
|
||||
}
|
||||
|
||||
return $totalWidth;
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\helper\question;
|
||||
|
||||
|
||||
class Choice extends Question
|
||||
{
|
||||
|
||||
private $choices;
|
||||
private $multiselect = false;
|
||||
private $prompt = ' > ';
|
||||
private $errorMessage = 'Value "%s" is invalid';
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string $question 问题
|
||||
* @param array $choices 选项
|
||||
* @param mixed $default 默认答案
|
||||
*/
|
||||
public function __construct($question, array $choices, $default = null)
|
||||
{
|
||||
parent::__construct($question, $default);
|
||||
|
||||
$this->choices = $choices;
|
||||
$this->setValidator($this->getDefaultValidator());
|
||||
$this->setAutocompleterValues($choices);
|
||||
}
|
||||
|
||||
/**
|
||||
* 可选项
|
||||
* @return array
|
||||
*/
|
||||
public function getChoices()
|
||||
{
|
||||
return $this->choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置可否多选
|
||||
* @param bool $multiselect
|
||||
* @return self
|
||||
*/
|
||||
public function setMultiselect($multiselect)
|
||||
{
|
||||
$this->multiselect = $multiselect;
|
||||
$this->setValidator($this->getDefaultValidator());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提示
|
||||
* @return string
|
||||
*/
|
||||
public function getPrompt()
|
||||
{
|
||||
return $this->prompt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置提示
|
||||
* @param string $prompt
|
||||
* @return self
|
||||
*/
|
||||
public function setPrompt($prompt)
|
||||
{
|
||||
$this->prompt = $prompt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置错误提示信息
|
||||
* @param string $errorMessage
|
||||
* @return self
|
||||
*/
|
||||
public function setErrorMessage($errorMessage)
|
||||
{
|
||||
$this->errorMessage = $errorMessage;
|
||||
$this->setValidator($this->getDefaultValidator());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认的验证方法
|
||||
* @return callable
|
||||
*/
|
||||
private function getDefaultValidator()
|
||||
{
|
||||
$choices = $this->choices;
|
||||
$errorMessage = $this->errorMessage;
|
||||
$multiselect = $this->multiselect;
|
||||
$isAssoc = $this->isAssoc($choices);
|
||||
|
||||
return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) {
|
||||
// Collapse all spaces.
|
||||
$selectedChoices = str_replace(' ', '', $selected);
|
||||
|
||||
if ($multiselect) {
|
||||
// Check for a separated comma values
|
||||
if (!preg_match('/^[a-zA-Z0-9_-]+(?:,[a-zA-Z0-9_-]+)*$/', $selectedChoices, $matches)) {
|
||||
throw new \InvalidArgumentException(sprintf($errorMessage, $selected));
|
||||
}
|
||||
$selectedChoices = explode(',', $selectedChoices);
|
||||
} else {
|
||||
$selectedChoices = [$selected];
|
||||
}
|
||||
|
||||
$multiselectChoices = [];
|
||||
foreach ($selectedChoices as $value) {
|
||||
$results = [];
|
||||
foreach ($choices as $key => $choice) {
|
||||
if ($choice === $value) {
|
||||
$results[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($results) > 1) {
|
||||
throw new \InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', implode(' or ', $results)));
|
||||
}
|
||||
|
||||
$result = array_search($value, $choices);
|
||||
|
||||
if (!$isAssoc) {
|
||||
if (!empty($result)) {
|
||||
$result = $choices[$result];
|
||||
} elseif (isset($choices[$value])) {
|
||||
$result = $choices[$value];
|
||||
}
|
||||
} elseif (empty($result) && array_key_exists($value, $choices)) {
|
||||
$result = $value;
|
||||
}
|
||||
|
||||
if (empty($result)) {
|
||||
throw new \InvalidArgumentException(sprintf($errorMessage, $value));
|
||||
}
|
||||
array_push($multiselectChoices, $result);
|
||||
}
|
||||
|
||||
if ($multiselect) {
|
||||
return $multiselectChoices;
|
||||
}
|
||||
|
||||
return current($multiselectChoices);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\helper\question;
|
||||
|
||||
|
||||
class Confirmation extends Question
|
||||
{
|
||||
|
||||
private $trueAnswerRegex;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string $question 问题
|
||||
* @param bool $default 默认答案
|
||||
* @param string $trueAnswerRegex 验证正则
|
||||
*/
|
||||
public function __construct($question, $default = true, $trueAnswerRegex = '/^y/i')
|
||||
{
|
||||
parent::__construct($question, (bool)$default);
|
||||
|
||||
$this->trueAnswerRegex = $trueAnswerRegex;
|
||||
$this->setNormalizer($this->getDefaultNormalizer());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认的答案回调
|
||||
* @return callable
|
||||
*/
|
||||
private function getDefaultNormalizer()
|
||||
{
|
||||
$default = $this->getDefault();
|
||||
$regex = $this->trueAnswerRegex;
|
||||
|
||||
return function ($answer) use ($default, $regex) {
|
||||
if (is_bool($answer)) {
|
||||
return $answer;
|
||||
}
|
||||
|
||||
$answerIsTrue = (bool)preg_match($regex, $answer);
|
||||
if (false === $default) {
|
||||
return $answer && $answerIsTrue;
|
||||
}
|
||||
|
||||
return !$answer || $answerIsTrue;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\helper\question;
|
||||
|
||||
class Question
|
||||
{
|
||||
|
||||
private $question;
|
||||
private $attempts;
|
||||
private $hidden = false;
|
||||
private $hiddenFallback = true;
|
||||
private $autocompleterValues;
|
||||
private $validator;
|
||||
private $default;
|
||||
private $normalizer;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string $question 问题
|
||||
* @param mixed $default 默认答案
|
||||
*/
|
||||
public function __construct($question, $default = null)
|
||||
{
|
||||
$this->question = $question;
|
||||
$this->default = $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取问题
|
||||
* @return string
|
||||
*/
|
||||
public function getQuestion()
|
||||
{
|
||||
return $this->question;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认答案
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDefault()
|
||||
{
|
||||
return $this->default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否隐藏答案
|
||||
* @return bool
|
||||
*/
|
||||
public function isHidden()
|
||||
{
|
||||
return $this->hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏答案
|
||||
* @param bool $hidden
|
||||
* @return Question
|
||||
*/
|
||||
public function setHidden($hidden)
|
||||
{
|
||||
if ($this->autocompleterValues) {
|
||||
throw new \LogicException('A hidden question cannot use the autocompleter.');
|
||||
}
|
||||
|
||||
$this->hidden = (bool)$hidden;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 不能被隐藏是否撤销
|
||||
* @return bool
|
||||
*/
|
||||
public function isHiddenFallback()
|
||||
{
|
||||
return $this->hiddenFallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置不能被隐藏的时候的操作
|
||||
* @param bool $fallback
|
||||
* @return Question
|
||||
*/
|
||||
public function setHiddenFallback($fallback)
|
||||
{
|
||||
$this->hiddenFallback = (bool)$fallback;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自动完成
|
||||
* @return null|array|\Traversable
|
||||
*/
|
||||
public function getAutocompleterValues()
|
||||
{
|
||||
return $this->autocompleterValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置自动完成的值
|
||||
* @param null|array|\Traversable $values
|
||||
* @return Question
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function setAutocompleterValues($values)
|
||||
{
|
||||
if (is_array($values) && $this->isAssoc($values)) {
|
||||
$values = array_merge(array_keys($values), array_values($values));
|
||||
}
|
||||
|
||||
if (null !== $values && !is_array($values)) {
|
||||
if (!$values instanceof \Traversable || $values instanceof \Countable) {
|
||||
throw new \InvalidArgumentException('Autocompleter values can be either an array, `null` or an object implementing both `Countable` and `Traversable` interfaces.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->hidden) {
|
||||
throw new \LogicException('A hidden question cannot use the autocompleter.');
|
||||
}
|
||||
|
||||
$this->autocompleterValues = $values;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置答案的验证器
|
||||
* @param null|callable $validator
|
||||
* @return Question The current instance
|
||||
*/
|
||||
public function setValidator($validator)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证器
|
||||
* @return null|callable
|
||||
*/
|
||||
public function getValidator()
|
||||
{
|
||||
return $this->validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大重试次数
|
||||
* @param null|int $attempts
|
||||
* @return Question
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setMaxAttempts($attempts)
|
||||
{
|
||||
if (null !== $attempts && $attempts < 1) {
|
||||
throw new \InvalidArgumentException('Maximum number of attempts must be a positive value.');
|
||||
}
|
||||
|
||||
$this->attempts = $attempts;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最大重试次数
|
||||
* @return null|int
|
||||
*/
|
||||
public function getMaxAttempts()
|
||||
{
|
||||
return $this->attempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置响应的回调
|
||||
* @param string|\Closure $normalizer
|
||||
* @return Question
|
||||
*/
|
||||
public function setNormalizer($normalizer)
|
||||
{
|
||||
$this->normalizer = $normalizer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相应回调
|
||||
* The normalizer can ba a callable (a string), a closure or a class implementing __invoke.
|
||||
* @return string|\Closure
|
||||
*/
|
||||
public function getNormalizer()
|
||||
{
|
||||
return $this->normalizer;
|
||||
}
|
||||
|
||||
protected function isAssoc($array)
|
||||
{
|
||||
return (bool)count(array_filter(array_keys($array), 'is_string'));
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\input;
|
||||
|
||||
class Argument
|
||||
{
|
||||
|
||||
const REQUIRED = 1;
|
||||
const OPTIONAL = 2;
|
||||
const IS_ARRAY = 4;
|
||||
|
||||
private $name;
|
||||
private $mode;
|
||||
private $default;
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string $name 参数名
|
||||
* @param int $mode 参数类型: self::REQUIRED 或者 self::OPTIONAL
|
||||
* @param string $description 描述
|
||||
* @param mixed $default 默认值 (仅 self::OPTIONAL 类型有效)
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct($name, $mode = null, $description = '', $default = null)
|
||||
{
|
||||
if (null === $mode) {
|
||||
$mode = self::OPTIONAL;
|
||||
} elseif (!is_int($mode) || $mode > 7 || $mode < 1) {
|
||||
throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->mode = $mode;
|
||||
$this->description = $description;
|
||||
|
||||
$this->setDefault($default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数名
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否必须
|
||||
* @return bool
|
||||
*/
|
||||
public function isRequired()
|
||||
{
|
||||
return self::REQUIRED === (self::REQUIRED & $this->mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 该参数是否接受数组
|
||||
* @return bool
|
||||
*/
|
||||
public function isArray()
|
||||
{
|
||||
return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认值
|
||||
* @param mixed $default 默认值
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function setDefault($default = null)
|
||||
{
|
||||
if (self::REQUIRED === $this->mode && null !== $default) {
|
||||
throw new \LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
|
||||
}
|
||||
|
||||
if ($this->isArray()) {
|
||||
if (null === $default) {
|
||||
$default = [];
|
||||
} elseif (!is_array($default)) {
|
||||
throw new \LogicException('A default value for an array argument must be an array.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->default = $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDefault()
|
||||
{
|
||||
return $this->default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取描述
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
}
|
||||
@@ -1,375 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\input;
|
||||
|
||||
class Definition
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Argument[]
|
||||
*/
|
||||
private $arguments;
|
||||
|
||||
private $requiredCount;
|
||||
private $hasAnArrayArgument = false;
|
||||
private $hasOptional;
|
||||
|
||||
/**
|
||||
* @var Option[]
|
||||
*/
|
||||
private $options;
|
||||
private $shortcuts;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param array $definition
|
||||
* @api
|
||||
*/
|
||||
public function __construct(array $definition = [])
|
||||
{
|
||||
$this->setDefinition($definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置指令的定义
|
||||
* @param array $definition 定义的数组
|
||||
*/
|
||||
public function setDefinition(array $definition)
|
||||
{
|
||||
$arguments = [];
|
||||
$options = [];
|
||||
foreach ($definition as $item) {
|
||||
if ($item instanceof Option) {
|
||||
$options[] = $item;
|
||||
} else {
|
||||
$arguments[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$this->setArguments($arguments);
|
||||
$this->setOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置参数
|
||||
* @param Argument[] $arguments 参数数组
|
||||
*/
|
||||
public function setArguments($arguments = [])
|
||||
{
|
||||
$this->arguments = [];
|
||||
$this->requiredCount = 0;
|
||||
$this->hasOptional = false;
|
||||
$this->hasAnArrayArgument = false;
|
||||
$this->addArguments($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加参数
|
||||
* @param Argument[] $arguments 参数数组
|
||||
* @api
|
||||
*/
|
||||
public function addArguments($arguments = [])
|
||||
{
|
||||
if (null !== $arguments) {
|
||||
foreach ($arguments as $argument) {
|
||||
$this->addArgument($argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个参数
|
||||
* @param Argument $argument 参数
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function addArgument(Argument $argument)
|
||||
{
|
||||
if (isset($this->arguments[$argument->getName()])) {
|
||||
throw new \LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
|
||||
}
|
||||
|
||||
if ($this->hasAnArrayArgument) {
|
||||
throw new \LogicException('Cannot add an argument after an array argument.');
|
||||
}
|
||||
|
||||
if ($argument->isRequired() && $this->hasOptional) {
|
||||
throw new \LogicException('Cannot add a required argument after an optional one.');
|
||||
}
|
||||
|
||||
if ($argument->isArray()) {
|
||||
$this->hasAnArrayArgument = true;
|
||||
}
|
||||
|
||||
if ($argument->isRequired()) {
|
||||
++$this->requiredCount;
|
||||
} else {
|
||||
$this->hasOptional = true;
|
||||
}
|
||||
|
||||
$this->arguments[$argument->getName()] = $argument;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称或者位置获取参数
|
||||
* @param string|int $name 参数名或者位置
|
||||
* @return Argument 参数
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getArgument($name)
|
||||
{
|
||||
if (!$this->hasArgument($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
|
||||
}
|
||||
|
||||
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
|
||||
|
||||
return $arguments[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称或位置检查是否具有某个参数
|
||||
* @param string|int $name 参数名或者位置
|
||||
* @return bool
|
||||
* @api
|
||||
*/
|
||||
public function hasArgument($name)
|
||||
{
|
||||
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
|
||||
|
||||
return isset($arguments[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的参数
|
||||
* @return Argument[] 参数数组
|
||||
*/
|
||||
public function getArguments()
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数数量
|
||||
* @return int
|
||||
*/
|
||||
public function getArgumentCount()
|
||||
{
|
||||
return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取必填的参数的数量
|
||||
* @return int
|
||||
*/
|
||||
public function getArgumentRequiredCount()
|
||||
{
|
||||
return $this->requiredCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数默认值
|
||||
* @return array
|
||||
*/
|
||||
public function getArgumentDefaults()
|
||||
{
|
||||
$values = [];
|
||||
foreach ($this->arguments as $argument) {
|
||||
$values[$argument->getName()] = $argument->getDefault();
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选项
|
||||
* @param Option[] $options 选项数组
|
||||
*/
|
||||
public function setOptions($options = [])
|
||||
{
|
||||
$this->options = [];
|
||||
$this->shortcuts = [];
|
||||
$this->addOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加选项
|
||||
* @param Option[] $options 选项数组
|
||||
* @api
|
||||
*/
|
||||
public function addOptions($options = [])
|
||||
{
|
||||
foreach ($options as $option) {
|
||||
$this->addOption($option);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个选项
|
||||
* @param Option $option 选项
|
||||
* @throws \LogicException
|
||||
* @api
|
||||
*/
|
||||
public function addOption(Option $option)
|
||||
{
|
||||
if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
|
||||
throw new \LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
|
||||
}
|
||||
|
||||
if ($option->getShortcut()) {
|
||||
foreach (explode('|', $option->getShortcut()) as $shortcut) {
|
||||
if (isset($this->shortcuts[$shortcut])
|
||||
&& !$option->equals($this->options[$this->shortcuts[$shortcut]])
|
||||
) {
|
||||
throw new \LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->options[$option->getName()] = $option;
|
||||
if ($option->getShortcut()) {
|
||||
foreach (explode('|', $option->getShortcut()) as $shortcut) {
|
||||
$this->shortcuts[$shortcut] = $option->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称获取选项
|
||||
* @param string $name 选项名
|
||||
* @return Option
|
||||
* @throws \InvalidArgumentException
|
||||
* @api
|
||||
*/
|
||||
public function getOption($name)
|
||||
{
|
||||
if (!$this->hasOption($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
|
||||
}
|
||||
|
||||
return $this->options[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称检查是否有这个选项
|
||||
* @param string $name 选项名
|
||||
* @return bool
|
||||
* @api
|
||||
*/
|
||||
public function hasOption($name)
|
||||
{
|
||||
return isset($this->options[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有选项
|
||||
* @return Option[]
|
||||
* @api
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称检查某个选项是否有短名称
|
||||
* @param string $name 短名称
|
||||
* @return bool
|
||||
*/
|
||||
public function hasShortcut($name)
|
||||
{
|
||||
return isset($this->shortcuts[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据短名称获取选项
|
||||
* @param string $shortcut 短名称
|
||||
* @return Option
|
||||
*/
|
||||
public function getOptionForShortcut($shortcut)
|
||||
{
|
||||
return $this->getOption($this->shortcutToName($shortcut));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有选项的默认值
|
||||
* @return array
|
||||
*/
|
||||
public function getOptionDefaults()
|
||||
{
|
||||
$values = [];
|
||||
foreach ($this->options as $option) {
|
||||
$values[$option->getName()] = $option->getDefault();
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据短名称获取选项名
|
||||
* @param string $shortcut 短名称
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
private function shortcutToName($shortcut)
|
||||
{
|
||||
if (!isset($this->shortcuts[$shortcut])) {
|
||||
throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
|
||||
}
|
||||
|
||||
return $this->shortcuts[$shortcut];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该指令的介绍
|
||||
* @param bool $short 是否简洁介绍
|
||||
* @return string
|
||||
*/
|
||||
public function getSynopsis($short = false)
|
||||
{
|
||||
$elements = [];
|
||||
|
||||
if ($short && $this->getOptions()) {
|
||||
$elements[] = '[options]';
|
||||
} elseif (!$short) {
|
||||
foreach ($this->getOptions() as $option) {
|
||||
$value = '';
|
||||
if ($option->acceptValue()) {
|
||||
$value = sprintf(' %s%s%s', $option->isValueOptional() ? '[' : '', strtoupper($option->getName()), $option->isValueOptional() ? ']' : '');
|
||||
}
|
||||
|
||||
$shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
|
||||
$elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($elements) && $this->getArguments()) {
|
||||
$elements[] = '[--]';
|
||||
}
|
||||
|
||||
foreach ($this->getArguments() as $argument) {
|
||||
$element = '<' . $argument->getName() . '>';
|
||||
if (!$argument->isRequired()) {
|
||||
$element = '[' . $element . ']';
|
||||
} elseif ($argument->isArray()) {
|
||||
$element .= ' (' . $element . ')';
|
||||
}
|
||||
|
||||
if ($argument->isArray()) {
|
||||
$element .= '...';
|
||||
}
|
||||
|
||||
$elements[] = $element;
|
||||
}
|
||||
|
||||
return implode(' ', $elements);
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\input;
|
||||
|
||||
class Option
|
||||
{
|
||||
|
||||
const VALUE_NONE = 1;
|
||||
const VALUE_REQUIRED = 2;
|
||||
const VALUE_OPTIONAL = 4;
|
||||
const VALUE_IS_ARRAY = 8;
|
||||
|
||||
private $name;
|
||||
private $shortcut;
|
||||
private $mode;
|
||||
private $default;
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param string $name 选项名
|
||||
* @param string|array $shortcut 短名称,多个用|隔开或者使用数组
|
||||
* @param int $mode 选项类型(可选类型为 self::VALUE_*)
|
||||
* @param string $description 描述
|
||||
* @param mixed $default 默认值 (类型为 self::VALUE_REQUIRED 或者 self::VALUE_NONE 的时候必须为null)
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
|
||||
{
|
||||
if (0 === strpos($name, '--')) {
|
||||
$name = substr($name, 2);
|
||||
}
|
||||
|
||||
if (empty($name)) {
|
||||
throw new \InvalidArgumentException('An option name cannot be empty.');
|
||||
}
|
||||
|
||||
if (empty($shortcut)) {
|
||||
$shortcut = null;
|
||||
}
|
||||
|
||||
if (null !== $shortcut) {
|
||||
if (is_array($shortcut)) {
|
||||
$shortcut = implode('|', $shortcut);
|
||||
}
|
||||
$shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
|
||||
$shortcuts = array_filter($shortcuts);
|
||||
$shortcut = implode('|', $shortcuts);
|
||||
|
||||
if (empty($shortcut)) {
|
||||
throw new \InvalidArgumentException('An option shortcut cannot be empty.');
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $mode) {
|
||||
$mode = self::VALUE_NONE;
|
||||
} elseif (!is_int($mode) || $mode > 15 || $mode < 1) {
|
||||
throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
$this->shortcut = $shortcut;
|
||||
$this->mode = $mode;
|
||||
$this->description = $description;
|
||||
|
||||
if ($this->isArray() && !$this->acceptValue()) {
|
||||
throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
|
||||
}
|
||||
|
||||
$this->setDefault($default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取短名称
|
||||
* @return string
|
||||
*/
|
||||
public function getShortcut()
|
||||
{
|
||||
return $this->shortcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取选项名
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可以设置值
|
||||
* @return bool 类型不是 self::VALUE_NONE 的时候返回true,其他均返回false
|
||||
*/
|
||||
public function acceptValue()
|
||||
{
|
||||
return $this->isValueRequired() || $this->isValueOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否必须
|
||||
* @return bool 类型是 self::VALUE_REQUIRED 的时候返回true,其他均返回false
|
||||
*/
|
||||
public function isValueRequired()
|
||||
{
|
||||
return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否可选
|
||||
* @return bool 类型是 self::VALUE_OPTIONAL 的时候返回true,其他均返回false
|
||||
*/
|
||||
public function isValueOptional()
|
||||
{
|
||||
return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 选项值是否接受数组
|
||||
* @return bool 类型是 self::VALUE_IS_ARRAY 的时候返回true,其他均返回false
|
||||
*/
|
||||
public function isArray()
|
||||
{
|
||||
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认值
|
||||
* @param mixed $default 默认值
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function setDefault($default = null)
|
||||
{
|
||||
if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) {
|
||||
throw new \LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.');
|
||||
}
|
||||
|
||||
if ($this->isArray()) {
|
||||
if (null === $default) {
|
||||
$default = [];
|
||||
} elseif (!is_array($default)) {
|
||||
throw new \LogicException('A default value for an array option must be an array.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->default = $this->acceptValue() ? $default : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDefault()
|
||||
{
|
||||
return $this->default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取描述文字
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查所给选项是否是当前这个
|
||||
* @param Option $option
|
||||
* @return bool
|
||||
*/
|
||||
public function equals(Option $option)
|
||||
{
|
||||
return $option->getName() === $this->getName()
|
||||
&& $option->getShortcut() === $this->getShortcut()
|
||||
&& $option->getDefault() === $this->getDefault()
|
||||
&& $option->isArray() === $this->isArray()
|
||||
&& $option->isValueRequired() === $this->isValueRequired()
|
||||
&& $option->isValueOptional() === $this->isValueOptional();
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\console\output;
|
||||
|
||||
use think\console\output\formatter\Style;
|
||||
use think\console\output\formatter\Stack as StyleStack;
|
||||
|
||||
class Formatter
|
||||
{
|
||||
|
||||
private $decorated = false;
|
||||
private $styles = [];
|
||||
private $styleStack;
|
||||
|
||||
/**
|
||||
* 转义
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
public static function escape($text)
|
||||
{
|
||||
return preg_replace('/([^\\\\]?)</is', '$1\\<', $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化命令行输出格式
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->setStyle('error', new Style('white', 'red'));
|
||||
$this->setStyle('info', new Style('green'));
|
||||
$this->setStyle('comment', new Style('yellow'));
|
||||
$this->setStyle('question', new Style('black', 'cyan'));
|
||||
|
||||
$this->styleStack = new StyleStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置外观标识
|
||||
* @param bool $decorated 是否美化文职
|
||||
*/
|
||||
public function setDecorated($decorated)
|
||||
{
|
||||
$this->decorated = (bool)$decorated;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取外观标识
|
||||
* @return bool
|
||||
*/
|
||||
public function isDecorated()
|
||||
{
|
||||
return $this->decorated;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个新样式
|
||||
* @param string $name 样式名
|
||||
* @param Style $style 样式实例
|
||||
*/
|
||||
public function setStyle($name, Style $style)
|
||||
{
|
||||
$this->styles[strtolower($name)] = $style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有这个样式
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function hasStyle($name)
|
||||
{
|
||||
return isset($this->styles[strtolower($name)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取样式
|
||||
* @param string $name
|
||||
* @return Style
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getStyle($name)
|
||||
{
|
||||
if (!$this->hasStyle($name)) {
|
||||
throw new \InvalidArgumentException(sprintf('Undefined style: %s', $name));
|
||||
}
|
||||
|
||||
return $this->styles[strtolower($name)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用所给的样式格式化文字
|
||||
* @param string $message 文字
|
||||
* @return string
|
||||
*/
|
||||
public function format($message)
|
||||
{
|
||||
$offset = 0;
|
||||
$output = '';
|
||||
$tagRegex = '[a-z][a-z0-9_=;-]*';
|
||||
preg_match_all("#<(($tagRegex) | /($tagRegex)?)>#isx", $message, $matches, PREG_OFFSET_CAPTURE);
|
||||
foreach ($matches[0] as $i => $match) {
|
||||
$pos = $match[1];
|
||||
$text = $match[0];
|
||||
|
||||
if (0 != $pos && '\\' == $message[$pos - 1]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset));
|
||||
$offset = $pos + strlen($text);
|
||||
|
||||
if ($open = '/' != $text[1]) {
|
||||
$tag = $matches[1][$i][0];
|
||||
} else {
|
||||
$tag = isset($matches[3][$i][0]) ? $matches[3][$i][0] : '';
|
||||
}
|
||||
|
||||
if (!$open && !$tag) {
|
||||
// </>
|
||||
$this->styleStack->pop();
|
||||
} elseif (false === $style = $this->createStyleFromString(strtolower($tag))) {
|
||||
$output .= $this->applyCurrentStyle($text);
|
||||
} elseif ($open) {
|
||||
$this->styleStack->push($style);
|
||||
} else {
|
||||
$this->styleStack->pop($style);
|
||||
}
|
||||
}
|
||||
|
||||
$output .= $this->applyCurrentStyle(substr($message, $offset));
|
||||
|
||||
return str_replace('\\<', '<', $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return StyleStack
|
||||
*/
|
||||
public function getStyleStack()
|
||||
{
|
||||
return $this->styleStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字符串创建新的样式实例
|
||||
* @param string $string
|
||||
* @return Style|bool
|
||||
*/
|
||||
private function createStyleFromString($string)
|
||||
{
|
||||
if (isset($this->styles[$string])) {
|
||||
return $this->styles[$string];
|
||||
}
|
||||
|
||||
if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$style = new Style();
|
||||
foreach ($matches as $match) {
|
||||
array_shift($match);
|
||||
|
||||
if ('fg' == $match[0]) {
|
||||
$style->setForeground($match[1]);
|
||||
} elseif ('bg' == $match[0]) {
|
||||
$style->setBackground($match[1]);
|
||||
} else {
|
||||
try {
|
||||
$style->setOption($match[1]);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从堆栈应用样式到文字
|
||||
* @param string $text 文字
|
||||
* @return string
|
||||
*/
|
||||
private function applyCurrentStyle($text)
|
||||
{
|
||||
return $this->isDecorated() && strlen($text) > 0 ? $this->styleStack->getCurrent()->apply($text) : $text;
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*/
|
||||
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 false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI');
|
||||
}
|
||||
|
||||
return function_exists('posix_isatty') && @posix_isatty($this->stream);
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\output\formatter;
|
||||
|
||||
class Stack
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Style[]
|
||||
*/
|
||||
private $styles;
|
||||
|
||||
/**
|
||||
* @var Style
|
||||
*/
|
||||
private $emptyStyle;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param Style|null $emptyStyle
|
||||
*/
|
||||
public function __construct(Style $emptyStyle = null)
|
||||
{
|
||||
$this->emptyStyle = $emptyStyle ?: new Style();
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置堆栈
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->styles = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 推一个样式进入堆栈
|
||||
* @param Style $style
|
||||
*/
|
||||
public function push(Style $style)
|
||||
{
|
||||
$this->styles[] = $style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从堆栈中弹出一个样式
|
||||
* @param Style|null $style
|
||||
* @return Style
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function pop(Style $style = null)
|
||||
{
|
||||
if (empty($this->styles)) {
|
||||
return $this->emptyStyle;
|
||||
}
|
||||
|
||||
if (null === $style) {
|
||||
return array_pop($this->styles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var int $index
|
||||
* @var Style $stackedStyle
|
||||
*/
|
||||
foreach (array_reverse($this->styles, true) as $index => $stackedStyle) {
|
||||
if ($style->apply('') === $stackedStyle->apply('')) {
|
||||
$this->styles = array_slice($this->styles, 0, $index);
|
||||
|
||||
return $stackedStyle;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Incorrectly nested style tag found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算堆栈的当前样式。
|
||||
* @return Style
|
||||
*/
|
||||
public function getCurrent()
|
||||
{
|
||||
if (empty($this->styles)) {
|
||||
return $this->emptyStyle;
|
||||
}
|
||||
|
||||
return $this->styles[count($this->styles) - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Style $emptyStyle
|
||||
* @return Stack
|
||||
*/
|
||||
public function setEmptyStyle(Style $emptyStyle)
|
||||
{
|
||||
$this->emptyStyle = $emptyStyle;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Style
|
||||
*/
|
||||
public function getEmptyStyle()
|
||||
{
|
||||
return $this->emptyStyle;
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\console\output\formatter;
|
||||
|
||||
class Style
|
||||
{
|
||||
|
||||
private static $availableForegroundColors = [
|
||||
'black' => ['set' => 30, 'unset' => 39],
|
||||
'red' => ['set' => 31, 'unset' => 39],
|
||||
'green' => ['set' => 32, 'unset' => 39],
|
||||
'yellow' => ['set' => 33, 'unset' => 39],
|
||||
'blue' => ['set' => 34, 'unset' => 39],
|
||||
'magenta' => ['set' => 35, 'unset' => 39],
|
||||
'cyan' => ['set' => 36, 'unset' => 39],
|
||||
'white' => ['set' => 37, 'unset' => 39],
|
||||
];
|
||||
private static $availableBackgroundColors = [
|
||||
'black' => ['set' => 40, 'unset' => 49],
|
||||
'red' => ['set' => 41, 'unset' => 49],
|
||||
'green' => ['set' => 42, 'unset' => 49],
|
||||
'yellow' => ['set' => 43, 'unset' => 49],
|
||||
'blue' => ['set' => 44, 'unset' => 49],
|
||||
'magenta' => ['set' => 45, 'unset' => 49],
|
||||
'cyan' => ['set' => 46, 'unset' => 49],
|
||||
'white' => ['set' => 47, 'unset' => 49],
|
||||
];
|
||||
private static $availableOptions = [
|
||||
'bold' => ['set' => 1, 'unset' => 22],
|
||||
'underscore' => ['set' => 4, 'unset' => 24],
|
||||
'blink' => ['set' => 5, 'unset' => 25],
|
||||
'reverse' => ['set' => 7, 'unset' => 27],
|
||||
'conceal' => ['set' => 8, 'unset' => 28],
|
||||
];
|
||||
|
||||
private $foreground;
|
||||
private $background;
|
||||
private $options = [];
|
||||
|
||||
/**
|
||||
* 初始化输出的样式
|
||||
* @param string|null $foreground 字体颜色
|
||||
* @param string|null $background 背景色
|
||||
* @param array $options 格式
|
||||
* @api
|
||||
*/
|
||||
public function __construct($foreground = null, $background = null, array $options = [])
|
||||
{
|
||||
if (null !== $foreground) {
|
||||
$this->setForeground($foreground);
|
||||
}
|
||||
if (null !== $background) {
|
||||
$this->setBackground($background);
|
||||
}
|
||||
if (count($options)) {
|
||||
$this->setOptions($options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字体颜色
|
||||
* @param string|null $color 颜色名
|
||||
* @throws \InvalidArgumentException
|
||||
* @api
|
||||
*/
|
||||
public function setForeground($color = null)
|
||||
{
|
||||
if (null === $color) {
|
||||
$this->foreground = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset(static::$availableForegroundColors[$color])) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid foreground color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableForegroundColors))));
|
||||
}
|
||||
|
||||
$this->foreground = static::$availableForegroundColors[$color];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置背景色
|
||||
* @param string|null $color 颜色名
|
||||
* @throws \InvalidArgumentException
|
||||
* @api
|
||||
*/
|
||||
public function setBackground($color = null)
|
||||
{
|
||||
if (null === $color) {
|
||||
$this->background = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset(static::$availableBackgroundColors[$color])) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid background color specified: "%s". Expected one of (%s)', $color, implode(', ', array_keys(static::$availableBackgroundColors))));
|
||||
}
|
||||
|
||||
$this->background = static::$availableBackgroundColors[$color];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字体格式
|
||||
* @param string $option 格式名
|
||||
* @throws \InvalidArgumentException When the option name isn't defined
|
||||
* @api
|
||||
*/
|
||||
public function setOption($option)
|
||||
{
|
||||
if (!isset(static::$availableOptions[$option])) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions))));
|
||||
}
|
||||
|
||||
if (!in_array(static::$availableOptions[$option], $this->options)) {
|
||||
$this->options[] = static::$availableOptions[$option];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置字体格式
|
||||
* @param string $option 格式名
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function unsetOption($option)
|
||||
{
|
||||
if (!isset(static::$availableOptions[$option])) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s)', $option, implode(', ', array_keys(static::$availableOptions))));
|
||||
}
|
||||
|
||||
$pos = array_search(static::$availableOptions[$option], $this->options);
|
||||
if (false !== $pos) {
|
||||
unset($this->options[$pos]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置字体格式
|
||||
* @param array $options
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
{
|
||||
$this->options = [];
|
||||
|
||||
foreach ($options as $option) {
|
||||
$this->setOption($option);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用样式到文字
|
||||
* @param string $text 文字
|
||||
* @return string
|
||||
*/
|
||||
public function apply($text)
|
||||
{
|
||||
$setCodes = [];
|
||||
$unsetCodes = [];
|
||||
|
||||
if (null !== $this->foreground) {
|
||||
$setCodes[] = $this->foreground['set'];
|
||||
$unsetCodes[] = $this->foreground['unset'];
|
||||
}
|
||||
if (null !== $this->background) {
|
||||
$setCodes[] = $this->background['set'];
|
||||
$unsetCodes[] = $this->background['unset'];
|
||||
}
|
||||
if (count($this->options)) {
|
||||
foreach ($this->options as $option) {
|
||||
$setCodes[] = $option['set'];
|
||||
$unsetCodes[] = $option['unset'];
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === count($setCodes)) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
return sprintf("\033[%sm%s\033[%sm", implode(';', $setCodes), $text, implode(';', $unsetCodes));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user