[ Index ]

PHP Cross Reference of phpBB-3.2.11-deutsch

title

Body

[close]

/vendor/symfony/console/ -> Application.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of the Symfony package.
   5   *
   6   * (c) Fabien Potencier <fabien@symfony.com>
   7   *
   8   * For the full copyright and license information, please view the LICENSE
   9   * file that was distributed with this source code.
  10   */
  11  
  12  namespace Symfony\Component\Console;
  13  
  14  use Symfony\Component\Console\Command\Command;
  15  use Symfony\Component\Console\Command\HelpCommand;
  16  use Symfony\Component\Console\Command\ListCommand;
  17  use Symfony\Component\Console\Descriptor\TextDescriptor;
  18  use Symfony\Component\Console\Descriptor\XmlDescriptor;
  19  use Symfony\Component\Console\Event\ConsoleCommandEvent;
  20  use Symfony\Component\Console\Event\ConsoleExceptionEvent;
  21  use Symfony\Component\Console\Event\ConsoleTerminateEvent;
  22  use Symfony\Component\Console\Exception\CommandNotFoundException;
  23  use Symfony\Component\Console\Exception\ExceptionInterface;
  24  use Symfony\Component\Console\Exception\LogicException;
  25  use Symfony\Component\Console\Formatter\OutputFormatter;
  26  use Symfony\Component\Console\Helper\DebugFormatterHelper;
  27  use Symfony\Component\Console\Helper\DialogHelper;
  28  use Symfony\Component\Console\Helper\FormatterHelper;
  29  use Symfony\Component\Console\Helper\Helper;
  30  use Symfony\Component\Console\Helper\HelperSet;
  31  use Symfony\Component\Console\Helper\ProcessHelper;
  32  use Symfony\Component\Console\Helper\ProgressHelper;
  33  use Symfony\Component\Console\Helper\QuestionHelper;
  34  use Symfony\Component\Console\Helper\TableHelper;
  35  use Symfony\Component\Console\Input\ArgvInput;
  36  use Symfony\Component\Console\Input\ArrayInput;
  37  use Symfony\Component\Console\Input\InputArgument;
  38  use Symfony\Component\Console\Input\InputAwareInterface;
  39  use Symfony\Component\Console\Input\InputDefinition;
  40  use Symfony\Component\Console\Input\InputInterface;
  41  use Symfony\Component\Console\Input\InputOption;
  42  use Symfony\Component\Console\Output\BufferedOutput;
  43  use Symfony\Component\Console\Output\ConsoleOutput;
  44  use Symfony\Component\Console\Output\ConsoleOutputInterface;
  45  use Symfony\Component\Console\Output\OutputInterface;
  46  use Symfony\Component\Debug\Exception\FatalThrowableError;
  47  use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  48  
  49  /**
  50   * An Application is the container for a collection of commands.
  51   *
  52   * It is the main entry point of a Console application.
  53   *
  54   * This class is optimized for a standard CLI environment.
  55   *
  56   * Usage:
  57   *
  58   *     $app = new Application('myapp', '1.0 (stable)');
  59   *     $app->add(new SimpleCommand());
  60   *     $app->run();
  61   *
  62   * @author Fabien Potencier <fabien@symfony.com>
  63   */
  64  class Application
  65  {
  66      private $commands = array();
  67      private $wantHelps = false;
  68      private $runningCommand;
  69      private $name;
  70      private $version;
  71      private $catchExceptions = true;
  72      private $autoExit = true;
  73      private $definition;
  74      private $helperSet;
  75      private $dispatcher;
  76      private $terminalDimensions;
  77      private $defaultCommand;
  78      private $initialized;
  79  
  80      /**
  81       * @param string $name    The name of the application
  82       * @param string $version The version of the application
  83       */
  84      public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
  85      {
  86          $this->name = $name;
  87          $this->version = $version;
  88          $this->defaultCommand = 'list';
  89      }
  90  
  91      public function setDispatcher(EventDispatcherInterface $dispatcher)
  92      {
  93          $this->dispatcher = $dispatcher;
  94      }
  95  
  96      /**
  97       * Runs the current application.
  98       *
  99       * @return int 0 if everything went fine, or an error code
 100       *
 101       * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}.
 102       */
 103      public function run(InputInterface $input = null, OutputInterface $output = null)
 104      {
 105          if (null === $input) {
 106              $input = new ArgvInput();
 107          }
 108  
 109          if (null === $output) {
 110              $output = new ConsoleOutput();
 111          }
 112  
 113          $this->configureIO($input, $output);
 114  
 115          try {
 116              $e = null;
 117              $exitCode = $this->doRun($input, $output);
 118          } catch (\Exception $e) {
 119          }
 120  
 121          if (null !== $e) {
 122              if (!$this->catchExceptions) {
 123                  throw $e;
 124              }
 125  
 126              if ($output instanceof ConsoleOutputInterface) {
 127                  $this->renderException($e, $output->getErrorOutput());
 128              } else {
 129                  $this->renderException($e, $output);
 130              }
 131  
 132              $exitCode = $this->getExitCodeForThrowable($e);
 133          }
 134  
 135          if ($this->autoExit) {
 136              if ($exitCode > 255) {
 137                  $exitCode = 255;
 138              }
 139  
 140              exit($exitCode);
 141          }
 142  
 143          return $exitCode;
 144      }
 145  
 146      /**
 147       * Runs the current application.
 148       *
 149       * @return int 0 if everything went fine, or an error code
 150       */
 151      public function doRun(InputInterface $input, OutputInterface $output)
 152      {
 153          if (true === $input->hasParameterOption(array('--version', '-V'))) {
 154              $output->writeln($this->getLongVersion());
 155  
 156              return 0;
 157          }
 158  
 159          $name = $this->getCommandName($input);
 160          if (true === $input->hasParameterOption(array('--help', '-h'))) {
 161              if (!$name) {
 162                  $name = 'help';
 163                  $input = new ArrayInput(array('command' => 'help'));
 164              } else {
 165                  $this->wantHelps = true;
 166              }
 167          }
 168  
 169          if (!$name) {
 170              $name = $this->defaultCommand;
 171              $definition = $this->getDefinition();
 172              $definition->setArguments(array_merge(
 173                  $definition->getArguments(),
 174                  array(
 175                      'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),
 176                  )
 177              ));
 178          }
 179  
 180          $this->runningCommand = null;
 181          // the command name MUST be the first element of the input
 182          $command = $this->find($name);
 183  
 184          $this->runningCommand = $command;
 185          $exitCode = $this->doRunCommand($command, $input, $output);
 186          $this->runningCommand = null;
 187  
 188          return $exitCode;
 189      }
 190  
 191      public function setHelperSet(HelperSet $helperSet)
 192      {
 193          $this->helperSet = $helperSet;
 194      }
 195  
 196      /**
 197       * Get the helper set associated with the command.
 198       *
 199       * @return HelperSet The HelperSet instance associated with this command
 200       */
 201      public function getHelperSet()
 202      {
 203          if (!$this->helperSet) {
 204              $this->helperSet = $this->getDefaultHelperSet();
 205          }
 206  
 207          return $this->helperSet;
 208      }
 209  
 210      public function setDefinition(InputDefinition $definition)
 211      {
 212          $this->definition = $definition;
 213      }
 214  
 215      /**
 216       * Gets the InputDefinition related to this Application.
 217       *
 218       * @return InputDefinition The InputDefinition instance
 219       */
 220      public function getDefinition()
 221      {
 222          if (!$this->definition) {
 223              $this->definition = $this->getDefaultInputDefinition();
 224          }
 225  
 226          return $this->definition;
 227      }
 228  
 229      /**
 230       * Gets the help message.
 231       *
 232       * @return string A help message
 233       */
 234      public function getHelp()
 235      {
 236          return $this->getLongVersion();
 237      }
 238  
 239      /**
 240       * Sets whether to catch exceptions or not during commands execution.
 241       *
 242       * @param bool $boolean Whether to catch exceptions or not during commands execution
 243       */
 244      public function setCatchExceptions($boolean)
 245      {
 246          $this->catchExceptions = (bool) $boolean;
 247      }
 248  
 249      /**
 250       * Sets whether to automatically exit after a command execution or not.
 251       *
 252       * @param bool $boolean Whether to automatically exit after a command execution or not
 253       */
 254      public function setAutoExit($boolean)
 255      {
 256          $this->autoExit = (bool) $boolean;
 257      }
 258  
 259      /**
 260       * Gets the name of the application.
 261       *
 262       * @return string The application name
 263       */
 264      public function getName()
 265      {
 266          return $this->name;
 267      }
 268  
 269      /**
 270       * Sets the application name.
 271       *
 272       * @param string $name The application name
 273       */
 274      public function setName($name)
 275      {
 276          $this->name = $name;
 277      }
 278  
 279      /**
 280       * Gets the application version.
 281       *
 282       * @return string The application version
 283       */
 284      public function getVersion()
 285      {
 286          return $this->version;
 287      }
 288  
 289      /**
 290       * Sets the application version.
 291       *
 292       * @param string $version The application version
 293       */
 294      public function setVersion($version)
 295      {
 296          $this->version = $version;
 297      }
 298  
 299      /**
 300       * Returns the long version of the application.
 301       *
 302       * @return string The long application version
 303       */
 304      public function getLongVersion()
 305      {
 306          if ('UNKNOWN' !== $this->getName()) {
 307              if ('UNKNOWN' !== $this->getVersion()) {
 308                  return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
 309              }
 310  
 311              return sprintf('<info>%s</info>', $this->getName());
 312          }
 313  
 314          return '<info>Console Tool</info>';
 315      }
 316  
 317      /**
 318       * Registers a new command.
 319       *
 320       * @param string $name The command name
 321       *
 322       * @return Command The newly created command
 323       */
 324      public function register($name)
 325      {
 326          return $this->add(new Command($name));
 327      }
 328  
 329      /**
 330       * Adds an array of command objects.
 331       *
 332       * If a Command is not enabled it will not be added.
 333       *
 334       * @param Command[] $commands An array of commands
 335       */
 336      public function addCommands(array $commands)
 337      {
 338          foreach ($commands as $command) {
 339              $this->add($command);
 340          }
 341      }
 342  
 343      /**
 344       * Adds a command object.
 345       *
 346       * If a command with the same name already exists, it will be overridden.
 347       * If the command is not enabled it will not be added.
 348       *
 349       * @return Command|null The registered command if enabled or null
 350       */
 351      public function add(Command $command)
 352      {
 353          $this->init();
 354  
 355          $command->setApplication($this);
 356  
 357          if (!$command->isEnabled()) {
 358              $command->setApplication(null);
 359  
 360              return;
 361          }
 362  
 363          if (null === $command->getDefinition()) {
 364              throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', \get_class($command)));
 365          }
 366  
 367          $this->commands[$command->getName()] = $command;
 368  
 369          foreach ($command->getAliases() as $alias) {
 370              $this->commands[$alias] = $command;
 371          }
 372  
 373          return $command;
 374      }
 375  
 376      /**
 377       * Returns a registered command by name or alias.
 378       *
 379       * @param string $name The command name or alias
 380       *
 381       * @return Command A Command object
 382       *
 383       * @throws CommandNotFoundException When given command name does not exist
 384       */
 385      public function get($name)
 386      {
 387          $this->init();
 388  
 389          if (!isset($this->commands[$name])) {
 390              throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
 391          }
 392  
 393          $command = $this->commands[$name];
 394  
 395          if ($this->wantHelps) {
 396              $this->wantHelps = false;
 397  
 398              $helpCommand = $this->get('help');
 399              $helpCommand->setCommand($command);
 400  
 401              return $helpCommand;
 402          }
 403  
 404          return $command;
 405      }
 406  
 407      /**
 408       * Returns true if the command exists, false otherwise.
 409       *
 410       * @param string $name The command name or alias
 411       *
 412       * @return bool true if the command exists, false otherwise
 413       */
 414      public function has($name)
 415      {
 416          $this->init();
 417  
 418          return isset($this->commands[$name]);
 419      }
 420  
 421      /**
 422       * Returns an array of all unique namespaces used by currently registered commands.
 423       *
 424       * It does not return the global namespace which always exists.
 425       *
 426       * @return string[] An array of namespaces
 427       */
 428      public function getNamespaces()
 429      {
 430          $namespaces = array();
 431          foreach ($this->all() as $command) {
 432              $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
 433  
 434              foreach ($command->getAliases() as $alias) {
 435                  $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
 436              }
 437          }
 438  
 439          return array_values(array_unique(array_filter($namespaces)));
 440      }
 441  
 442      /**
 443       * Finds a registered namespace by a name or an abbreviation.
 444       *
 445       * @param string $namespace A namespace or abbreviation to search for
 446       *
 447       * @return string A registered namespace
 448       *
 449       * @throws CommandNotFoundException When namespace is incorrect or ambiguous
 450       */
 451      public function findNamespace($namespace)
 452      {
 453          $allNamespaces = $this->getNamespaces();
 454          $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace);
 455          $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);
 456  
 457          if (empty($namespaces)) {
 458              $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
 459  
 460              if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
 461                  if (1 == \count($alternatives)) {
 462                      $message .= "\n\nDid you mean this?\n    ";
 463                  } else {
 464                      $message .= "\n\nDid you mean one of these?\n    ";
 465                  }
 466  
 467                  $message .= implode("\n    ", $alternatives);
 468              }
 469  
 470              throw new CommandNotFoundException($message, $alternatives);
 471          }
 472  
 473          $exact = \in_array($namespace, $namespaces, true);
 474          if (\count($namespaces) > 1 && !$exact) {
 475              throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
 476          }
 477  
 478          return $exact ? $namespace : reset($namespaces);
 479      }
 480  
 481      /**
 482       * Finds a command by name or alias.
 483       *
 484       * Contrary to get, this command tries to find the best
 485       * match if you give it an abbreviation of a name or alias.
 486       *
 487       * @param string $name A command name or a command alias
 488       *
 489       * @return Command A Command instance
 490       *
 491       * @throws CommandNotFoundException When command name is incorrect or ambiguous
 492       */
 493      public function find($name)
 494      {
 495          $this->init();
 496          $aliases = array();
 497          $allCommands = array_keys($this->commands);
 498          $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
 499          $commands = preg_grep('{^'.$expr.'}', $allCommands);
 500  
 501          if (empty($commands) || \count(preg_grep('{^'.$expr.'$}', $commands)) < 1) {
 502              if (false !== $pos = strrpos($name, ':')) {
 503                  // check if a namespace exists and contains commands
 504                  $this->findNamespace(substr($name, 0, $pos));
 505              }
 506  
 507              $message = sprintf('Command "%s" is not defined.', $name);
 508  
 509              if ($alternatives = $this->findAlternatives($name, $allCommands)) {
 510                  if (1 == \count($alternatives)) {
 511                      $message .= "\n\nDid you mean this?\n    ";
 512                  } else {
 513                      $message .= "\n\nDid you mean one of these?\n    ";
 514                  }
 515                  $message .= implode("\n    ", $alternatives);
 516              }
 517  
 518              throw new CommandNotFoundException($message, $alternatives);
 519          }
 520  
 521          // filter out aliases for commands which are already on the list
 522          if (\count($commands) > 1) {
 523              $commandList = $this->commands;
 524              $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands, &$aliases) {
 525                  $commandName = $commandList[$nameOrAlias]->getName();
 526                  $aliases[$nameOrAlias] = $commandName;
 527  
 528                  return $commandName === $nameOrAlias || !\in_array($commandName, $commands);
 529              });
 530          }
 531  
 532          $exact = \in_array($name, $commands, true) || isset($aliases[$name]);
 533          if (!$exact && \count($commands) > 1) {
 534              $suggestions = $this->getAbbreviationSuggestions(array_values($commands));
 535  
 536              throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands));
 537          }
 538  
 539          return $this->get($exact ? $name : reset($commands));
 540      }
 541  
 542      /**
 543       * Gets the commands (registered in the given namespace if provided).
 544       *
 545       * The array keys are the full names and the values the command instances.
 546       *
 547       * @param string $namespace A namespace name
 548       *
 549       * @return Command[] An array of Command instances
 550       */
 551      public function all($namespace = null)
 552      {
 553          $this->init();
 554  
 555          if (null === $namespace) {
 556              return $this->commands;
 557          }
 558  
 559          $commands = array();
 560          foreach ($this->commands as $name => $command) {
 561              if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
 562                  $commands[$name] = $command;
 563              }
 564          }
 565  
 566          return $commands;
 567      }
 568  
 569      /**
 570       * Returns an array of possible abbreviations given a set of names.
 571       *
 572       * @param array $names An array of names
 573       *
 574       * @return array An array of abbreviations
 575       */
 576      public static function getAbbreviations($names)
 577      {
 578          $abbrevs = array();
 579          foreach ($names as $name) {
 580              for ($len = \strlen($name); $len > 0; --$len) {
 581                  $abbrev = substr($name, 0, $len);
 582                  $abbrevs[$abbrev][] = $name;
 583              }
 584          }
 585  
 586          return $abbrevs;
 587      }
 588  
 589      /**
 590       * Returns a text representation of the Application.
 591       *
 592       * @param string $namespace An optional namespace name
 593       * @param bool   $raw       Whether to return raw command list
 594       *
 595       * @return string A string representing the Application
 596       *
 597       * @deprecated since version 2.3, to be removed in 3.0.
 598       */
 599      public function asText($namespace = null, $raw = false)
 600      {
 601          @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.3 and will be removed in 3.0.', E_USER_DEPRECATED);
 602  
 603          $descriptor = new TextDescriptor();
 604          $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, !$raw);
 605          $descriptor->describe($output, $this, array('namespace' => $namespace, 'raw_output' => true));
 606  
 607          return $output->fetch();
 608      }
 609  
 610      /**
 611       * Returns an XML representation of the Application.
 612       *
 613       * @param string $namespace An optional namespace name
 614       * @param bool   $asDom     Whether to return a DOM or an XML string
 615       *
 616       * @return string|\DOMDocument An XML string representing the Application
 617       *
 618       * @deprecated since version 2.3, to be removed in 3.0.
 619       */
 620      public function asXml($namespace = null, $asDom = false)
 621      {
 622          @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.3 and will be removed in 3.0.', E_USER_DEPRECATED);
 623  
 624          $descriptor = new XmlDescriptor();
 625  
 626          if ($asDom) {
 627              return $descriptor->getApplicationDocument($this, $namespace);
 628          }
 629  
 630          $output = new BufferedOutput();
 631          $descriptor->describe($output, $this, array('namespace' => $namespace));
 632  
 633          return $output->fetch();
 634      }
 635  
 636      /**
 637       * Renders a caught exception.
 638       */
 639      public function renderException($e, $output)
 640      {
 641          $output->writeln('', OutputInterface::VERBOSITY_QUIET);
 642  
 643          do {
 644              $title = sprintf('  [%s]  ', \get_class($e));
 645  
 646              $len = Helper::strlen($title);
 647  
 648              $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
 649              // HHVM only accepts 32 bits integer in str_split, even when PHP_INT_MAX is a 64 bit integer: https://github.com/facebook/hhvm/issues/1327
 650              if (\defined('HHVM_VERSION') && $width > 1 << 31) {
 651                  $width = 1 << 31;
 652              }
 653              $lines = array();
 654              foreach (preg_split('/\r?\n/', trim($e->getMessage())) as $line) {
 655                  foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
 656                      // pre-format lines to get the right string length
 657                      $lineLength = Helper::strlen($line) + 4;
 658                      $lines[] = array($line, $lineLength);
 659  
 660                      $len = max($lineLength, $len);
 661                  }
 662              }
 663  
 664              $messages = array();
 665              $messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
 666              $messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::strlen($title))));
 667              foreach ($lines as $line) {
 668                  $messages[] = sprintf('<error>  %s  %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1]));
 669              }
 670              $messages[] = $emptyLine;
 671              $messages[] = '';
 672  
 673              $output->writeln($messages, OutputInterface::VERBOSITY_QUIET);
 674  
 675              if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
 676                  $output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET);
 677  
 678                  // exception related properties
 679                  $trace = $e->getTrace();
 680                  array_unshift($trace, array(
 681                      'function' => '',
 682                      'file' => null !== $e->getFile() ? $e->getFile() : 'n/a',
 683                      'line' => null !== $e->getLine() ? $e->getLine() : 'n/a',
 684                      'args' => array(),
 685                  ));
 686  
 687                  for ($i = 0, $count = \count($trace); $i < $count; ++$i) {
 688                      $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
 689                      $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
 690                      $function = $trace[$i]['function'];
 691                      $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
 692                      $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
 693  
 694                      $output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line), OutputInterface::VERBOSITY_QUIET);
 695                  }
 696  
 697                  $output->writeln('', OutputInterface::VERBOSITY_QUIET);
 698              }
 699          } while ($e = $e->getPrevious());
 700  
 701          if (null !== $this->runningCommand) {
 702              $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
 703              $output->writeln('', OutputInterface::VERBOSITY_QUIET);
 704          }
 705      }
 706  
 707      /**
 708       * Tries to figure out the terminal width in which this application runs.
 709       *
 710       * @return int|null
 711       */
 712      protected function getTerminalWidth()
 713      {
 714          $dimensions = $this->getTerminalDimensions();
 715  
 716          return $dimensions[0];
 717      }
 718  
 719      /**
 720       * Tries to figure out the terminal height in which this application runs.
 721       *
 722       * @return int|null
 723       */
 724      protected function getTerminalHeight()
 725      {
 726          $dimensions = $this->getTerminalDimensions();
 727  
 728          return $dimensions[1];
 729      }
 730  
 731      /**
 732       * Tries to figure out the terminal dimensions based on the current environment.
 733       *
 734       * @return array Array containing width and height
 735       */
 736      public function getTerminalDimensions()
 737      {
 738          if ($this->terminalDimensions) {
 739              return $this->terminalDimensions;
 740          }
 741  
 742          if ('\\' === \DIRECTORY_SEPARATOR) {
 743              // extract [w, H] from "wxh (WxH)"
 744              if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
 745                  return array((int) $matches[1], (int) $matches[2]);
 746              }
 747              // extract [w, h] from "wxh"
 748              if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) {
 749                  return array((int) $matches[1], (int) $matches[2]);
 750              }
 751          }
 752  
 753          if ($sttyString = $this->getSttyColumns()) {
 754              // extract [w, h] from "rows h; columns w;"
 755              if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
 756                  return array((int) $matches[2], (int) $matches[1]);
 757              }
 758              // extract [w, h] from "; h rows; w columns"
 759              if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
 760                  return array((int) $matches[2], (int) $matches[1]);
 761              }
 762          }
 763  
 764          return array(null, null);
 765      }
 766  
 767      /**
 768       * Sets terminal dimensions.
 769       *
 770       * Can be useful to force terminal dimensions for functional tests.
 771       *
 772       * @param int $width  The width
 773       * @param int $height The height
 774       *
 775       * @return $this
 776       */
 777      public function setTerminalDimensions($width, $height)
 778      {
 779          $this->terminalDimensions = array($width, $height);
 780  
 781          return $this;
 782      }
 783  
 784      /**
 785       * Configures the input and output instances based on the user arguments and options.
 786       */
 787      protected function configureIO(InputInterface $input, OutputInterface $output)
 788      {
 789          if (true === $input->hasParameterOption(array('--ansi'))) {
 790              $output->setDecorated(true);
 791          } elseif (true === $input->hasParameterOption(array('--no-ansi'))) {
 792              $output->setDecorated(false);
 793          }
 794  
 795          if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) {
 796              $input->setInteractive(false);
 797          } elseif (\function_exists('posix_isatty') && $this->getHelperSet()->has('question')) {
 798              $inputStream = $this->getHelperSet()->get('question')->getInputStream();
 799              if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) {
 800                  $input->setInteractive(false);
 801              }
 802          }
 803  
 804          if (true === $input->hasParameterOption(array('--quiet', '-q'))) {
 805              $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
 806              $input->setInteractive(false);
 807          } else {
 808              if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || 3 === $input->getParameterOption('--verbose')) {
 809                  $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
 810              } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || 2 === $input->getParameterOption('--verbose')) {
 811                  $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
 812              } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
 813                  $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
 814              }
 815          }
 816      }
 817  
 818      /**
 819       * Runs the current command.
 820       *
 821       * If an event dispatcher has been attached to the application,
 822       * events are also dispatched during the life-cycle of the command.
 823       *
 824       * @return int 0 if everything went fine, or an error code
 825       */
 826      protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
 827      {
 828          foreach ($command->getHelperSet() as $helper) {
 829              if ($helper instanceof InputAwareInterface) {
 830                  $helper->setInput($input);
 831              }
 832          }
 833  
 834          if (null === $this->dispatcher) {
 835              return $command->run($input, $output);
 836          }
 837  
 838          // bind before the console.command event, so the listeners have access to input options/arguments
 839          try {
 840              $command->mergeApplicationDefinition();
 841              $input->bind($command->getDefinition());
 842          } catch (ExceptionInterface $e) {
 843              // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition
 844          }
 845  
 846          $event = new ConsoleCommandEvent($command, $input, $output);
 847          $e = null;
 848  
 849          try {
 850              $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
 851  
 852              if ($event->commandShouldRun()) {
 853                  $exitCode = $command->run($input, $output);
 854              } else {
 855                  $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
 856              }
 857          } catch (\Exception $e) {
 858          } catch (\Throwable $e) {
 859          }
 860          if (null !== $e) {
 861              $x = $e instanceof \Exception ? $e : new FatalThrowableError($e);
 862              $event = new ConsoleExceptionEvent($command, $input, $output, $x, $x->getCode());
 863              $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
 864  
 865              if ($x !== $event->getException()) {
 866                  $e = $event->getException();
 867              }
 868  
 869              $exitCode = $this->getExitCodeForThrowable($e);
 870          }
 871  
 872          $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
 873          $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
 874  
 875          if (null !== $e) {
 876              throw $e;
 877          }
 878  
 879          return $event->getExitCode();
 880      }
 881  
 882      /**
 883       * Gets the name of the command based on input.
 884       *
 885       * @return string The command name
 886       */
 887      protected function getCommandName(InputInterface $input)
 888      {
 889          return $input->getFirstArgument();
 890      }
 891  
 892      /**
 893       * Gets the default input definition.
 894       *
 895       * @return InputDefinition An InputDefinition instance
 896       */
 897      protected function getDefaultInputDefinition()
 898      {
 899          return new InputDefinition(array(
 900              new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
 901  
 902              new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
 903              new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
 904              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'),
 905              new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
 906              new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
 907              new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
 908              new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
 909          ));
 910      }
 911  
 912      /**
 913       * Gets the default commands that should always be available.
 914       *
 915       * @return Command[] An array of default Command instances
 916       */
 917      protected function getDefaultCommands()
 918      {
 919          return array(new HelpCommand(), new ListCommand());
 920      }
 921  
 922      /**
 923       * Gets the default helper set with the helpers that should always be available.
 924       *
 925       * @return HelperSet A HelperSet instance
 926       */
 927      protected function getDefaultHelperSet()
 928      {
 929          return new HelperSet(array(
 930              new FormatterHelper(),
 931              new DialogHelper(false),
 932              new ProgressHelper(false),
 933              new TableHelper(false),
 934              new DebugFormatterHelper(),
 935              new ProcessHelper(),
 936              new QuestionHelper(),
 937          ));
 938      }
 939  
 940      /**
 941       * Runs and parses stty -a if it's available, suppressing any error output.
 942       *
 943       * @return string
 944       */
 945      private function getSttyColumns()
 946      {
 947          if (!\function_exists('proc_open')) {
 948              return;
 949          }
 950  
 951          $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
 952          $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
 953          if (\is_resource($process)) {
 954              $info = stream_get_contents($pipes[1]);
 955              fclose($pipes[1]);
 956              fclose($pipes[2]);
 957              proc_close($process);
 958  
 959              return $info;
 960          }
 961      }
 962  
 963      /**
 964       * Runs and parses mode CON if it's available, suppressing any error output.
 965       *
 966       * @return string|null <width>x<height> or null if it could not be parsed
 967       */
 968      private function getConsoleMode()
 969      {
 970          if (!\function_exists('proc_open')) {
 971              return;
 972          }
 973  
 974          $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
 975          $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
 976          if (\is_resource($process)) {
 977              $info = stream_get_contents($pipes[1]);
 978              fclose($pipes[1]);
 979              fclose($pipes[2]);
 980              proc_close($process);
 981  
 982              if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
 983                  return $matches[2].'x'.$matches[1];
 984              }
 985          }
 986      }
 987  
 988      /**
 989       * Returns abbreviated suggestions in string format.
 990       *
 991       * @param array $abbrevs Abbreviated suggestions to convert
 992       *
 993       * @return string A formatted string of abbreviated suggestions
 994       */
 995      private function getAbbreviationSuggestions($abbrevs)
 996      {
 997          return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], \count($abbrevs) > 2 ? sprintf(' and %d more', \count($abbrevs) - 2) : '');
 998      }
 999  
