1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 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: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 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: 62: 63: 64: 65: 66: 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: 85: 86: 87: 88: 89: 90: 91: 92: 93: 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:
127: exit($statusCode);
128:
129: }
130:
131: return $statusCode;
132: }
133:
134: 135: 136: 137: 138: 139: 140: 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:
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: 201: 202: 203: 204: 205:
206: public function setHelperSet(HelperSet $helperSet)
207: {
208: $this->helperSet = $helperSet;
209: }
210:
211: 212: 213: 214: 215: 216: 217:
218: public function getHelperSet()
219: {
220: return $this->helperSet;
221: }
222:
223: 224: 225: 226: 227: 228: 229:
230: public function setDefinition(InputDefinition $definition)
231: {
232: $this->definition = $definition;
233: }
234:
235: 236: 237: 238: 239:
240: public function getDefinition()
241: {
242: return $this->definition;
243: }
244:
245: 246: 247: 248: 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: 274: 275: 276: 277: 278:
279: public function setCatchExceptions($boolean)
280: {
281: $this->catchExceptions = (Boolean) $boolean;
282: }
283:
284: 285: 286: 287: 288: 289: 290:
291: public function setAutoExit($boolean)
292: {
293: $this->autoExit = (Boolean) $boolean;
294: }
295:
296: 297: 298: 299: 300: 301: 302:
303: public function getName()
304: {
305: return $this->name;
306: }
307:
308: 309: 310: 311: 312: 313: 314:
315: public function setName($name)
316: {
317: $this->name = $name;
318: }
319:
320: 321: 322: 323: 324: 325: 326:
327: public function getVersion()
328: {
329: return $this->version;
330: }
331:
332: 333: 334: 335: 336: 337: 338:
339: public function setVersion($version)
340: {
341: $this->version = $version;
342: }
343:
344: 345: 346: 347: 348: 349: 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: 362: 363: 364: 365: 366: 367: 368:
369: public function register($name)
370: {
371: return $this->add(new Command($name));
372: }
373:
374: 375: 376: 377: 378: 379: 380:
381: public function addCommands(array $commands)
382: {
383: foreach ($commands as $command) {
384: $this->add($command);
385: }
386: }
387:
388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 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: 420: 421: 422: 423: 424: 425: 426: 427: 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: 451: 452: 453: 454: 455: 456: 457:
458: public function has($name)
459: {
460: return isset($this->commands[$name]);
461: }
462:
463: 464: 465: 466: 467: 468: 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: 486: 487: 488: 489: 490: 491: 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: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547:
548: public function find($name)
549: {
550:
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:
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:
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: 618: 619: 620: 621: 622: 623: 624: 625: 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: 645: 646: 647: 648: 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:
665: foreach ($names as $name) {
666: $abbrevs[$name] = array($name);
667: }
668:
669: return $abbrevs;
670: }
671:
672: 673: 674: 675: 676: 677: 678: 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:
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: 724: 725: 726: 727: 728: 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:
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: 778: 779: 780: 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:
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: 860: 861: 862:
863: protected function getTerminalWidth()
864: {
865: $dimensions = $this->getTerminalDimensions();
866:
867: return $dimensions[0];
868: }
869:
870: 871: 872: 873: 874:
875: protected function getTerminalHeight()
876: {
877: $dimensions = $this->getTerminalDimensions();
878:
879: return $dimensions[1];
880: }
881:
882: 883: 884: 885: 886:
887: public function getTerminalDimensions()
888: {
889: if (defined('PHP_WINDOWS_VERSION_BUILD')) {
890:
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:
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:
902: if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
903: return array((int) $matches[2], (int) $matches[1]);
904: }
905:
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: 916: 917: 918: 919: 920:
921: protected function getCommandName(InputInterface $input)
922: {
923: return $input->getFirstArgument();
924: }
925:
926: 927: 928: 929: 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: 948: 949: 950:
951: protected function getDefaultCommands()
952: {
953: return array(new HelpCommand(), new ListCommand());
954: }
955:
956: 957: 958: 959: 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: 972: 973: 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: 995: 996: 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: 1020: 1021: 1022: 1023: 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: 1047: 1048: 1049: 1050: 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: 1059: 1060: 1061: 1062: 1063: 1064:
1065: private function ($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: 1075: 1076: 1077: 1078: 1079: 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: 1092: 1093: 1094: 1095: 1096: 1097:
1098: private function findAlternativeNamespace($name, $abbrevs)
1099: {
1100: return $this->findAlternatives($name, $this->getNamespaces(), $abbrevs);
1101: }
1102:
1103: 1104: 1105: 1106: 1107: 1108: 1109: 1110: 1111: 1112: 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: