Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/console/Application.php @ 14:1fec387a4317
Update Drupal core to 8.5.2 via Composer
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:46:53 +0100 |
parents | 7a779792577d |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
13:5fb285c0d0e3 | 14:1fec387a4317 |
---|---|
9 * file that was distributed with this source code. | 9 * file that was distributed with this source code. |
10 */ | 10 */ |
11 | 11 |
12 namespace Symfony\Component\Console; | 12 namespace Symfony\Component\Console; |
13 | 13 |
14 use Symfony\Component\Console\CommandLoader\CommandLoaderInterface; | |
14 use Symfony\Component\Console\Exception\ExceptionInterface; | 15 use Symfony\Component\Console\Exception\ExceptionInterface; |
15 use Symfony\Component\Console\Formatter\OutputFormatter; | 16 use Symfony\Component\Console\Formatter\OutputFormatter; |
16 use Symfony\Component\Console\Helper\DebugFormatterHelper; | 17 use Symfony\Component\Console\Helper\DebugFormatterHelper; |
17 use Symfony\Component\Console\Helper\Helper; | 18 use Symfony\Component\Console\Helper\Helper; |
18 use Symfony\Component\Console\Helper\ProcessHelper; | 19 use Symfony\Component\Console\Helper\ProcessHelper; |
32 use Symfony\Component\Console\Command\HelpCommand; | 33 use Symfony\Component\Console\Command\HelpCommand; |
33 use Symfony\Component\Console\Command\ListCommand; | 34 use Symfony\Component\Console\Command\ListCommand; |
34 use Symfony\Component\Console\Helper\HelperSet; | 35 use Symfony\Component\Console\Helper\HelperSet; |
35 use Symfony\Component\Console\Helper\FormatterHelper; | 36 use Symfony\Component\Console\Helper\FormatterHelper; |
36 use Symfony\Component\Console\Event\ConsoleCommandEvent; | 37 use Symfony\Component\Console\Event\ConsoleCommandEvent; |
38 use Symfony\Component\Console\Event\ConsoleErrorEvent; | |
37 use Symfony\Component\Console\Event\ConsoleExceptionEvent; | 39 use Symfony\Component\Console\Event\ConsoleExceptionEvent; |
38 use Symfony\Component\Console\Event\ConsoleTerminateEvent; | 40 use Symfony\Component\Console\Event\ConsoleTerminateEvent; |
39 use Symfony\Component\Console\Exception\CommandNotFoundException; | 41 use Symfony\Component\Console\Exception\CommandNotFoundException; |
40 use Symfony\Component\Console\Exception\LogicException; | 42 use Symfony\Component\Console\Exception\LogicException; |
43 use Symfony\Component\Debug\ErrorHandler; | |
41 use Symfony\Component\Debug\Exception\FatalThrowableError; | 44 use Symfony\Component\Debug\Exception\FatalThrowableError; |
42 use Symfony\Component\EventDispatcher\EventDispatcherInterface; | 45 use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
43 | 46 |
44 /** | 47 /** |
45 * An Application is the container for a collection of commands. | 48 * An Application is the container for a collection of commands. |
61 private $commands = array(); | 64 private $commands = array(); |
62 private $wantHelps = false; | 65 private $wantHelps = false; |
63 private $runningCommand; | 66 private $runningCommand; |
64 private $name; | 67 private $name; |
65 private $version; | 68 private $version; |
69 private $commandLoader; | |
66 private $catchExceptions = true; | 70 private $catchExceptions = true; |
67 private $autoExit = true; | 71 private $autoExit = true; |
68 private $definition; | 72 private $definition; |
69 private $helperSet; | 73 private $helperSet; |
70 private $dispatcher; | 74 private $dispatcher; |
71 private $terminal; | 75 private $terminal; |
72 private $defaultCommand; | 76 private $defaultCommand; |
73 private $singleCommand; | 77 private $singleCommand; |
78 private $initialized; | |
74 | 79 |
75 /** | 80 /** |
76 * @param string $name The name of the application | 81 * @param string $name The name of the application |
77 * @param string $version The version of the application | 82 * @param string $version The version of the application |
78 */ | 83 */ |
80 { | 85 { |
81 $this->name = $name; | 86 $this->name = $name; |
82 $this->version = $version; | 87 $this->version = $version; |
83 $this->terminal = new Terminal(); | 88 $this->terminal = new Terminal(); |
84 $this->defaultCommand = 'list'; | 89 $this->defaultCommand = 'list'; |
85 $this->helperSet = $this->getDefaultHelperSet(); | |
86 $this->definition = $this->getDefaultInputDefinition(); | |
87 | |
88 foreach ($this->getDefaultCommands() as $command) { | |
89 $this->add($command); | |
90 } | |
91 } | 90 } |
92 | 91 |
93 public function setDispatcher(EventDispatcherInterface $dispatcher) | 92 public function setDispatcher(EventDispatcherInterface $dispatcher) |
94 { | 93 { |
95 $this->dispatcher = $dispatcher; | 94 $this->dispatcher = $dispatcher; |
96 } | 95 } |
97 | 96 |
97 public function setCommandLoader(CommandLoaderInterface $commandLoader) | |
98 { | |
99 $this->commandLoader = $commandLoader; | |
100 } | |
101 | |
98 /** | 102 /** |
99 * Runs the current application. | 103 * Runs the current application. |
100 * | |
101 * @param InputInterface $input An Input instance | |
102 * @param OutputInterface $output An Output instance | |
103 * | 104 * |
104 * @return int 0 if everything went fine, or an error code | 105 * @return int 0 if everything went fine, or an error code |
105 * | 106 * |
106 * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}. | 107 * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}. |
107 */ | 108 */ |
116 | 117 |
117 if (null === $output) { | 118 if (null === $output) { |
118 $output = new ConsoleOutput(); | 119 $output = new ConsoleOutput(); |
119 } | 120 } |
120 | 121 |
121 $this->configureIO($input, $output); | 122 $renderException = function ($e) use ($output) { |
122 | 123 if (!$e instanceof \Exception) { |
123 try { | 124 $e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); |
124 $e = null; | 125 } |
125 $exitCode = $this->doRun($input, $output); | |
126 } catch (\Exception $x) { | |
127 $e = $x; | |
128 } catch (\Throwable $x) { | |
129 $e = new FatalThrowableError($x); | |
130 } | |
131 | |
132 if (null !== $e) { | |
133 if (!$this->catchExceptions || !$x instanceof \Exception) { | |
134 throw $x; | |
135 } | |
136 | |
137 if ($output instanceof ConsoleOutputInterface) { | 126 if ($output instanceof ConsoleOutputInterface) { |
138 $this->renderException($e, $output->getErrorOutput()); | 127 $this->renderException($e, $output->getErrorOutput()); |
139 } else { | 128 } else { |
140 $this->renderException($e, $output); | 129 $this->renderException($e, $output); |
141 } | 130 } |
131 }; | |
132 if ($phpHandler = set_exception_handler($renderException)) { | |
133 restore_exception_handler(); | |
134 if (!is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) { | |
135 $debugHandler = true; | |
136 } elseif ($debugHandler = $phpHandler[0]->setExceptionHandler($renderException)) { | |
137 $phpHandler[0]->setExceptionHandler($debugHandler); | |
138 } | |
139 } | |
140 | |
141 if (null !== $this->dispatcher && $this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) { | |
142 @trigger_error(sprintf('The "ConsoleEvents::EXCEPTION" event is deprecated since Symfony 3.3 and will be removed in 4.0. Listen to the "ConsoleEvents::ERROR" event instead.'), E_USER_DEPRECATED); | |
143 } | |
144 | |
145 $this->configureIO($input, $output); | |
146 | |
147 try { | |
148 $exitCode = $this->doRun($input, $output); | |
149 } catch (\Exception $e) { | |
150 if (!$this->catchExceptions) { | |
151 throw $e; | |
152 } | |
153 | |
154 $renderException($e); | |
142 | 155 |
143 $exitCode = $e->getCode(); | 156 $exitCode = $e->getCode(); |
144 if (is_numeric($exitCode)) { | 157 if (is_numeric($exitCode)) { |
145 $exitCode = (int) $exitCode; | 158 $exitCode = (int) $exitCode; |
146 if (0 === $exitCode) { | 159 if (0 === $exitCode) { |
147 $exitCode = 1; | 160 $exitCode = 1; |
148 } | 161 } |
149 } else { | 162 } else { |
150 $exitCode = 1; | 163 $exitCode = 1; |
151 } | 164 } |
165 } finally { | |
166 // if the exception handler changed, keep it | |
167 // otherwise, unregister $renderException | |
168 if (!$phpHandler) { | |
169 if (set_exception_handler($renderException) === $renderException) { | |
170 restore_exception_handler(); | |
171 } | |
172 restore_exception_handler(); | |
173 } elseif (!$debugHandler) { | |
174 $finalHandler = $phpHandler[0]->setExceptionHandler(null); | |
175 if ($finalHandler !== $renderException) { | |
176 $phpHandler[0]->setExceptionHandler($finalHandler); | |
177 } | |
178 } | |
152 } | 179 } |
153 | 180 |
154 if ($this->autoExit) { | 181 if ($this->autoExit) { |
155 if ($exitCode > 255) { | 182 if ($exitCode > 255) { |
156 $exitCode = 255; | 183 $exitCode = 255; |
162 return $exitCode; | 189 return $exitCode; |
163 } | 190 } |
164 | 191 |
165 /** | 192 /** |
166 * Runs the current application. | 193 * Runs the current application. |
167 * | |
168 * @param InputInterface $input An Input instance | |
169 * @param OutputInterface $output An Output instance | |
170 * | 194 * |
171 * @return int 0 if everything went fine, or an error code | 195 * @return int 0 if everything went fine, or an error code |
172 */ | 196 */ |
173 public function doRun(InputInterface $input, OutputInterface $output) | 197 public function doRun(InputInterface $input, OutputInterface $output) |
174 { | 198 { |
188 } | 212 } |
189 } | 213 } |
190 | 214 |
191 if (!$name) { | 215 if (!$name) { |
192 $name = $this->defaultCommand; | 216 $name = $this->defaultCommand; |
193 $this->definition->setArguments(array_merge( | 217 $definition = $this->getDefinition(); |
194 $this->definition->getArguments(), | 218 $definition->setArguments(array_merge( |
219 $definition->getArguments(), | |
195 array( | 220 array( |
196 'command' => new InputArgument('command', InputArgument::OPTIONAL, $this->definition->getArgument('command')->getDescription(), $name), | 221 'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name), |
197 ) | 222 ) |
198 )); | 223 )); |
199 } | 224 } |
200 | 225 |
201 $this->runningCommand = null; | 226 try { |
202 // the command name MUST be the first element of the input | 227 $e = $this->runningCommand = null; |
203 $command = $this->find($name); | 228 // the command name MUST be the first element of the input |
229 $command = $this->find($name); | |
230 } catch (\Exception $e) { | |
231 } catch (\Throwable $e) { | |
232 } | |
233 if (null !== $e) { | |
234 if (null !== $this->dispatcher) { | |
235 $event = new ConsoleErrorEvent($input, $output, $e); | |
236 $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event); | |
237 $e = $event->getError(); | |
238 | |
239 if (0 === $event->getExitCode()) { | |
240 return 0; | |
241 } | |
242 } | |
243 | |
244 throw $e; | |
245 } | |
204 | 246 |
205 $this->runningCommand = $command; | 247 $this->runningCommand = $command; |
206 $exitCode = $this->doRunCommand($command, $input, $output); | 248 $exitCode = $this->doRunCommand($command, $input, $output); |
207 $this->runningCommand = null; | 249 $this->runningCommand = null; |
208 | 250 |
209 return $exitCode; | 251 return $exitCode; |
210 } | 252 } |
211 | 253 |
212 /** | |
213 * Set a helper set to be used with the command. | |
214 * | |
215 * @param HelperSet $helperSet The helper set | |
216 */ | |
217 public function setHelperSet(HelperSet $helperSet) | 254 public function setHelperSet(HelperSet $helperSet) |
218 { | 255 { |
219 $this->helperSet = $helperSet; | 256 $this->helperSet = $helperSet; |
220 } | 257 } |
221 | 258 |
224 * | 261 * |
225 * @return HelperSet The HelperSet instance associated with this command | 262 * @return HelperSet The HelperSet instance associated with this command |
226 */ | 263 */ |
227 public function getHelperSet() | 264 public function getHelperSet() |
228 { | 265 { |
266 if (!$this->helperSet) { | |
267 $this->helperSet = $this->getDefaultHelperSet(); | |
268 } | |
269 | |
229 return $this->helperSet; | 270 return $this->helperSet; |
230 } | 271 } |
231 | 272 |
232 /** | |
233 * Set an input definition to be used with this application. | |
234 * | |
235 * @param InputDefinition $definition The input definition | |
236 */ | |
237 public function setDefinition(InputDefinition $definition) | 273 public function setDefinition(InputDefinition $definition) |
238 { | 274 { |
239 $this->definition = $definition; | 275 $this->definition = $definition; |
240 } | 276 } |
241 | 277 |
244 * | 280 * |
245 * @return InputDefinition The InputDefinition instance | 281 * @return InputDefinition The InputDefinition instance |
246 */ | 282 */ |
247 public function getDefinition() | 283 public function getDefinition() |
248 { | 284 { |
285 if (!$this->definition) { | |
286 $this->definition = $this->getDefaultInputDefinition(); | |
287 } | |
288 | |
249 if ($this->singleCommand) { | 289 if ($this->singleCommand) { |
250 $inputDefinition = $this->definition; | 290 $inputDefinition = $this->definition; |
251 $inputDefinition->setArguments(); | 291 $inputDefinition->setArguments(); |
252 | 292 |
253 return $inputDefinition; | 293 return $inputDefinition; |
394 * Adds a command object. | 434 * Adds a command object. |
395 * | 435 * |
396 * If a command with the same name already exists, it will be overridden. | 436 * If a command with the same name already exists, it will be overridden. |
397 * If the command is not enabled it will not be added. | 437 * If the command is not enabled it will not be added. |
398 * | 438 * |
399 * @param Command $command A Command object | |
400 * | |
401 * @return Command|null The registered command if enabled or null | 439 * @return Command|null The registered command if enabled or null |
402 */ | 440 */ |
403 public function add(Command $command) | 441 public function add(Command $command) |
404 { | 442 { |
443 $this->init(); | |
444 | |
405 $command->setApplication($this); | 445 $command->setApplication($this); |
406 | 446 |
407 if (!$command->isEnabled()) { | 447 if (!$command->isEnabled()) { |
408 $command->setApplication(null); | 448 $command->setApplication(null); |
409 | 449 |
412 | 452 |
413 if (null === $command->getDefinition()) { | 453 if (null === $command->getDefinition()) { |
414 throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))); | 454 throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command))); |
415 } | 455 } |
416 | 456 |
457 if (!$command->getName()) { | |
458 throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_class($command))); | |
459 } | |
460 | |
417 $this->commands[$command->getName()] = $command; | 461 $this->commands[$command->getName()] = $command; |
418 | 462 |
419 foreach ($command->getAliases() as $alias) { | 463 foreach ($command->getAliases() as $alias) { |
420 $this->commands[$alias] = $command; | 464 $this->commands[$alias] = $command; |
421 } | 465 } |
432 * | 476 * |
433 * @throws CommandNotFoundException When given command name does not exist | 477 * @throws CommandNotFoundException When given command name does not exist |
434 */ | 478 */ |
435 public function get($name) | 479 public function get($name) |
436 { | 480 { |
437 if (!isset($this->commands[$name])) { | 481 $this->init(); |
482 | |
483 if (!$this->has($name)) { | |
438 throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); | 484 throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name)); |
439 } | 485 } |
440 | 486 |
441 $command = $this->commands[$name]; | 487 $command = $this->commands[$name]; |
442 | 488 |
459 * | 505 * |
460 * @return bool true if the command exists, false otherwise | 506 * @return bool true if the command exists, false otherwise |
461 */ | 507 */ |
462 public function has($name) | 508 public function has($name) |
463 { | 509 { |
464 return isset($this->commands[$name]); | 510 $this->init(); |
511 | |
512 return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name))); | |
465 } | 513 } |
466 | 514 |
467 /** | 515 /** |
468 * Returns an array of all unique namespaces used by currently registered commands. | 516 * Returns an array of all unique namespaces used by currently registered commands. |
469 * | 517 * |
516 throw new CommandNotFoundException($message, $alternatives); | 564 throw new CommandNotFoundException($message, $alternatives); |
517 } | 565 } |
518 | 566 |
519 $exact = in_array($namespace, $namespaces, true); | 567 $exact = in_array($namespace, $namespaces, true); |
520 if (count($namespaces) > 1 && !$exact) { | 568 if (count($namespaces) > 1 && !$exact) { |
521 throw new CommandNotFoundException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); | 569 throw new CommandNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces)); |
522 } | 570 } |
523 | 571 |
524 return $exact ? $namespace : reset($namespaces); | 572 return $exact ? $namespace : reset($namespaces); |
525 } | 573 } |
526 | 574 |
536 * | 584 * |
537 * @throws CommandNotFoundException When command name is incorrect or ambiguous | 585 * @throws CommandNotFoundException When command name is incorrect or ambiguous |
538 */ | 586 */ |
539 public function find($name) | 587 public function find($name) |
540 { | 588 { |
541 $allCommands = array_keys($this->commands); | 589 $this->init(); |
590 | |
591 $aliases = array(); | |
592 $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands); | |
542 $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); | 593 $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name); |
543 $commands = preg_grep('{^'.$expr.'}', $allCommands); | 594 $commands = preg_grep('{^'.$expr.'}', $allCommands); |
544 | 595 |
545 if (empty($commands) || count(preg_grep('{^'.$expr.'$}', $commands)) < 1) { | 596 if (empty($commands)) { |
597 $commands = preg_grep('{^'.$expr.'}i', $allCommands); | |
598 } | |
599 | |
600 // if no commands matched or we just matched namespaces | |
601 if (empty($commands) || count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) { | |
546 if (false !== $pos = strrpos($name, ':')) { | 602 if (false !== $pos = strrpos($name, ':')) { |
547 // check if a namespace exists and contains commands | 603 // check if a namespace exists and contains commands |
548 $this->findNamespace(substr($name, 0, $pos)); | 604 $this->findNamespace(substr($name, 0, $pos)); |
549 } | 605 } |
550 | 606 |
562 throw new CommandNotFoundException($message, $alternatives); | 618 throw new CommandNotFoundException($message, $alternatives); |
563 } | 619 } |
564 | 620 |
565 // filter out aliases for commands which are already on the list | 621 // filter out aliases for commands which are already on the list |
566 if (count($commands) > 1) { | 622 if (count($commands) > 1) { |
567 $commandList = $this->commands; | 623 $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands; |
568 $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) { | 624 $commands = array_unique(array_filter($commands, function ($nameOrAlias) use ($commandList, $commands, &$aliases) { |
569 $commandName = $commandList[$nameOrAlias]->getName(); | 625 $commandName = $commandList[$nameOrAlias] instanceof Command ? $commandList[$nameOrAlias]->getName() : $nameOrAlias; |
626 $aliases[$nameOrAlias] = $commandName; | |
570 | 627 |
571 return $commandName === $nameOrAlias || !in_array($commandName, $commands); | 628 return $commandName === $nameOrAlias || !in_array($commandName, $commands); |
572 }); | 629 })); |
573 } | 630 } |
574 | 631 |
575 $exact = in_array($name, $commands, true); | 632 $exact = in_array($name, $commands, true) || isset($aliases[$name]); |
576 if (count($commands) > 1 && !$exact) { | 633 if (count($commands) > 1 && !$exact) { |
577 $suggestions = $this->getAbbreviationSuggestions(array_values($commands)); | 634 $usableWidth = $this->terminal->getWidth() - 10; |
578 | 635 $abbrevs = array_values($commands); |
579 throw new CommandNotFoundException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions), array_values($commands)); | 636 $maxLen = 0; |
637 foreach ($abbrevs as $abbrev) { | |
638 $maxLen = max(Helper::strlen($abbrev), $maxLen); | |
639 } | |
640 $abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen) { | |
641 if (!$commandList[$cmd] instanceof Command) { | |
642 return $cmd; | |
643 } | |
644 $abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription(); | |
645 | |
646 return Helper::strlen($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev; | |
647 }, array_values($commands)); | |
648 $suggestions = $this->getAbbreviationSuggestions($abbrevs); | |
649 | |
650 throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s", $name, $suggestions), array_values($commands)); | |
580 } | 651 } |
581 | 652 |
582 return $this->get($exact ? $name : reset($commands)); | 653 return $this->get($exact ? $name : reset($commands)); |
583 } | 654 } |
584 | 655 |
591 * | 662 * |
592 * @return Command[] An array of Command instances | 663 * @return Command[] An array of Command instances |
593 */ | 664 */ |
594 public function all($namespace = null) | 665 public function all($namespace = null) |
595 { | 666 { |
667 $this->init(); | |
668 | |
596 if (null === $namespace) { | 669 if (null === $namespace) { |
597 return $this->commands; | 670 if (!$this->commandLoader) { |
671 return $this->commands; | |
672 } | |
673 | |
674 $commands = $this->commands; | |
675 foreach ($this->commandLoader->getNames() as $name) { | |
676 if (!isset($commands[$name]) && $this->has($name)) { | |
677 $commands[$name] = $this->get($name); | |
678 } | |
679 } | |
680 | |
681 return $commands; | |
598 } | 682 } |
599 | 683 |
600 $commands = array(); | 684 $commands = array(); |
601 foreach ($this->commands as $name => $command) { | 685 foreach ($this->commands as $name => $command) { |
602 if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { | 686 if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { |
603 $commands[$name] = $command; | 687 $commands[$name] = $command; |
688 } | |
689 } | |
690 | |
691 if ($this->commandLoader) { | |
692 foreach ($this->commandLoader->getNames() as $name) { | |
693 if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) { | |
694 $commands[$name] = $this->get($name); | |
695 } | |
604 } | 696 } |
605 } | 697 } |
606 | 698 |
607 return $commands; | 699 return $commands; |
608 } | 700 } |
627 return $abbrevs; | 719 return $abbrevs; |
628 } | 720 } |
629 | 721 |
630 /** | 722 /** |
631 * Renders a caught exception. | 723 * Renders a caught exception. |
632 * | |
633 * @param \Exception $e An exception instance | |
634 * @param OutputInterface $output An OutputInterface instance | |
635 */ | 724 */ |
636 public function renderException(\Exception $e, OutputInterface $output) | 725 public function renderException(\Exception $e, OutputInterface $output) |
637 { | 726 { |
638 $output->writeln('', OutputInterface::VERBOSITY_QUIET); | 727 $output->writeln('', OutputInterface::VERBOSITY_QUIET); |
639 | 728 |
729 $this->doRenderException($e, $output); | |
730 | |
731 if (null !== $this->runningCommand) { | |
732 $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET); | |
733 $output->writeln('', OutputInterface::VERBOSITY_QUIET); | |
734 } | |
735 } | |
736 | |
737 protected function doRenderException(\Exception $e, OutputInterface $output) | |
738 { | |
640 do { | 739 do { |
641 $title = sprintf( | 740 $message = trim($e->getMessage()); |
642 ' [%s%s] ', | 741 if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { |
643 get_class($e), | 742 $title = sprintf(' [%s%s] ', get_class($e), 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : ''); |
644 $output->isVerbose() && 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '' | 743 $len = Helper::strlen($title); |
645 ); | 744 } else { |
646 | 745 $len = 0; |
647 $len = Helper::strlen($title); | 746 } |
648 | 747 |
649 $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : PHP_INT_MAX; | 748 $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : PHP_INT_MAX; |
650 // 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 | 749 // 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 |
651 if (defined('HHVM_VERSION') && $width > 1 << 31) { | 750 if (defined('HHVM_VERSION') && $width > 1 << 31) { |
652 $width = 1 << 31; | 751 $width = 1 << 31; |
653 } | 752 } |
654 $lines = array(); | 753 $lines = array(); |
655 foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) { | 754 foreach ('' !== $message ? preg_split('/\r?\n/', $message) : array() as $line) { |
656 foreach ($this->splitStringByWidth($line, $width - 4) as $line) { | 755 foreach ($this->splitStringByWidth($line, $width - 4) as $line) { |
657 // pre-format lines to get the right string length | 756 // pre-format lines to get the right string length |
658 $lineLength = Helper::strlen($line) + 4; | 757 $lineLength = Helper::strlen($line) + 4; |
659 $lines[] = array($line, $lineLength); | 758 $lines[] = array($line, $lineLength); |
660 | 759 |
661 $len = max($lineLength, $len); | 760 $len = max($lineLength, $len); |
662 } | 761 } |
663 } | 762 } |
664 | 763 |
665 $messages = array(); | 764 $messages = array(); |
765 if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { | |
766 $messages[] = sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))); | |
767 } | |
666 $messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len)); | 768 $messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len)); |
667 $messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::strlen($title)))); | 769 if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { |
770 $messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::strlen($title)))); | |
771 } | |
668 foreach ($lines as $line) { | 772 foreach ($lines as $line) { |
669 $messages[] = sprintf('<error> %s %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1])); | 773 $messages[] = sprintf('<error> %s %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1])); |
670 } | 774 } |
671 $messages[] = $emptyLine; | 775 $messages[] = $emptyLine; |
672 $messages[] = ''; | 776 $messages[] = ''; |
676 if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { | 780 if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { |
677 $output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET); | 781 $output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET); |
678 | 782 |
679 // exception related properties | 783 // exception related properties |
680 $trace = $e->getTrace(); | 784 $trace = $e->getTrace(); |
681 array_unshift($trace, array( | |
682 'function' => '', | |
683 'file' => $e->getFile() !== null ? $e->getFile() : 'n/a', | |
684 'line' => $e->getLine() !== null ? $e->getLine() : 'n/a', | |
685 'args' => array(), | |
686 )); | |
687 | 785 |
688 for ($i = 0, $count = count($trace); $i < $count; ++$i) { | 786 for ($i = 0, $count = count($trace); $i < $count; ++$i) { |
689 $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; | 787 $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; |
690 $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; | 788 $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; |
691 $function = $trace[$i]['function']; | 789 $function = $trace[$i]['function']; |
696 } | 794 } |
697 | 795 |
698 $output->writeln('', OutputInterface::VERBOSITY_QUIET); | 796 $output->writeln('', OutputInterface::VERBOSITY_QUIET); |
699 } | 797 } |
700 } while ($e = $e->getPrevious()); | 798 } while ($e = $e->getPrevious()); |
701 | |
702 if (null !== $this->runningCommand) { | |
703 $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET); | |
704 $output->writeln('', OutputInterface::VERBOSITY_QUIET); | |
705 } | |
706 } | 799 } |
707 | 800 |
708 /** | 801 /** |
709 * Tries to figure out the terminal width in which this application runs. | 802 * Tries to figure out the terminal width in which this application runs. |
710 * | 803 * |
769 return $this; | 862 return $this; |
770 } | 863 } |
771 | 864 |
772 /** | 865 /** |
773 * Configures the input and output instances based on the user arguments and options. | 866 * Configures the input and output instances based on the user arguments and options. |
774 * | |
775 * @param InputInterface $input An InputInterface instance | |
776 * @param OutputInterface $output An OutputInterface instance | |
777 */ | 867 */ |
778 protected function configureIO(InputInterface $input, OutputInterface $output) | 868 protected function configureIO(InputInterface $input, OutputInterface $output) |
779 { | 869 { |
780 if (true === $input->hasParameterOption(array('--ansi'), true)) { | 870 if (true === $input->hasParameterOption(array('--ansi'), true)) { |
781 $output->setDecorated(true); | 871 $output->setDecorated(true); |
801 if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { | 891 if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) { |
802 $input->setInteractive(false); | 892 $input->setInteractive(false); |
803 } | 893 } |
804 } | 894 } |
805 | 895 |
896 switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) { | |
897 case -1: $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); break; | |
898 case 1: $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); break; | |
899 case 2: $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); break; | |
900 case 3: $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); break; | |
901 default: $shellVerbosity = 0; break; | |
902 } | |
903 | |
806 if (true === $input->hasParameterOption(array('--quiet', '-q'), true)) { | 904 if (true === $input->hasParameterOption(array('--quiet', '-q'), true)) { |
807 $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); | 905 $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); |
808 $input->setInteractive(false); | 906 $shellVerbosity = -1; |
809 } else { | 907 } else { |
810 if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || $input->getParameterOption('--verbose', false, true) === 3) { | 908 if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) { |
811 $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); | 909 $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); |
812 } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || $input->getParameterOption('--verbose', false, true) === 2) { | 910 $shellVerbosity = 3; |
911 } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) { | |
813 $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); | 912 $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); |
913 $shellVerbosity = 2; | |
814 } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) { | 914 } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) { |
815 $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); | 915 $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); |
816 } | 916 $shellVerbosity = 1; |
817 } | 917 } |
918 } | |
919 | |
920 if (-1 === $shellVerbosity) { | |
921 $input->setInteractive(false); | |
922 } | |
923 | |
924 putenv('SHELL_VERBOSITY='.$shellVerbosity); | |
925 $_ENV['SHELL_VERBOSITY'] = $shellVerbosity; | |
926 $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity; | |
818 } | 927 } |
819 | 928 |
820 /** | 929 /** |
821 * Runs the current command. | 930 * Runs the current command. |
822 * | 931 * |
823 * If an event dispatcher has been attached to the application, | 932 * If an event dispatcher has been attached to the application, |
824 * events are also dispatched during the life-cycle of the command. | 933 * events are also dispatched during the life-cycle of the command. |
825 * | |
826 * @param Command $command A Command instance | |
827 * @param InputInterface $input An Input instance | |
828 * @param OutputInterface $output An Output instance | |
829 * | 934 * |
830 * @return int 0 if everything went fine, or an error code | 935 * @return int 0 if everything went fine, or an error code |
831 */ | 936 */ |
832 protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) | 937 protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) |
833 { | 938 { |
862 } | 967 } |
863 } catch (\Exception $e) { | 968 } catch (\Exception $e) { |
864 } catch (\Throwable $e) { | 969 } catch (\Throwable $e) { |
865 } | 970 } |
866 if (null !== $e) { | 971 if (null !== $e) { |
867 $x = $e instanceof \Exception ? $e : new FatalThrowableError($e); | 972 if ($this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) { |
868 $event = new ConsoleExceptionEvent($command, $input, $output, $x, $x->getCode()); | 973 $x = $e instanceof \Exception ? $e : new FatalThrowableError($e); |
869 $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); | 974 $event = new ConsoleExceptionEvent($command, $input, $output, $x, $x->getCode()); |
870 | 975 $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event); |
871 if ($x !== $event->getException()) { | 976 |
872 $e = $event->getException(); | 977 if ($x !== $event->getException()) { |
873 } | 978 $e = $event->getException(); |
874 $exitCode = $e->getCode(); | 979 } |
980 } | |
981 $event = new ConsoleErrorEvent($input, $output, $e, $command); | |
982 $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event); | |
983 $e = $event->getError(); | |
984 | |
985 if (0 === $exitCode = $event->getExitCode()) { | |
986 $e = null; | |
987 } | |
875 } | 988 } |
876 | 989 |
877 $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); | 990 $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); |
878 $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); | 991 $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); |
879 | 992 |
884 return $event->getExitCode(); | 997 return $event->getExitCode(); |
885 } | 998 } |
886 | 999 |
887 /** | 1000 /** |
888 * Gets the name of the command based on input. | 1001 * Gets the name of the command based on input. |
889 * | |
890 * @param InputInterface $input The input interface | |
891 * | 1002 * |
892 * @return string The command name | 1003 * @return string The command name |
893 */ | 1004 */ |
894 protected function getCommandName(InputInterface $input) | 1005 protected function getCommandName(InputInterface $input) |
895 { | 1006 { |
948 * | 1059 * |
949 * @return string A formatted string of abbreviated suggestions | 1060 * @return string A formatted string of abbreviated suggestions |
950 */ | 1061 */ |
951 private function getAbbreviationSuggestions($abbrevs) | 1062 private function getAbbreviationSuggestions($abbrevs) |
952 { | 1063 { |
953 return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); | 1064 return ' '.implode("\n ", $abbrevs); |
954 } | 1065 } |
955 | 1066 |
956 /** | 1067 /** |
957 * Returns the namespace part of the command name. | 1068 * Returns the namespace part of the command name. |
958 * | 1069 * |
973 | 1084 |
974 /** | 1085 /** |
975 * Finds alternative of $name among $collection, | 1086 * Finds alternative of $name among $collection, |
976 * if nothing is found in $collection, try in $abbrevs. | 1087 * if nothing is found in $collection, try in $abbrevs. |
977 * | 1088 * |
978 * @param string $name The string | 1089 * @param string $name The string |
979 * @param array|\Traversable $collection The collection | 1090 * @param iterable $collection The collection |
980 * | 1091 * |
981 * @return string[] A sorted array of similar string | 1092 * @return string[] A sorted array of similar string |
982 */ | 1093 */ |
983 private function findAlternatives($name, $collection) | 1094 private function findAlternatives($name, $collection) |
984 { | 1095 { |
1015 $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; | 1126 $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; |
1016 } | 1127 } |
1017 } | 1128 } |
1018 | 1129 |
1019 $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); | 1130 $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); |
1020 asort($alternatives); | 1131 ksort($alternatives, SORT_NATURAL | SORT_FLAG_CASE); |
1021 | 1132 |
1022 return array_keys($alternatives); | 1133 return array_keys($alternatives); |
1023 } | 1134 } |
1024 | 1135 |
1025 /** | 1136 /** |
1064 } | 1175 } |
1065 // if not, push current line to array and make new line | 1176 // if not, push current line to array and make new line |
1066 $lines[] = str_pad($line, $width); | 1177 $lines[] = str_pad($line, $width); |
1067 $line = $char; | 1178 $line = $char; |
1068 } | 1179 } |
1069 if ('' !== $line) { | 1180 |
1070 $lines[] = count($lines) ? str_pad($line, $width) : $line; | 1181 $lines[] = count($lines) ? str_pad($line, $width) : $line; |
1071 } | |
1072 | 1182 |
1073 mb_convert_variables($encoding, 'utf8', $lines); | 1183 mb_convert_variables($encoding, 'utf8', $lines); |
1074 | 1184 |
1075 return $lines; | 1185 return $lines; |
1076 } | 1186 } |
1096 } | 1206 } |
1097 } | 1207 } |
1098 | 1208 |
1099 return $namespaces; | 1209 return $namespaces; |
1100 } | 1210 } |
1211 | |
1212 private function init() | |
1213 { | |
1214 if ($this->initialized) { | |
1215 return; | |
1216 } | |
1217 $this->initialized = true; | |
1218 | |
1219 foreach ($this->getDefaultCommands() as $command) { | |
1220 $this->add($command); | |
1221 } | |
1222 } | |
1101 } | 1223 } |