Overview

Namespaces

  • Contrib
    • Bundle
      • CoverallsBundle
        • Console
        • Entity
      • CoverallsV1Bundle
        • Api
        • Collector
        • Command
        • Config
        • Entity
          • Git
    • Component
      • File
      • Log
      • System
        • Git
  • Guzzle
    • Batch
      • Exception
    • Cache
    • Common
      • Exception
    • Http
      • Curl
      • Exception
      • Message
      • QueryAggregator
    • Inflection
    • Iterator
    • Log
    • Parser
      • Cookie
      • Message
      • UriTemplate
      • Url
    • Plugin
      • Async
      • Backoff
      • Cache
      • Cookie
        • CookieJar
        • Exception
      • CurlAuth
      • ErrorResponse
        • Exception
      • History
      • Log
      • Md5
      • Mock
      • Oauth
    • Service
      • Builder
      • Command
        • Factory
        • LocationVisitor
          • Request
          • Response
      • Description
      • Exception
      • Resource
    • Stream
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Config
        • Definition
          • Builder
          • Exception
        • Exception
        • Loader
        • Resource
        • Util
      • Console
        • Command
        • Formatter
        • Helper
        • Input
        • Output
        • Tester
      • EventDispatcher
        • Debug
      • Finder
        • Adapter
        • Comparator
        • Exception
        • Expression
        • Iterator
        • Shell
      • Stopwatch
      • Yaml
        • Exception

Classes

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