[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |