Verzeichnisstruktur phpBB-3.1.0
- Veröffentlicht
- 27.10.2014
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. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Application.php
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\Descriptor\TextDescriptor;
0015 use Symfony\Component\Console\Descriptor\XmlDescriptor;
0016 use Symfony\Component\Console\Input\InputInterface;
0017 use Symfony\Component\Console\Input\ArgvInput;
0018 use Symfony\Component\Console\Input\ArrayInput;
0019 use Symfony\Component\Console\Input\InputDefinition;
0020 use Symfony\Component\Console\Input\InputOption;
0021 use Symfony\Component\Console\Input\InputArgument;
0022 use Symfony\Component\Console\Output\OutputInterface;
0023 use Symfony\Component\Console\Output\ConsoleOutput;
0024 use Symfony\Component\Console\Output\ConsoleOutputInterface;
0025 use Symfony\Component\Console\Command\Command;
0026 use Symfony\Component\Console\Command\HelpCommand;
0027 use Symfony\Component\Console\Command\ListCommand;
0028 use Symfony\Component\Console\Helper\HelperSet;
0029 use Symfony\Component\Console\Helper\FormatterHelper;
0030 use Symfony\Component\Console\Helper\DialogHelper;
0031 use Symfony\Component\Console\Helper\ProgressHelper;
0032 use Symfony\Component\Console\Helper\TableHelper;
0033 use Symfony\Component\Console\Event\ConsoleCommandEvent;
0034 use Symfony\Component\Console\Event\ConsoleExceptionEvent;
0035 use Symfony\Component\Console\Event\ConsoleTerminateEvent;
0036 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
0037
0038 /**
0039 * An Application is the container for a collection of commands.
0040 *
0041 * It is the main entry point of a Console application.
0042 *
0043 * This class is optimized for a standard CLI environment.
0044 *
0045 * Usage:
0046 *
0047 * $app = new Application('myapp', '1.0 (stable)');
0048 * $app->add(new SimpleCommand());
0049 * $app->run();
0050 *
0051 * @author Fabien Potencier <fabien@symfony.com>
0052 *
0053 * @api
0054 */
0055 class Application
0056 {
0057 private $commands;
0058 private $wantHelps = false;
0059 private $runningCommand;
0060 private $name;
0061 private $version;
0062 private $catchExceptions;
0063 private $autoExit;
0064 private $definition;
0065 private $helperSet;
0066 private $dispatcher;
0067
0068 /**
0069 * Constructor.
0070 *
0071 * @param string $name The name of the application
0072 * @param string $version The version of the application
0073 *
0074 * @api
0075 */
0076 public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
0077 {
0078 $this->name = $name;
0079 $this->version = $version;
0080 $this->catchExceptions = true;
0081 $this->autoExit = true;
0082 $this->commands = array();
0083 $this->helperSet = $this->getDefaultHelperSet();
0084 $this->definition = $this->getDefaultInputDefinition();
0085
0086 foreach ($this->getDefaultCommands() as $command) {
0087 $this->add($command);
0088 }
0089 }
0090
0091 public function setDispatcher(EventDispatcherInterface $dispatcher)
0092 {
0093 $this->dispatcher = $dispatcher;
0094 }
0095
0096 /**
0097 * Runs the current application.
0098 *
0099 * @param InputInterface $input An Input instance
0100 * @param OutputInterface $output An Output instance
0101 *
0102 * @return int 0 if everything went fine, or an error code
0103 *
0104 * @throws \Exception When doRun returns Exception
0105 *
0106 * @api
0107 */
0108 public function run(InputInterface $input = null, OutputInterface $output = null)
0109 {
0110 if (null === $input) {
0111 $input = new ArgvInput();
0112 }
0113
0114 if (null === $output) {
0115 $output = new ConsoleOutput();
0116 }
0117
0118 $this->configureIO($input, $output);
0119
0120 try {
0121 $exitCode = $this->doRun($input, $output);
0122 } catch (\Exception $e) {
0123 if (!$this->catchExceptions) {
0124 throw $e;
0125 }
0126
0127 if ($output instanceof ConsoleOutputInterface) {
0128 $this->renderException($e, $output->getErrorOutput());
0129 } else {
0130 $this->renderException($e, $output);
0131 }
0132
0133 $exitCode = $e->getCode();
0134 if (is_numeric($exitCode)) {
0135 $exitCode = (int) $exitCode;
0136 if (0 === $exitCode) {
0137 $exitCode = 1;
0138 }
0139 } else {
0140 $exitCode = 1;
0141 }
0142 }
0143
0144 if ($this->autoExit) {
0145 if ($exitCode > 255) {
0146 $exitCode = 255;
0147 }
0148 // @codeCoverageIgnoreStart
0149 exit($exitCode);
0150 // @codeCoverageIgnoreEnd
0151 }
0152
0153 return $exitCode;
0154 }
0155
0156 /**
0157 * Runs the current application.
0158 *
0159 * @param InputInterface $input An Input instance
0160 * @param OutputInterface $output An Output instance
0161 *
0162 * @return int 0 if everything went fine, or an error code
0163 */
0164 public function doRun(InputInterface $input, OutputInterface $output)
0165 {
0166 if (true === $input->hasParameterOption(array('--version', '-V'))) {
0167 $output->writeln($this->getLongVersion());
0168
0169 return 0;
0170 }
0171
0172 $name = $this->getCommandName($input);
0173 if (true === $input->hasParameterOption(array('--help', '-h'))) {
0174 if (!$name) {
0175 $name = 'help';
0176 $input = new ArrayInput(array('command' => 'help'));
0177 } else {
0178 $this->wantHelps = true;
0179 }
0180 }
0181
0182 if (!$name) {
0183 $name = 'list';
0184 $input = new ArrayInput(array('command' => 'list'));
0185 }
0186
0187 // the command name MUST be the first element of the input
0188 $command = $this->find($name);
0189
0190 $this->runningCommand = $command;
0191 $exitCode = $this->doRunCommand($command, $input, $output);
0192 $this->runningCommand = null;
0193
0194 return $exitCode;
0195 }
0196
0197 /**
0198 * Set a helper set to be used with the command.
0199 *
0200 * @param HelperSet $helperSet The helper set
0201 *
0202 * @api
0203 */
0204 public function setHelperSet(HelperSet $helperSet)
0205 {
0206 $this->helperSet = $helperSet;
0207 }
0208
0209 /**
0210 * Get the helper set associated with the command.
0211 *
0212 * @return HelperSet The HelperSet instance associated with this command
0213 *
0214 * @api
0215 */
0216 public function getHelperSet()
0217 {
0218 return $this->helperSet;
0219 }
0220
0221 /**
0222 * Set an input definition set to be used with this application
0223 *
0224 * @param InputDefinition $definition The input definition
0225 *
0226 * @api
0227 */
0228 public function setDefinition(InputDefinition $definition)
0229 {
0230 $this->definition = $definition;
0231 }
0232
0233 /**
0234 * Gets the InputDefinition related to this Application.
0235 *
0236 * @return InputDefinition The InputDefinition instance
0237 */
0238 public function getDefinition()
0239 {
0240 return $this->definition;
0241 }
0242
0243 /**
0244 * Gets the help message.
0245 *
0246 * @return string A help message.
0247 */
0248 public function getHelp()
0249 {
0250 $messages = array(
0251 $this->getLongVersion(),
0252 '',
0253 '<comment>Usage:</comment>',
0254 ' [options] command [arguments]',
0255 '',
0256 '<comment>Options:</comment>',
0257 );
0258
0259 foreach ($this->getDefinition()->getOptions() as $option) {
0260 $messages[] = sprintf(' %-29s %s %s',
0261 '<info>--'.$option->getName().'</info>',
0262 $option->getShortcut() ? '<info>-'.$option->getShortcut().'</info>' : ' ',
0263 $option->getDescription()
0264 );
0265 }
0266
0267 return implode(PHP_EOL, $messages);
0268 }
0269
0270 /**
0271 * Sets whether to catch exceptions or not during commands execution.
0272 *
0273 * @param bool $boolean Whether to catch exceptions or not during commands execution
0274 *
0275 * @api
0276 */
0277 public function setCatchExceptions($boolean)
0278 {
0279 $this->catchExceptions = (bool) $boolean;
0280 }
0281
0282 /**
0283 * Sets whether to automatically exit after a command execution or not.
0284 *
0285 * @param bool $boolean Whether to automatically exit after a command execution or not
0286 *
0287 * @api
0288 */
0289 public function setAutoExit($boolean)
0290 {
0291 $this->autoExit = (bool) $boolean;
0292 }
0293
0294 /**
0295 * Gets the name of the application.
0296 *
0297 * @return string The application name
0298 *
0299 * @api
0300 */
0301 public function getName()
0302 {
0303 return $this->name;
0304 }
0305
0306 /**
0307 * Sets the application name.
0308 *
0309 * @param string $name The application name
0310 *
0311 * @api
0312 */
0313 public function setName($name)
0314 {
0315 $this->name = $name;
0316 }
0317
0318 /**
0319 * Gets the application version.
0320 *
0321 * @return string The application version
0322 *
0323 * @api
0324 */
0325 public function getVersion()
0326 {
0327 return $this->version;
0328 }
0329
0330 /**
0331 * Sets the application version.
0332 *
0333 * @param string $version The application version
0334 *
0335 * @api
0336 */
0337 public function setVersion($version)
0338 {
0339 $this->version = $version;
0340 }
0341
0342 /**
0343 * Returns the long version of the application.
0344 *
0345 * @return string The long application version
0346 *
0347 * @api
0348 */
0349 public function getLongVersion()
0350 {
0351 if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
0352 return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
0353 }
0354
0355 return '<info>Console Tool</info>';
0356 }
0357
0358 /**
0359 * Registers a new command.
0360 *
0361 * @param string $name The command name
0362 *
0363 * @return Command The newly created command
0364 *
0365 * @api
0366 */
0367 public function register($name)
0368 {
0369 return $this->add(new Command($name));
0370 }
0371
0372 /**
0373 * Adds an array of command objects.
0374 *
0375 * @param Command[] $commands An array of commands
0376 *
0377 * @api
0378 */
0379 public function addCommands(array $commands)
0380 {
0381 foreach ($commands as $command) {
0382 $this->add($command);
0383 }
0384 }
0385
0386 /**
0387 * Adds a command object.
0388 *
0389 * If a command with the same name already exists, it will be overridden.
0390 *
0391 * @param Command $command A Command object
0392 *
0393 * @return Command The registered command
0394 *
0395 * @api
0396 */
0397 public function add(Command $command)
0398 {
0399 $command->setApplication($this);
0400
0401 if (!$command->isEnabled()) {
0402 $command->setApplication(null);
0403
0404 return;
0405 }
0406
0407 $this->commands[$command->getName()] = $command;
0408
0409 foreach ($command->getAliases() as $alias) {
0410 $this->commands[$alias] = $command;
0411 }
0412
0413 return $command;
0414 }
0415
0416 /**
0417 * Returns a registered command by name or alias.
0418 *
0419 * @param string $name The command name or alias
0420 *
0421 * @return Command A Command object
0422 *
0423 * @throws \InvalidArgumentException When command name given does not exist
0424 *
0425 * @api
0426 */
0427 public function get($name)
0428 {
0429 if (!isset($this->commands[$name])) {
0430 throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
0431 }
0432
0433 $command = $this->commands[$name];
0434
0435 if ($this->wantHelps) {
0436 $this->wantHelps = false;
0437
0438 $helpCommand = $this->get('help');
0439 $helpCommand->setCommand($command);
0440
0441 return $helpCommand;
0442 }
0443
0444 return $command;
0445 }
0446
0447 /**
0448 * Returns true if the command exists, false otherwise.
0449 *
0450 * @param string $name The command name or alias
0451 *
0452 * @return bool true if the command exists, false otherwise
0453 *
0454 * @api
0455 */
0456 public function has($name)
0457 {
0458 return isset($this->commands[$name]);
0459 }
0460
0461 /**
0462 * Returns an array of all unique namespaces used by currently registered commands.
0463 *
0464 * It does not returns the global namespace which always exists.
0465 *
0466 * @return array An array of namespaces
0467 */
0468 public function getNamespaces()
0469 {
0470 $namespaces = array();
0471 foreach ($this->commands as $command) {
0472 $namespaces[] = $this->extractNamespace($command->getName());
0473
0474 foreach ($command->getAliases() as $alias) {
0475 $namespaces[] = $this->extractNamespace($alias);
0476 }
0477 }
0478
0479 return array_values(array_unique(array_filter($namespaces)));
0480 }
0481
0482 /**
0483 * Finds a registered namespace by a name or an abbreviation.
0484 *
0485 * @param string $namespace A namespace or abbreviation to search for
0486 *
0487 * @return string A registered namespace
0488 *
0489 * @throws \InvalidArgumentException When namespace is incorrect or ambiguous
0490 */
0491 public function findNamespace($namespace)
0492 {
0493 $allNamespaces = $this->getNamespaces();
0494 $found = '';
0495 foreach (explode(':', $namespace) as $i => $part) {
0496 // select sub-namespaces matching the current namespace we found
0497 $namespaces = array();
0498 foreach ($allNamespaces as $n) {
0499 if ('' === $found || 0 === strpos($n, $found)) {
0500 $namespaces[$n] = explode(':', $n);
0501 }
0502 }
0503
0504 $abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $namespaces)))));
0505
0506 if (!isset($abbrevs[$part])) {
0507 $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
0508
0509 if (1 <= $i) {
0510 $part = $found.':'.$part;
0511 }
0512
0513 if ($alternatives = $this->findAlternativeNamespace($part, $abbrevs)) {
0514 if (1 == count($alternatives)) {
0515 $message .= "\n\nDid you mean this?\n ";
0516 } else {
0517 $message .= "\n\nDid you mean one of these?\n ";
0518 }
0519
0520 $message .= implode("\n ", $alternatives);
0521 }
0522
0523 throw new \InvalidArgumentException($message);
0524 }
0525
0526 // there are multiple matches, but $part is an exact match of one of them so we select it
0527 if (in_array($part, $abbrevs[$part])) {
0528 $abbrevs[$part] = array($part);
0529 }
0530
0531 if (count($abbrevs[$part]) > 1) {
0532 throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$part])));
0533 }
0534
0535 $found .= $found ? ':'.$abbrevs[$part][0] : $abbrevs[$part][0];
0536 }
0537
0538 return $found;
0539 }
0540
0541 /**
0542 * Finds a command by name or alias.
0543 *
0544 * Contrary to get, this command tries to find the best
0545 * match if you give it an abbreviation of a name or alias.
0546 *
0547 * @param string $name A command name or a command alias
0548 *
0549 * @return Command A Command instance
0550 *
0551 * @throws \InvalidArgumentException When command name is incorrect or ambiguous
0552 *
0553 * @api
0554 */
0555 public function find($name)
0556 {
0557 // namespace
0558 $namespace = '';
0559 $searchName = $name;
0560 if (false !== $pos = strrpos($name, ':')) {
0561 $namespace = $this->findNamespace(substr($name, 0, $pos));
0562 $searchName = $namespace.substr($name, $pos);
0563 }
0564
0565 // name
0566 $commands = array();
0567 foreach ($this->commands as $command) {
0568 $extractedNamespace = $this->extractNamespace($command->getName());
0569 if ($extractedNamespace === $namespace
0570 || !empty($namespace) && 0 === strpos($extractedNamespace, $namespace)
0571 ) {
0572 $commands[] = $command->getName();
0573 }
0574 }
0575
0576 $abbrevs = static::getAbbreviations(array_unique($commands));
0577 if (isset($abbrevs[$searchName]) && 1 == count($abbrevs[$searchName])) {
0578 return $this->get($abbrevs[$searchName][0]);
0579 }
0580
0581 if (isset($abbrevs[$searchName]) && in_array($searchName, $abbrevs[$searchName])) {
0582 return $this->get($searchName);
0583 }
0584
0585 if (isset($abbrevs[$searchName]) && count($abbrevs[$searchName]) > 1) {
0586 $suggestions = $this->getAbbreviationSuggestions($abbrevs[$searchName]);
0587
0588 throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
0589 }
0590
0591 // aliases
0592 $aliases = array();
0593 foreach ($this->commands as $command) {
0594 foreach ($command->getAliases() as $alias) {
0595 $extractedNamespace = $this->extractNamespace($alias);
0596 if ($extractedNamespace === $namespace
0597 || !empty($namespace) && 0 === strpos($extractedNamespace, $namespace)
0598 ) {
0599 $aliases[] = $alias;
0600 }
0601 }
0602 }
0603
0604 $aliases = static::getAbbreviations(array_unique($aliases));
0605 if (!isset($aliases[$searchName])) {
0606 $message = sprintf('Command "%s" is not defined.', $name);
0607
0608 if ($alternatives = $this->findAlternativeCommands($searchName, $abbrevs)) {
0609 if (1 == count($alternatives)) {
0610 $message .= "\n\nDid you mean this?\n ";
0611 } else {
0612 $message .= "\n\nDid you mean one of these?\n ";
0613 }
0614 $message .= implode("\n ", $alternatives);
0615 }
0616
0617 throw new \InvalidArgumentException($message);
0618 }
0619
0620 if (count($aliases[$searchName]) > 1) {
0621 throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $this->getAbbreviationSuggestions($aliases[$searchName])));
0622 }
0623
0624 return $this->get($aliases[$searchName][0]);
0625 }
0626
0627 /**
0628 * Gets the commands (registered in the given namespace if provided).
0629 *
0630 * The array keys are the full names and the values the command instances.
0631 *
0632 * @param string $namespace A namespace name
0633 *
0634 * @return Command[] An array of Command instances
0635 *
0636 * @api
0637 */
0638 public function all($namespace = null)
0639 {
0640 if (null === $namespace) {
0641 return $this->commands;
0642 }
0643
0644 $commands = array();
0645 foreach ($this->commands as $name => $command) {
0646 if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
0647 $commands[$name] = $command;
0648 }
0649 }
0650
0651 return $commands;
0652 }
0653
0654 /**
0655 * Returns an array of possible abbreviations given a set of names.
0656 *
0657 * @param array $names An array of names
0658 *
0659 * @return array An array of abbreviations
0660 */
0661 public static function getAbbreviations($names)
0662 {
0663 $abbrevs = array();
0664 foreach ($names as $name) {
0665 for ($len = strlen($name); $len > 0; --$len) {
0666 $abbrev = substr($name, 0, $len);
0667 $abbrevs[$abbrev][] = $name;
0668 }
0669 }
0670
0671 return $abbrevs;
0672 }
0673
0674 /**
0675 * Returns a text representation of the Application.
0676 *
0677 * @param string $namespace An optional namespace name
0678 * @param bool $raw Whether to return raw command list
0679 *
0680 * @return string A string representing the Application
0681 *
0682 * @deprecated Deprecated since version 2.3, to be removed in 3.0.
0683 */
0684 public function asText($namespace = null, $raw = false)
0685 {
0686 $descriptor = new TextDescriptor();
0687
0688 return $descriptor->describe($this, array('namespace' => $namespace, 'raw_text' => $raw));
0689 }
0690
0691 /**
0692 * Returns an XML representation of the Application.
0693 *
0694 * @param string $namespace An optional namespace name
0695 * @param bool $asDom Whether to return a DOM or an XML string
0696 *
0697 * @return string|\DOMDocument An XML string representing the Application
0698 *
0699 * @deprecated Deprecated since version 2.3, to be removed in 3.0.
0700 */
0701 public function asXml($namespace = null, $asDom = false)
0702 {
0703 $descriptor = new XmlDescriptor();
0704
0705 return $descriptor->describe($this, array('namespace' => $namespace, 'as_dom' => $asDom));
0706 }
0707
0708 /**
0709 * Renders a caught exception.
0710 *
0711 * @param \Exception $e An exception instance
0712 * @param OutputInterface $output An OutputInterface instance
0713 */
0714 public function renderException($e, $output)
0715 {
0716 do {
0717 $title = sprintf(' [%s] ', get_class($e));
0718
0719 $len = $this->stringWidth($title);
0720
0721 $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
0722 // 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
0723 if (defined('HHVM_VERSION') && $width > 1 << 31) {
0724 $width = 1 << 31;
0725 }
0726 $formatter = $output->getFormatter();
0727 $lines = array();
0728 foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
0729 foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
0730 // pre-format lines to get the right string length
0731 $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
0732 $lines[] = array($line, $lineLength);
0733
0734 $len = max($lineLength, $len);
0735 }
0736 }
0737
0738 $messages = array('', '');
0739 $messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len)));
0740 $messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
0741 foreach ($lines as $line) {
0742 $messages[] = $formatter->format(sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1])));
0743 }
0744 $messages[] = $emptyLine;
0745 $messages[] = '';
0746 $messages[] = '';
0747
0748 $output->writeln($messages, OutputInterface::OUTPUT_RAW);
0749
0750 if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
0751 $output->writeln('<comment>Exception trace:</comment>');
0752
0753 // exception related properties
0754 $trace = $e->getTrace();
0755 array_unshift($trace, array(
0756 'function' => '',
0757 'file' => $e->getFile() != null ? $e->getFile() : 'n/a',
0758 'line' => $e->getLine() != null ? $e->getLine() : 'n/a',
0759 'args' => array(),
0760 ));
0761
0762 for ($i = 0, $count = count($trace); $i < $count; $i++) {
0763 $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
0764 $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
0765 $function = $trace[$i]['function'];
0766 $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
0767 $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
0768
0769 $output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line));
0770 }
0771
0772 $output->writeln("");
0773 $output->writeln("");
0774 }
0775 } while ($e = $e->getPrevious());
0776
0777 if (null !== $this->runningCommand) {
0778 $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())));
0779 $output->writeln("");
0780 $output->writeln("");
0781 }
0782 }
0783
0784 /**
0785 * Tries to figure out the terminal width in which this application runs
0786 *
0787 * @return int|null
0788 */
0789 protected function getTerminalWidth()
0790 {
0791 $dimensions = $this->getTerminalDimensions();
0792
0793 return $dimensions[0];
0794 }
0795
0796 /**
0797 * Tries to figure out the terminal height in which this application runs
0798 *
0799 * @return int|null
0800 */
0801 protected function getTerminalHeight()
0802 {
0803 $dimensions = $this->getTerminalDimensions();
0804
0805 return $dimensions[1];
0806 }
0807
0808 /**
0809 * Tries to figure out the terminal dimensions based on the current environment
0810 *
0811 * @return array Array containing width and height
0812 */
0813 public function getTerminalDimensions()
0814 {
0815 if (defined('PHP_WINDOWS_VERSION_BUILD')) {
0816 // extract [w, H] from "wxh (WxH)"
0817 if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
0818 return array((int) $matches[1], (int) $matches[2]);
0819 }
0820 // extract [w, h] from "wxh"
0821 if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) {
0822 return array((int) $matches[1], (int) $matches[2]);
0823 }
0824 }
0825
0826 if ($sttyString = $this->getSttyColumns()) {
0827 // extract [w, h] from "rows h; columns w;"
0828 if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
0829 return array((int) $matches[2], (int) $matches[1]);
0830 }
0831 // extract [w, h] from "; h rows; w columns"
0832 if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
0833 return array((int) $matches[2], (int) $matches[1]);
0834 }
0835 }
0836
0837 return array(null, null);
0838 }
0839
0840 /**
0841 * Configures the input and output instances based on the user arguments and options.
0842 *
0843 * @param InputInterface $input An InputInterface instance
0844 * @param OutputInterface $output An OutputInterface instance
0845 */
0846 protected function configureIO(InputInterface $input, OutputInterface $output)
0847 {
0848 if (true === $input->hasParameterOption(array('--ansi'))) {
0849 $output->setDecorated(true);
0850 } elseif (true === $input->hasParameterOption(array('--no-ansi'))) {
0851 $output->setDecorated(false);
0852 }
0853
0854 if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) {
0855 $input->setInteractive(false);
0856 } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('dialog')) {
0857 $inputStream = $this->getHelperSet()->get('dialog')->getInputStream();
0858 if (!@posix_isatty($inputStream)) {
0859 $input->setInteractive(false);
0860 }
0861 }
0862
0863 if (true === $input->hasParameterOption(array('--quiet', '-q'))) {
0864 $output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
0865 } else {
0866 if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
0867 $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG);
0868 } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
0869 $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE);
0870 } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
0871 $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
0872 }
0873 }
0874 }
0875
0876 /**
0877 * Runs the current command.
0878 *
0879 * If an event dispatcher has been attached to the application,
0880 * events are also dispatched during the life-cycle of the command.
0881 *
0882 * @param Command $command A Command instance
0883 * @param InputInterface $input An Input instance
0884 * @param OutputInterface $output An Output instance
0885 *
0886 * @return int 0 if everything went fine, or an error code
0887 *
0888 * @throws \Exception when the command being run threw an exception
0889 */
0890 protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
0891 {
0892 if (null === $this->dispatcher) {
0893 return $command->run($input, $output);
0894 }
0895
0896 $event = new ConsoleCommandEvent($command, $input, $output);
0897 $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
0898
0899 try {
0900 $exitCode = $command->run($input, $output);
0901 } catch (\Exception $e) {
0902 $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode());
0903 $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
0904
0905 $event = new ConsoleExceptionEvent($command, $input, $output, $e, $event->getExitCode());
0906 $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
0907
0908 throw $event->getException();
0909 }
0910
0911 $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
0912 $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
0913
0914 return $event->getExitCode();
0915 }
0916
0917 /**
0918 * Gets the name of the command based on input.
0919 *
0920 * @param InputInterface $input The input interface
0921 *
0922 * @return string The command name
0923 */
0924 protected function getCommandName(InputInterface $input)
0925 {
0926 return $input->getFirstArgument();
0927 }
0928
0929 /**
0930 * Gets the default input definition.
0931 *
0932 * @return InputDefinition An InputDefinition instance
0933 */
0934 protected function getDefaultInputDefinition()
0935 {
0936 return new InputDefinition(array(
0937 new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
0938
0939 new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'),
0940 new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message.'),
0941 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.'),
0942 new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.'),
0943 new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output.'),
0944 new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output.'),
0945 new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'),
0946 ));
0947 }
0948
0949 /**
0950 * Gets the default commands that should always be available.
0951 *
0952 * @return Command[] An array of default Command instances
0953 */
0954 protected function getDefaultCommands()
0955 {
0956 return array(new HelpCommand(), new ListCommand());
0957 }
0958
0959 /**
0960 * Gets the default helper set with the helpers that should always be available.
0961 *
0962 * @return HelperSet A HelperSet instance
0963 */
0964 protected function getDefaultHelperSet()
0965 {
0966 return new HelperSet(array(
0967 new FormatterHelper(),
0968 new DialogHelper(),
0969 new ProgressHelper(),
0970 new TableHelper(),
0971 ));
0972 }
0973
0974 /**
0975 * Runs and parses stty -a if it's available, suppressing any error output
0976 *
0977 * @return string
0978 */
0979 private function getSttyColumns()
0980 {
0981 if (!function_exists('proc_open')) {
0982 return;
0983 }
0984
0985 $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
0986 $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
0987 if (is_resource($process)) {
0988 $info = stream_get_contents($pipes[1]);
0989 fclose($pipes[1]);
0990 fclose($pipes[2]);
0991 proc_close($process);
0992
0993 return $info;
0994 }
0995 }
0996
0997 /**
0998 * Runs and parses mode CON if it's available, suppressing any error output
0999 *
1000 * @return string <width>x<height> or null if it could not be parsed
1001 */
1002 private function getConsoleMode()
1003 {
1004 if (!function_exists('proc_open')) {
1005 return;
1006 }
1007
1008 $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
1009 $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
1010 if (is_resource($process)) {
1011 $info = stream_get_contents($pipes[1]);
1012 fclose($pipes[1]);
1013 fclose($pipes[2]);
1014 proc_close($process);
1015
1016 if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
1017 return $matches[2].'x'.$matches[1];
1018 }
1019 }
1020 }
1021
1022 /**
1023 * Returns abbreviated suggestions in string format.
1024 *
1025 * @param array $abbrevs Abbreviated suggestions to convert
1026 *
1027 * @return string A formatted string of abbreviated suggestions
1028 */
1029 private function getAbbreviationSuggestions($abbrevs)
1030 {
1031 return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
1032 }
1033
1034 /**
1035 * Returns the namespace part of the command name.
1036 *
1037 * This method is not part of public API and should not be used directly.
1038 *
1039 * @param string $name The full name of the command
1040 * @param string $limit The maximum number of parts of the namespace
1041 *
1042 * @return string The namespace of the command
1043 */
1044 public function extractNamespace($name, $limit = null)
1045 {
1046 $parts = explode(':', $name);
1047 array_pop($parts);
1048
1049 return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
1050 }
1051
1052 /**
1053 * Finds alternative commands of $name
1054 *
1055 * @param string $name The full name of the command
1056 * @param array $abbrevs The abbreviations
1057 *
1058 * @return array A sorted array of similar commands
1059 */
1060 private function findAlternativeCommands($name, $abbrevs)
1061 {
1062 $callback = function ($item) {
1063 return $item->getName();
1064 };
1065
1066 return $this->findAlternatives($name, $this->commands, $abbrevs, $callback);
1067 }
1068
1069 /**
1070 * Finds alternative namespace of $name
1071 *
1072 * @param string $name The full name of the namespace
1073 * @param array $abbrevs The abbreviations
1074 *
1075 * @return array A sorted array of similar namespace
1076 */
1077 private function findAlternativeNamespace($name, $abbrevs)
1078 {
1079 return $this->findAlternatives($name, $this->getNamespaces(), $abbrevs);
1080 }
1081
1082 /**
1083 * Finds alternative of $name among $collection,
1084 * if nothing is found in $collection, try in $abbrevs
1085 *
1086 * @param string $name The string
1087 * @param array|\Traversable $collection The collection
1088 * @param array $abbrevs The abbreviations
1089 * @param Closure|string|array $callback The callable to transform collection item before comparison
1090 *
1091 * @return array A sorted array of similar string
1092 */
1093 private function findAlternatives($name, $collection, $abbrevs, $callback = null)
1094 {
1095 $alternatives = array();
1096
1097 foreach ($collection as $item) {
1098 if (null !== $callback) {
1099 $item = call_user_func($callback, $item);
1100 }
1101
1102 $lev = levenshtein($name, $item);
1103 if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
1104 $alternatives[$item] = $lev;
1105 }
1106 }
1107
1108 if (!$alternatives) {
1109 foreach ($abbrevs as $key => $values) {
1110 $lev = levenshtein($name, $key);
1111 if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) {
1112 foreach ($values as $value) {
1113 $alternatives[$value] = $lev;
1114 }
1115 }
1116 }
1117 }
1118
1119 asort($alternatives);
1120
1121 return array_keys($alternatives);
1122 }
1123
1124 private function stringWidth($string)
1125 {
1126 if (!function_exists('mb_strwidth')) {
1127 return strlen($string);
1128 }
1129
1130 if (false === $encoding = mb_detect_encoding($string)) {
1131 return strlen($string);
1132 }
1133
1134 return mb_strwidth($string, $encoding);
1135 }
1136
1137 private function splitStringByWidth($string, $width)
1138 {
1139 // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
1140 // additionally, array_slice() is not enough as some character has doubled width.
1141 // we need a function to split string not by character count but by string width
1142
1143 if (!function_exists('mb_strwidth')) {
1144 return str_split($string, $width);
1145 }
1146
1147 if (false === $encoding = mb_detect_encoding($string)) {
1148 return str_split($string, $width);
1149 }
1150
1151 $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
1152 $lines = array();
1153 $line = '';
1154 foreach (preg_split('//u', $utf8String) as $char) {
1155 // test if $char could be appended to current line
1156 if (mb_strwidth($line.$char, 'utf8') <= $width) {
1157 $line .= $char;
1158 continue;
1159 }
1160 // if not, push current line to array and make new line
1161 $lines[] = str_pad($line, $width);
1162 $line = $char;
1163 }
1164 if (strlen($line)) {
1165 $lines[] = count($lines) ? str_pad($line, $width) : $line;
1166 }
1167
1168 mb_convert_variables($encoding, 'utf8', $lines);
1169
1170 return $lines;
1171 }
1172 }
1173