1000      /**
1001       * Returns the namespace part of the command name.
1002       *
1003       * This method is not part of public API and should not be used directly.
1004       *
1005       * @param string $name  The full name of the command
1006       * @param string $limit The maximum number of parts of the namespace
1007       *
1008       * @return string The namespace of the command
1009       */
1010      public function extractNamespace($name, $limit = null)
1011      {
1012          $parts = explode(':', $name);
1013          array_pop($parts);
1014  
1015          return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit));
1016      }
1017  
1018      /**
1019       * Finds alternative of $name among $collection,
1020       * if nothing is found in $collection, try in $abbrevs.
1021       *
1022       * @param string   $name       The string
1023       * @param iterable $collection The collection
1024       *
1025       * @return string[] A sorted array of similar string
1026       */
1027      private function findAlternatives($name, $collection)
1028      {
1029          $threshold = 1e3;
1030          $alternatives = array();
1031  
1032          $collectionParts = array();
1033          foreach ($collection as $item) {
1034              $collectionParts[$item] = explode(':', $item);
1035          }
1036  
1037          foreach (explode(':', $name) as $i => $subname) {
1038              foreach ($collectionParts as $collectionName => $parts) {
1039                  $exists = isset($alternatives[$collectionName]);
1040                  if (!isset($parts[$i]) && $exists) {
1041                      $alternatives[$collectionName] += $threshold;
1042                      continue;
1043                  } elseif (!isset($parts[$i])) {
1044                      continue;
1045                  }
1046  
1047                  $lev = levenshtein($subname, $parts[$i]);
1048                  if ($lev <= \strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
1049                      $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
1050                  } elseif ($exists) {
1051                      $alternatives[$collectionName] += $threshold;
1052                  }
1053              }
1054          }
1055  
1056          foreach ($collection as $item) {
1057              $lev = levenshtein($name, $item);
1058              if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) {
1059                  $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
1060              }
1061          }
1062  
1063          $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
1064          asort($alternatives);
1065  
1066          return array_keys($alternatives);
1067      }
1068  
1069      /**
1070       * Sets the default Command name.
1071       *
1072       * @param string $commandName The Command name
1073       */
1074      public function setDefaultCommand($commandName)
1075      {
1076          $this->defaultCommand = $commandName;
1077      }
1078  
1079      private function splitStringByWidth($string, $width)
1080      {
1081          // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
1082          // additionally, array_slice() is not enough as some character has doubled width.
1083          // we need a function to split string not by character count but by string width
1084          if (false === $encoding = mb_detect_encoding($string, null, true)) {
1085              return str_split($string, $width);
1086          }
1087  
1088          $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
1089          $lines = array();
1090          $line = '';
1091          foreach (preg_split('//u', $utf8String) as $char) {
1092              // test if $char could be appended to current line
1093              if (mb_strwidth($line.$char, 'utf8') <= $width) {
1094                  $line .= $char;
1095                  continue;
1096              }
1097              // if not, push current line to array and make new line
1098              $lines[] = str_pad($line, $width);
1099              $line = $char;
1100          }
1101  
1102          $lines[] = \count($lines) ? str_pad($line, $width) : $line;
1103  
1104          mb_convert_variables($encoding, 'utf8', $lines);
1105  
1106          return $lines;
1107      }
1108  
1109      /**
1110       * Returns all namespaces of the command name.
1111       *
1112       * @param string $name The full name of the command
1113       *
1114       * @return string[] The namespaces of the command
1115       */
1116      private function extractAllNamespaces($name)
1117      {
1118          // -1 as third argument is needed to skip the command short name when exploding
1119          $parts = explode(':', $name, -1);
1120          $namespaces = array();
1121  
1122          foreach ($parts as $part) {
1123              if (\count($namespaces)) {
1124                  $namespaces[] = end($namespaces).':'.$part;
1125              } else {
1126                  $namespaces[] = $part;
1127              }
1128          }
1129  
1130          return $namespaces;
1131      }
1132  
1133      private function init()
1134      {
1135          if ($this->initialized) {
1136              return;
1137          }
1138          $this->initialized = true;
1139  
1140          foreach ($this->getDefaultCommands() as $command) {
1141              $this->add($command);
1142          }
1143      }
1144  
1145      /**
1146       * @param \Exception|\Throwable $throwable
1147       *
1148       * @return int
1149       */
1150      private function getExitCodeForThrowable($throwable)
1151      {
1152          $exitCode = $throwable->getCode();
1153          if (is_numeric($exitCode)) {
1154              $exitCode = (int) $exitCode;
1155              if (0 === $exitCode) {
1156                  $exitCode = 1;
1157              }
1158          } else {
1159              $exitCode = 1;
1160          }
1161  
1162          return $exitCode;
1163      }
1164  }


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1