[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

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


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1