Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Application.php

Zuletzt modifiziert: 02.04.2025, 15:02 - Dateigröße: 43.30 KiB


0001  <?php
0002   
0003  /*
0004   * This file is part of the Symfony package.
0005   *
0006   * (c) Fabien Potencier <fabien@symfony.com>
0007   *
0008   * For the full copyright and license information, please view the LICENSE
0009   * file that was distributed with this source code.
0010   */
0011   
0012  namespace Symfony\Component\Console;
0013   
0014  use Symfony\Component\Console\Command\Command;
0015  use Symfony\Component\Console\Command\HelpCommand;
0016  use Symfony\Component\Console\Command\ListCommand;
0017  use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
0018  use Symfony\Component\Console\Event\ConsoleCommandEvent;
0019  use Symfony\Component\Console\Event\ConsoleErrorEvent;
0020  use Symfony\Component\Console\Event\ConsoleExceptionEvent;
0021  use Symfony\Component\Console\Event\ConsoleTerminateEvent;
0022  use Symfony\Component\Console\Exception\CommandNotFoundException;
0023  use Symfony\Component\Console\Exception\ExceptionInterface;
0024  use Symfony\Component\Console\Exception\LogicException;
0025  use Symfony\Component\Console\Formatter\OutputFormatter;
0026  use Symfony\Component\Console\Helper\DebugFormatterHelper;
0027  use Symfony\Component\Console\Helper\FormatterHelper;
0028  use Symfony\Component\Console\Helper\Helper;
0029  use Symfony\Component\Console\Helper\HelperSet;
0030  use Symfony\Component\Console\Helper\ProcessHelper;
0031  use Symfony\Component\Console\Helper\QuestionHelper;
0032  use Symfony\Component\Console\Input\ArgvInput;
0033  use Symfony\Component\Console\Input\ArrayInput;
0034  use Symfony\Component\Console\Input\InputArgument;
0035  use Symfony\Component\Console\Input\InputAwareInterface;
0036  use Symfony\Component\Console\Input\InputDefinition;
0037  use Symfony\Component\Console\Input\InputInterface;
0038  use Symfony\Component\Console\Input\InputOption;
0039  use Symfony\Component\Console\Input\StreamableInputInterface;
0040  use Symfony\Component\Console\Output\ConsoleOutput;
0041  use Symfony\Component\Console\Output\ConsoleOutputInterface;
0042  use Symfony\Component\Console\Output\OutputInterface;
0043  use Symfony\Component\Debug\ErrorHandler;
0044  use Symfony\Component\Debug\Exception\FatalThrowableError;
0045  use Symfony\Component\EventDispatcher\EventDispatcherInterface;
0046   
0047  /**
0048   * An Application is the container for a collection of commands.
0049   *
0050   * It is the main entry point of a Console application.
0051   *
0052   * This class is optimized for a standard CLI environment.
0053   *
0054   * Usage:
0055   *
0056   *     $app = new Application('myapp', '1.0 (stable)');
0057   *     $app->add(new SimpleCommand());
0058   *     $app->run();
0059   *
0060   * @author Fabien Potencier <fabien@symfony.com>
0061   */
0062  class Application
0063  {
0064      private $commands = [];
0065      private $wantHelps = false;
0066      private $runningCommand;
0067      private $name;
0068      private $version;
0069      private $commandLoader;
0070      private $catchExceptions = true;
0071      private $autoExit = true;
0072      private $definition;
0073      private $helperSet;
0074      private $dispatcher;
0075      private $terminal;
0076      private $defaultCommand;
0077      private $singleCommand = false;
0078      private $initialized;
0079   
0080      /**
0081       * @param string $name    The name of the application
0082       * @param string $version The version of the application
0083       */
0084      public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
0085      {
0086          $this->name = $name;
0087          $this->version = $version;
0088          $this->terminal = new Terminal();
0089          $this->defaultCommand = 'list';
0090      }
0091   
0092      public function setDispatcher(EventDispatcherInterface $dispatcher)
0093      {
0094          $this->dispatcher = $dispatcher;
0095      }
0096   
0097      public function setCommandLoader(CommandLoaderInterface $commandLoader)
0098      {
0099          $this->commandLoader = $commandLoader;
0100      }
0101   
0102      /**
0103       * Runs the current application.
0104       *
0105       * @return int 0 if everything went fine, or an error code
0106       *
0107       * @throws \Exception When running fails. Bypass this when {@link setCatchExceptions()}.
0108       */
0109      public function run(InputInterface $input = null, OutputInterface $output = null)
0110      {
0111          putenv('LINES='.$this->terminal->getHeight());
0112          putenv('COLUMNS='.$this->terminal->getWidth());
0113   
0114          if (null === $input) {
0115              $input = new ArgvInput();
0116          }
0117   
0118          if (null === $output) {
0119              $output = new ConsoleOutput();
0120          }
0121   
0122          $renderException = function ($e) use ($output) {
0123              if (!$e instanceof \Exception) {
0124                  $e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
0125              }
0126              if ($output instanceof ConsoleOutputInterface) {
0127                  $this->renderException($e, $output->getErrorOutput());
0128              } else {
0129                  $this->renderException($e, $output);
0130              }
0131          };
0132          if ($phpHandler = set_exception_handler($renderException)) {
0133              restore_exception_handler();
0134              if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) {
0135                  $debugHandler = true;
0136              } elseif ($debugHandler = $phpHandler[0]->setExceptionHandler($renderException)) {
0137                  $phpHandler[0]->setExceptionHandler($debugHandler);
0138              }
0139          }
0140   
0141          if (null !== $this->dispatcher && $this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) {
0142              @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);
0143          }
0144   
0145          $this->configureIO($input, $output);
0146   
0147          try {
0148              $exitCode = $this->doRun($input, $output);
0149          } catch (\Exception $e) {
0150              if (!$this->catchExceptions) {
0151                  throw $e;
0152              }
0153   
0154              $renderException($e);
0155   
0156              $exitCode = $e->getCode();
0157              if (is_numeric($exitCode)) {
0158                  $exitCode = (int) $exitCode;
0159                  if (0 === $exitCode) {
0160                      $exitCode = 1;
0161                  }
0162              } else {
0163                  $exitCode = 1;
0164              }
0165          } finally {
0166              // if the exception handler changed, keep it
0167              // otherwise, unregister $renderException
0168              if (!$phpHandler) {
0169                  if (set_exception_handler($renderException) === $renderException) {
0170                      restore_exception_handler();
0171                  }
0172                  restore_exception_handler();
0173              } elseif (!$debugHandler) {
0174                  $finalHandler = $phpHandler[0]->setExceptionHandler(null);
0175                  if ($finalHandler !== $renderException) {
0176                      $phpHandler[0]->setExceptionHandler($finalHandler);
0177                  }
0178              }
0179          }
0180   
0181          if ($this->autoExit) {
0182              if ($exitCode > 255) {
0183                  $exitCode = 255;
0184              }
0185   
0186              exit($exitCode);
0187          }
0188   
0189          return $exitCode;
0190      }
0191   
0192      /**
0193       * Runs the current application.
0194       *
0195       * @return int 0 if everything went fine, or an error code
0196       */
0197      public function doRun(InputInterface $input, OutputInterface $output)
0198      {
0199          if (true === $input->hasParameterOption(['--version', '-V'], true)) {
0200              $output->writeln($this->getLongVersion());
0201   
0202              return 0;
0203          }
0204   
0205          try {
0206              // Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument.
0207              $input->bind($this->getDefinition());
0208          } catch (ExceptionInterface $e) {
0209              // Errors must be ignored, full binding/validation happens later when the command is known.
0210          }
0211   
0212          $name = $this->getCommandName($input);
0213          if (true === $input->hasParameterOption(['--help', '-h'], true)) {
0214              if (!$name) {
0215                  $name = 'help';
0216                  $input = new ArrayInput(['command_name' => $this->defaultCommand]);
0217              } else {
0218                  $this->wantHelps = true;
0219              }
0220          }
0221   
0222          if (!$name) {
0223              $name = $this->defaultCommand;
0224              $definition = $this->getDefinition();
0225              $definition->setArguments(array_merge(
0226                  $definition->getArguments(),
0227                  [
0228                      'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),
0229                  ]
0230              ));
0231          }
0232   
0233          try {
0234              $e = $this->runningCommand = null;
0235              // the command name MUST be the first element of the input
0236              $command = $this->find($name);
0237          } catch (\Exception $e) {
0238          } catch (\Throwable $e) {
0239          }
0240          if (null !== $e) {
0241              if (null !== $this->dispatcher) {
0242                  $event = new ConsoleErrorEvent($input, $output, $e);
0243                  $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event);
0244                  $e = $event->getError();
0245   
0246                  if (0 === $event->getExitCode()) {
0247                      return 0;
0248                  }
0249              }
0250   
0251              throw $e;
0252          }
0253   
0254          $this->runningCommand = $command;
0255          $exitCode = $this->doRunCommand($command, $input, $output);
0256          $this->runningCommand = null;
0257   
0258          return $exitCode;
0259      }
0260   
0261      public function setHelperSet(HelperSet $helperSet)
0262      {
0263          $this->helperSet = $helperSet;
0264      }
0265   
0266      /**
0267       * Get the helper set associated with the command.
0268       *
0269       * @return HelperSet The HelperSet instance associated with this command
0270       */
0271      public function getHelperSet()
0272      {
0273          if (!$this->helperSet) {
0274              $this->helperSet = $this->getDefaultHelperSet();
0275          }
0276   
0277          return $this->helperSet;
0278      }
0279   
0280      public function setDefinition(InputDefinition $definition)
0281      {
0282          $this->definition = $definition;
0283      }
0284   
0285      /**
0286       * Gets the InputDefinition related to this Application.
0287       *
0288       * @return InputDefinition The InputDefinition instance
0289       */
0290      public function getDefinition()
0291      {
0292          if (!$this->definition) {
0293              $this->definition = $this->getDefaultInputDefinition();
0294          }
0295   
0296          if ($this->singleCommand) {
0297              $inputDefinition = $this->definition;
0298              $inputDefinition->setArguments();
0299   
0300              return $inputDefinition;
0301          }
0302   
0303          return $this->definition;
0304      }
0305   
0306      /**
0307       * Gets the help message.
0308       *
0309       * @return string A help message
0310       */
0311      public function getHelp()
0312      {
0313          return $this->getLongVersion();
0314      }
0315   
0316      /**
0317       * Gets whether to catch exceptions or not during commands execution.
0318       *
0319       * @return bool Whether to catch exceptions or not during commands execution
0320       */
0321      public function areExceptionsCaught()
0322      {
0323          return $this->catchExceptions;
0324      }
0325   
0326      /**
0327       * Sets whether to catch exceptions or not during commands execution.
0328       *
0329       * @param bool $boolean Whether to catch exceptions or not during commands execution
0330       */
0331      public function setCatchExceptions($boolean)
0332      {
0333          $this->catchExceptions = (bool) $boolean;
0334      }
0335   
0336      /**
0337       * Gets whether to automatically exit after a command execution or not.
0338       *
0339       * @return bool Whether to automatically exit after a command execution or not
0340       */
0341      public function isAutoExitEnabled()
0342      {
0343          return $this->autoExit;
0344      }
0345   
0346      /**
0347       * Sets whether to automatically exit after a command execution or not.
0348       *
0349       * @param bool $boolean Whether to automatically exit after a command execution or not
0350       */
0351      public function setAutoExit($boolean)
0352      {
0353          $this->autoExit = (bool) $boolean;
0354      }
0355   
0356      /**
0357       * Gets the name of the application.
0358       *
0359       * @return string The application name
0360       */
0361      public function getName()
0362      {
0363          return $this->name;
0364      }
0365   
0366      /**
0367       * Sets the application name.
0368       *
0369       * @param string $name The application name
0370       */
0371      public function setName($name)
0372      {
0373          $this->name = $name;
0374      }
0375   
0376      /**
0377       * Gets the application version.
0378       *
0379       * @return string The application version
0380       */
0381      public function getVersion()
0382      {
0383          return $this->version;
0384      }
0385   
0386      /**
0387       * Sets the application version.
0388       *
0389       * @param string $version The application version
0390       */
0391      public function setVersion($version)
0392      {
0393          $this->version = $version;
0394      }
0395   
0396      /**
0397       * Returns the long version of the application.
0398       *
0399       * @return string The long application version
0400       */
0401      public function getLongVersion()
0402      {
0403          if ('UNKNOWN' !== $this->getName()) {
0404              if ('UNKNOWN' !== $this->getVersion()) {
0405                  return sprintf('%s <info>%s</info>', $this->getName(), $this->getVersion());
0406              }
0407   
0408              return $this->getName();
0409          }
0410   
0411          return 'Console Tool';
0412      }
0413   
0414      /**
0415       * Registers a new command.
0416       *
0417       * @param string $name The command name
0418       *
0419       * @return Command The newly created command
0420       */
0421      public function register($name)
0422      {
0423          return $this->add(new Command($name));
0424      }
0425   
0426      /**
0427       * Adds an array of command objects.
0428       *
0429       * If a Command is not enabled it will not be added.
0430       *
0431       * @param Command[] $commands An array of commands
0432       */
0433      public function addCommands(array $commands)
0434      {
0435          foreach ($commands as $command) {
0436              $this->add($command);
0437          }
0438      }
0439   
0440      /**
0441       * Adds a command object.
0442       *
0443       * If a command with the same name already exists, it will be overridden.
0444       * If the command is not enabled it will not be added.
0445       *
0446       * @return Command|null The registered command if enabled or null
0447       */
0448      public function add(Command $command)
0449      {
0450          $this->init();
0451   
0452          $command->setApplication($this);
0453   
0454          if (!$command->isEnabled()) {
0455              $command->setApplication(null);
0456   
0457              return null;
0458          }
0459   
0460          // Will throw if the command is not correctly initialized.
0461          $command->getDefinition();
0462   
0463          if (!$command->getName()) {
0464              throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', \get_class($command)));
0465          }
0466   
0467          $this->commands[$command->getName()] = $command;
0468   
0469          foreach ($command->getAliases() as $alias) {
0470              $this->commands[$alias] = $command;
0471          }
0472   
0473          return $command;
0474      }
0475   
0476      /**
0477       * Returns a registered command by name or alias.
0478       *
0479       * @param string $name The command name or alias
0480       *
0481       * @return Command A Command object
0482       *
0483       * @throws CommandNotFoundException When given command name does not exist
0484       */
0485      public function get($name)
0486      {
0487          $this->init();
0488   
0489          if (!$this->has($name)) {
0490              throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
0491          }
0492   
0493          // When the command has a different name than the one used at the command loader level
0494          if (!isset($this->commands[$name])) {
0495              throw new CommandNotFoundException(sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name));
0496          }
0497   
0498          $command = $this->commands[$name];
0499   
0500          if ($this->wantHelps) {
0501              $this->wantHelps = false;
0502   
0503              $helpCommand = $this->get('help');
0504              $helpCommand->setCommand($command);
0505   
0506              return $helpCommand;
0507          }
0508   
0509          return $command;
0510      }
0511   
0512      /**
0513       * Returns true if the command exists, false otherwise.
0514       *
0515       * @param string $name The command name or alias
0516       *
0517       * @return bool true if the command exists, false otherwise
0518       */
0519      public function has($name)
0520      {
0521          $this->init();
0522   
0523          return isset($this->commands[$name]) || ($this->commandLoader && $this->commandLoader->has($name) && $this->add($this->commandLoader->get($name)));
0524      }
0525   
0526      /**
0527       * Returns an array of all unique namespaces used by currently registered commands.
0528       *
0529       * It does not return the global namespace which always exists.
0530       *
0531       * @return string[] An array of namespaces
0532       */
0533      public function getNamespaces()
0534      {
0535          $namespaces = [];
0536          foreach ($this->all() as $command) {
0537              if ($command->isHidden()) {
0538                  continue;
0539              }
0540   
0541              $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
0542   
0543              foreach ($command->getAliases() as $alias) {
0544                  $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
0545              }
0546          }
0547   
0548          return array_values(array_unique(array_filter($namespaces)));
0549      }
0550   
0551      /**
0552       * Finds a registered namespace by a name or an abbreviation.
0553       *
0554       * @param string $namespace A namespace or abbreviation to search for
0555       *
0556       * @return string A registered namespace
0557       *
0558       * @throws CommandNotFoundException When namespace is incorrect or ambiguous
0559       */
0560      public function findNamespace($namespace)
0561      {
0562          $allNamespaces = $this->getNamespaces();
0563          $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace);
0564          $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);
0565   
0566          if (empty($namespaces)) {
0567              $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
0568   
0569              if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
0570                  if (1 == \count($alternatives)) {
0571                      $message .= "\n\nDid you mean this?\n    ";
0572                  } else {
0573                      $message .= "\n\nDid you mean one of these?\n    ";
0574                  }
0575   
0576                  $message .= implode("\n    ", $alternatives);
0577              }
0578   
0579              throw new CommandNotFoundException($message, $alternatives);
0580          }
0581   
0582          $exact = \in_array($namespace, $namespaces, true);
0583          if (\count($namespaces) > 1 && !$exact) {
0584              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));
0585          }
0586   
0587          return $exact ? $namespace : reset($namespaces);
0588      }
0589   
0590      /**
0591       * Finds a command by name or alias.
0592       *
0593       * Contrary to get, this command tries to find the best
0594       * match if you give it an abbreviation of a name or alias.
0595       *
0596       * @param string $name A command name or a command alias
0597       *
0598       * @return Command A Command instance
0599       *
0600       * @throws CommandNotFoundException When command name is incorrect or ambiguous
0601       */
0602      public function find($name)
0603      {
0604          $this->init();
0605   
0606          $aliases = [];
0607   
0608          foreach ($this->commands as $command) {
0609              foreach ($command->getAliases() as $alias) {
0610                  if (!$this->has($alias)) {
0611                      $this->commands[$alias] = $command;
0612                  }
0613              }
0614          }
0615   
0616          if ($this->has($name)) {
0617              return $this->get($name);
0618          }
0619   
0620          $allCommands = $this->commandLoader ? array_merge($this->commandLoader->getNames(), array_keys($this->commands)) : array_keys($this->commands);
0621          $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
0622          $commands = preg_grep('{^'.$expr.'}', $allCommands);
0623   
0624          if (empty($commands)) {
0625              $commands = preg_grep('{^'.$expr.'}i', $allCommands);
0626          }
0627   
0628          // if no commands matched or we just matched namespaces
0629          if (empty($commands) || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) {
0630              if (false !== $pos = strrpos($name, ':')) {
0631                  // check if a namespace exists and contains commands
0632                  $this->findNamespace(substr($name, 0, $pos));
0633              }
0634   
0635              $message = sprintf('Command "%s" is not defined.', $name);
0636   
0637              if ($alternatives = $this->findAlternatives($name, $allCommands)) {
0638                  // remove hidden commands
0639                  $alternatives = array_filter($alternatives, function ($name) {
0640                      return !$this->get($name)->isHidden();
0641                  });
0642   
0643                  if (1 == \count($alternatives)) {
0644                      $message .= "\n\nDid you mean this?\n    ";
0645                  } else {
0646                      $message .= "\n\nDid you mean one of these?\n    ";
0647                  }
0648                  $message .= implode("\n    ", $alternatives);
0649              }
0650   
0651              throw new CommandNotFoundException($message, array_values($alternatives));
0652          }
0653   
0654          // filter out aliases for commands which are already on the list
0655          if (\count($commands) > 1) {
0656              $commandList = $this->commandLoader ? array_merge(array_flip($this->commandLoader->getNames()), $this->commands) : $this->commands;
0657              $commands = array_unique(array_filter($commands, function ($nameOrAlias) use (&$commandList, $commands, &$aliases) {
0658                  if (!$commandList[$nameOrAlias] instanceof Command) {
0659                      $commandList[$nameOrAlias] = $this->commandLoader->get($nameOrAlias);
0660                  }
0661   
0662                  $commandName = $commandList[$nameOrAlias]->getName();
0663   
0664                  $aliases[$nameOrAlias] = $commandName;
0665   
0666                  return $commandName === $nameOrAlias || !\in_array($commandName, $commands);
0667              }));
0668          }
0669   
0670          $exact = \in_array($name, $commands, true) || isset($aliases[$name]);
0671          if (\count($commands) > 1 && !$exact) {
0672              $usableWidth = $this->terminal->getWidth() - 10;
0673              $abbrevs = array_values($commands);
0674              $maxLen = 0;
0675              foreach ($abbrevs as $abbrev) {
0676                  $maxLen = max(Helper::strlen($abbrev), $maxLen);
0677              }
0678              $abbrevs = array_map(function ($cmd) use ($commandList, $usableWidth, $maxLen) {
0679                  if ($commandList[$cmd]->isHidden()) {
0680                      return false;
0681                  }
0682   
0683                  $abbrev = str_pad($cmd, $maxLen, ' ').' '.$commandList[$cmd]->getDescription();
0684   
0685                  return Helper::strlen($abbrev) > $usableWidth ? Helper::substr($abbrev, 0, $usableWidth - 3).'...' : $abbrev;
0686              }, array_values($commands));
0687              $suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs));
0688   
0689              throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $name, $suggestions), array_values($commands));
0690          }
0691   
0692          return $this->get($exact ? $name : reset($commands));
0693      }
0694   
0695      /**
0696       * Gets the commands (registered in the given namespace if provided).
0697       *
0698       * The array keys are the full names and the values the command instances.
0699       *
0700       * @param string $namespace A namespace name
0701       *
0702       * @return Command[] An array of Command instances
0703       */
0704      public function all($namespace = null)
0705      {
0706          $this->init();
0707   
0708          if (null === $namespace) {
0709              if (!$this->commandLoader) {
0710                  return $this->commands;
0711              }
0712   
0713              $commands = $this->commands;
0714              foreach ($this->commandLoader->getNames() as $name) {
0715                  if (!isset($commands[$name]) && $this->has($name)) {
0716                      $commands[$name] = $this->get($name);
0717                  }
0718              }
0719   
0720              return $commands;
0721          }
0722   
0723          $commands = [];
0724          foreach ($this->commands as $name => $command) {
0725              if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
0726                  $commands[$name] = $command;
0727              }
0728          }
0729   
0730          if ($this->commandLoader) {
0731              foreach ($this->commandLoader->getNames() as $name) {
0732                  if (!isset($commands[$name]) && $namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1) && $this->has($name)) {
0733                      $commands[$name] = $this->get($name);
0734                  }
0735              }
0736          }
0737   
0738          return $commands;
0739      }
0740   
0741      /**
0742       * Returns an array of possible abbreviations given a set of names.
0743       *
0744       * @param array $names An array of names
0745       *
0746       * @return array An array of abbreviations
0747       */
0748      public static function getAbbreviations($names)
0749      {
0750          $abbrevs = [];
0751          foreach ($names as $name) {
0752              for ($len = \strlen($name); $len > 0; --$len) {
0753                  $abbrev = substr($name, 0, $len);
0754                  $abbrevs[$abbrev][] = $name;
0755              }
0756          }
0757   
0758          return $abbrevs;
0759      }
0760   
0761      /**
0762       * Renders a caught exception.
0763       */
0764      public function renderException(\Exception $e, OutputInterface $output)
0765      {
0766          $output->writeln('', OutputInterface::VERBOSITY_QUIET);
0767   
0768          $this->doRenderException($e, $output);
0769   
0770          if (null !== $this->runningCommand) {
0771              $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
0772              $output->writeln('', OutputInterface::VERBOSITY_QUIET);
0773          }
0774      }
0775   
0776      protected function doRenderException(\Exception $e, OutputInterface $output)
0777      {
0778          do {
0779              $message = trim($e->getMessage());
0780              if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
0781                  $title = sprintf('  [%s%s]  ', \get_class($e), 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
0782                  $len = Helper::strlen($title);
0783              } else {
0784                  $len = 0;
0785              }
0786   
0787              $width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : \PHP_INT_MAX;
0788              // 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
0789              if (\defined('HHVM_VERSION') && $width > 1 << 31) {
0790                  $width = 1 << 31;
0791              }
0792              $lines = [];
0793              foreach ('' !== $message ? preg_split('/\r?\n/', $message) : [] as $line) {
0794                  foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
0795                      // pre-format lines to get the right string length
0796                      $lineLength = Helper::strlen($line) + 4;
0797                      $lines[] = [$line, $lineLength];
0798   
0799                      $len = max($lineLength, $len);
0800                  }
0801              }
0802   
0803              $messages = [];
0804              if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
0805                  $messages[] = sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a')));
0806              }
0807              $messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
0808              if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
0809                  $messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::strlen($title))));
0810              }
0811              foreach ($lines as $line) {
0812                  $messages[] = sprintf('<error>  %s  %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1]));
0813              }
0814              $messages[] = $emptyLine;
0815              $messages[] = '';
0816   
0817              $output->writeln($messages, OutputInterface::VERBOSITY_QUIET);
0818   
0819              if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
0820                  $output->writeln('<comment>Exception trace:</comment>', OutputInterface::VERBOSITY_QUIET);
0821   
0822                  // exception related properties
0823                  $trace = $e->getTrace();
0824   
0825                  array_unshift($trace, [
0826                      'function' => '',
0827                      'file' => $e->getFile() ?: 'n/a',
0828                      'line' => $e->getLine() ?: 'n/a',
0829                      'args' => [],
0830                  ]);
0831   
0832                  for ($i = 0, $count = \count($trace); $i < $count; ++$i) {
0833                      $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
0834                      $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
0835                      $function = isset($trace[$i]['function']) ? $trace[$i]['function'] : '';
0836                      $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
0837                      $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
0838   
0839                      $output->writeln(sprintf(' %s%s at <info>%s:%s</info>', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET);
0840                  }
0841   
0842                  $output->writeln('', OutputInterface::VERBOSITY_QUIET);
0843              }
0844          } while ($e = $e->getPrevious());
0845      }
0846   
0847      /**
0848       * Tries to figure out the terminal width in which this application runs.
0849       *
0850       * @return int|null
0851       *
0852       * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead.
0853       */
0854      protected function getTerminalWidth()
0855      {
0856          @trigger_error(sprintf('The "%s()" method is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), \E_USER_DEPRECATED);
0857   
0858          return $this->terminal->getWidth();
0859      }
0860   
0861      /**
0862       * Tries to figure out the terminal height in which this application runs.
0863       *
0864       * @return int|null
0865       *
0866       * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead.
0867       */
0868      protected function getTerminalHeight()
0869      {
0870          @trigger_error(sprintf('The "%s()" method is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), \E_USER_DEPRECATED);
0871   
0872          return $this->terminal->getHeight();
0873      }
0874   
0875      /**
0876       * Tries to figure out the terminal dimensions based on the current environment.
0877       *
0878       * @return array Array containing width and height
0879       *
0880       * @deprecated since version 3.2, to be removed in 4.0. Create a Terminal instance instead.
0881       */
0882      public function getTerminalDimensions()
0883      {
0884          @trigger_error(sprintf('The "%s()" method is deprecated as of 3.2 and will be removed in 4.0. Create a Terminal instance instead.', __METHOD__), \E_USER_DEPRECATED);
0885   
0886          return [$this->terminal->getWidth(), $this->terminal->getHeight()];
0887      }
0888   
0889      /**
0890       * Sets terminal dimensions.
0891       *
0892       * Can be useful to force terminal dimensions for functional tests.
0893       *
0894       * @param int $width  The width
0895       * @param int $height The height
0896       *
0897       * @return $this
0898       *
0899       * @deprecated since version 3.2, to be removed in 4.0. Set the COLUMNS and LINES env vars instead.
0900       */
0901      public function setTerminalDimensions($width, $height)
0902      {
0903          @trigger_error(sprintf('The "%s()" method is deprecated as of 3.2 and will be removed in 4.0. Set the COLUMNS and LINES env vars instead.', __METHOD__), \E_USER_DEPRECATED);
0904   
0905          putenv('COLUMNS='.$width);
0906          putenv('LINES='.$height);
0907   
0908          return $this;
0909      }
0910   
0911      /**
0912       * Configures the input and output instances based on the user arguments and options.
0913       */
0914      protected function configureIO(InputInterface $input, OutputInterface $output)
0915      {
0916          if (true === $input->hasParameterOption(['--ansi'], true)) {
0917              $output->setDecorated(true);
0918          } elseif (true === $input->hasParameterOption(['--no-ansi'], true)) {
0919              $output->setDecorated(false);
0920          }
0921   
0922          if (true === $input->hasParameterOption(['--no-interaction', '-n'], true)) {
0923              $input->setInteractive(false);
0924          } elseif (\function_exists('posix_isatty')) {
0925              $inputStream = null;
0926   
0927              if ($input instanceof StreamableInputInterface) {
0928                  $inputStream = $input->getStream();
0929              }
0930   
0931              // This check ensures that calling QuestionHelper::setInputStream() works
0932              // To be removed in 4.0 (in the same time as QuestionHelper::setInputStream)
0933              if (!$inputStream && $this->getHelperSet()->has('question')) {
0934                  $inputStream = $this->getHelperSet()->get('question')->getInputStream(false);
0935              }
0936   
0937              if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) {
0938                  $input->setInteractive(false);
0939              }
0940          }
0941   
0942          switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
0943              case -1: $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); break;
0944              case 1: $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); break;
0945              case 2: $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); break;
0946              case 3: $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); break;
0947              default: $shellVerbosity = 0; break;
0948          }
0949   
0950          if (true === $input->hasParameterOption(['--quiet', '-q'], true)) {
0951              $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
0952              $shellVerbosity = -1;
0953          } else {
0954              if ($input->hasParameterOption('-vvv', true) || $input->hasParameterOption('--verbose=3', true) || 3 === $input->getParameterOption('--verbose', false, true)) {
0955                  $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
0956                  $shellVerbosity = 3;
0957              } elseif ($input->hasParameterOption('-vv', true) || $input->hasParameterOption('--verbose=2', true) || 2 === $input->getParameterOption('--verbose', false, true)) {
0958                  $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
0959                  $shellVerbosity = 2;
0960              } elseif ($input->hasParameterOption('-v', true) || $input->hasParameterOption('--verbose=1', true) || $input->hasParameterOption('--verbose', true) || $input->getParameterOption('--verbose', false, true)) {
0961                  $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
0962                  $shellVerbosity = 1;
0963              }
0964          }
0965   
0966          if (-1 === $shellVerbosity) {
0967              $input->setInteractive(false);
0968          }
0969   
0970          putenv('SHELL_VERBOSITY='.$shellVerbosity);
0971          $_ENV['SHELL_VERBOSITY'] = $shellVerbosity;
0972          $_SERVER['SHELL_VERBOSITY'] = $shellVerbosity;
0973      }
0974   
0975      /**
0976       * Runs the current command.
0977       *
0978       * If an event dispatcher has been attached to the application,
0979       * events are also dispatched during the life-cycle of the command.
0980       *
0981       * @return int 0 if everything went fine, or an error code
0982       */
0983      protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
0984      {
0985          foreach ($command->getHelperSet() as $helper) {
0986              if ($helper instanceof InputAwareInterface) {
0987                  $helper->setInput($input);
0988              }
0989          }
0990   
0991          if (null === $this->dispatcher) {
0992              return $command->run($input, $output);
0993          }
0994   
0995          // bind before the console.command event, so the listeners have access to input options/arguments
0996          try {
0997              $command->mergeApplicationDefinition();
0998              $input->bind($command->getDefinition());
0999          } catch (ExceptionInterface $e) {
1000              // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition
1001          }
1002   
1003          $event = new ConsoleCommandEvent($command, $input, $output);
1004          $e = null;
1005   
1006          try {
1007              $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
1008   
1009              if ($event->commandShouldRun()) {
1010                  $exitCode = $command->run($input, $output);
1011              } else {
1012                  $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
1013              }
1014          } catch (\Exception $e) {
1015          } catch (\Throwable $e) {
1016          }
1017          if (null !== $e) {
1018              if ($this->dispatcher->hasListeners(ConsoleEvents::EXCEPTION)) {
1019                  $x = $e instanceof \Exception ? $e : new FatalThrowableError($e);
1020                  $event = new ConsoleExceptionEvent($command, $input, $output, $x, $x->getCode());
1021                  $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
1022   
1023                  if ($x !== $event->getException()) {
1024                      $e = $event->getException();
1025                  }
1026              }
1027              $event = new ConsoleErrorEvent($input, $output, $e, $command);
1028              $this->dispatcher->dispatch(ConsoleEvents::ERROR, $event);
1029              $e = $event->getError();
1030   
1031              if (0 === $exitCode = $event->getExitCode()) {
1032                  $e = null;
1033              }
1034          }
1035   
1036          $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
1037          $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
1038   
1039          if (null !== $e) {
1040              throw $e;
1041          }
1042   
1043          return $event->getExitCode();
1044      }
1045   
1046      /**
1047       * Gets the name of the command based on input.
1048       *
1049       * @return string|null
1050       */
1051      protected function getCommandName(InputInterface $input)
1052      {
1053          return $this->singleCommand ? $this->defaultCommand : $input->getFirstArgument();
1054      }
1055   
1056      /**
1057       * Gets the default input definition.
1058       *
1059       * @return InputDefinition An InputDefinition instance
1060       */
1061      protected function getDefaultInputDefinition()
1062      {
1063          return new InputDefinition([
1064              new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
1065   
1066              new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
1067              new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
1068              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'),
1069              new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
1070              new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
1071              new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
1072              new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
1073          ]);
1074      }
1075   
1076      /**
1077       * Gets the default commands that should always be available.
1078       *
1079       * @return Command[] An array of default Command instances
1080       */
1081      protected function getDefaultCommands()
1082      {
1083          return [new HelpCommand(), new ListCommand()];
1084      }
1085   
1086      /**
1087       * Gets the default helper set with the helpers that should always be available.
1088       *
1089       * @return HelperSet A HelperSet instance
1090       */
1091      protected function getDefaultHelperSet()
1092      {
1093          return new HelperSet([
1094              new FormatterHelper(),
1095              new DebugFormatterHelper(),
1096              new ProcessHelper(),
1097              new QuestionHelper(),
1098          ]);
1099      }
1100   
1101      /**
1102       * Returns abbreviated suggestions in string format.
1103       *
1104       * @param array $abbrevs Abbreviated suggestions to convert
1105       *
1106       * @return string A formatted string of abbreviated suggestions
1107       */
1108      private function getAbbreviationSuggestions($abbrevs)
1109      {
1110          return '    '.implode("\n    ", $abbrevs);
1111      }
1112   
1113      /**
1114       * Returns the namespace part of the command name.
1115       *
1116       * This method is not part of public API and should not be used directly.
1117       *
1118       * @param string $name  The full name of the command
1119       * @param string $limit The maximum number of parts of the namespace
1120       *
1121       * @return string The namespace of the command
1122       */
1123      public function extractNamespace($name, $limit = null)
1124      {
1125          $parts = explode(':', $name, -1);
1126   
1127          return implode(':', null === $limit ? $parts : \array_slice($parts, 0, $limit));
1128      }
1129   
1130      /**
1131       * Finds alternative of $name among $collection,
1132       * if nothing is found in $collection, try in $abbrevs.
1133       *
1134       * @param string   $name       The string
1135       * @param iterable $collection The collection
1136       *
1137       * @return string[] A sorted array of similar string
1138       */
1139      private function findAlternatives($name, $collection)
1140      {
1141          $threshold = 1e3;
1142          $alternatives = [];
1143   
1144          $collectionParts = [];
1145          foreach ($collection as $item) {
1146              $collectionParts[$item] = explode(':', $item);
1147          }
1148   
1149          foreach (explode(':', $name) as $i => $subname) {
1150              foreach ($collectionParts as $collectionName => $parts) {
1151                  $exists = isset($alternatives[$collectionName]);
1152                  if (!isset($parts[$i]) && $exists) {
1153                      $alternatives[$collectionName] += $threshold;
1154                      continue;
1155                  } elseif (!isset($parts[$i])) {
1156                      continue;
1157                  }
1158   
1159                  $lev = levenshtein($subname, $parts[$i]);
1160                  if ($lev <= \strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
1161                      $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
1162                  } elseif ($exists) {
1163                      $alternatives[$collectionName] += $threshold;
1164                  }
1165              }
1166          }
1167   
1168          foreach ($collection as $item) {
1169              $lev = levenshtein($name, $item);
1170              if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) {
1171                  $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
1172              }
1173          }
1174   
1175          $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
1176          ksort($alternatives, \SORT_NATURAL | \SORT_FLAG_CASE);
1177   
1178          return array_keys($alternatives);
1179      }
1180   
1181      /**
1182       * Sets the default Command name.
1183       *
1184       * @param string $commandName     The Command name
1185       * @param bool   $isSingleCommand Set to true if there is only one command in this application
1186       *
1187       * @return self
1188       */
1189      public function setDefaultCommand($commandName, $isSingleCommand = false)
1190      {
1191          $this->defaultCommand = $commandName;
1192   
1193          if ($isSingleCommand) {
1194              // Ensure the command exist
1195              $this->find($commandName);
1196   
1197              $this->singleCommand = true;
1198          }
1199   
1200          return $this;
1201      }
1202   
1203      /**
1204       * @internal
1205       */
1206      public function isSingleCommand()
1207      {
1208          return $this->singleCommand;
1209      }
1210   
1211      private function splitStringByWidth($string, $width)
1212      {
1213          // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
1214          // additionally, array_slice() is not enough as some character has doubled width.
1215          // we need a function to split string not by character count but by string width
1216          if (false === $encoding = mb_detect_encoding($string, null, true)) {
1217              return str_split($string, $width);
1218          }
1219   
1220          $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
1221          $lines = [];
1222          $line = '';
1223          foreach (preg_split('//u', $utf8String) as $char) {
1224              // test if $char could be appended to current line
1225              if (mb_strwidth($line.$char, 'utf8') <= $width) {
1226                  $line .= $char;
1227                  continue;
1228              }
1229              // if not, push current line to array and make new line
1230              $lines[] = str_pad($line, $width);
1231              $line = $char;
1232          }
1233   
1234          $lines[] = \count($lines) ? str_pad($line, $width) : $line;
1235   
1236          mb_convert_variables($encoding, 'utf8', $lines);
1237   
1238          return $lines;
1239      }
1240   
1241      /**
1242       * Returns all namespaces of the command name.
1243       *
1244       * @param string $name The full name of the command
1245       *
1246       * @return string[] The namespaces of the command
1247       */
1248      private function extractAllNamespaces($name)
1249      {
1250          // -1 as third argument is needed to skip the command short name when exploding
1251          $parts = explode(':', $name, -1);
1252          $namespaces = [];
1253   
1254          foreach ($parts as $part) {
1255              if (\count($namespaces)) {
1256                  $namespaces[] = end($namespaces).':'.$part;
1257              } else {
1258                  $namespaces[] = $part;
1259              }
1260          }
1261   
1262          return $namespaces;
1263      }
1264   
1265      private function init()
1266      {
1267          if ($this->initialized) {
1268              return;
1269          }
1270          $this->initialized = true;
1271   
1272          foreach ($this->getDefaultCommands() as $command) {
1273              $this->add($command);
1274          }
1275      }
1276  }
1277