text: <?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;
  use think\console\input\Argument;
  use think\console\input\Definition;
  use think\console\input\Option;
  
  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  Input */
      protected $input;
  
      /** @var  Output */
      protected $output;
  
      /**
       * 构造方法
       * @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;
      }
  
      /**
       * 获取控制台
       * @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->input  = $input;
          $this->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
       * @return $this
       */
      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
       * @throws \InvalidArgumentException
       */
      private function validateName($name)
      {
          if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
              throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));
          }
      }
  
      /**
       * 输出表格
       * @param Table $table
       * @return string
       */
      protected function table(Table $table)
      {
          $content = $table->render();
          $this->output->writeln($content);
          return $content;
      }
  }
  