TYPO3  7.6
vendor/symfony/console/Application.php
Go to the documentation of this file.
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 
41 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
42 
61 {
62  private $commands = array();
63  private $wantHelps = false;
64  private $runningCommand;
65  private $name;
66  private $version;
67  private $catchExceptions = true;
68  private $autoExit = true;
69  private $definition;
70  private $helperSet;
71  private $dispatcher;
73  private $defaultCommand;
74 
83  public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
84  {
85  $this->name = $name;
86  $this->version = $version;
87  $this->defaultCommand = 'list';
88  $this->helperSet = $this->getDefaultHelperSet();
89  $this->definition = $this->getDefaultInputDefinition();
90 
91  foreach ($this->getDefaultCommands() as $command) {
92  $this->add($command);
93  }
94  }
95 
96  public function setDispatcher(EventDispatcherInterface $dispatcher)
97  {
98  $this->dispatcher = $dispatcher;
99  }
100 
113  public function run(InputInterface $input = null, OutputInterface $output = null)
114  {
115  if (null === $input) {
116  $input = new ArgvInput();
117  }
118 
119  if (null === $output) {
120  $output = new ConsoleOutput();
121  }
122 
123  $this->configureIO($input, $output);
124 
125  try {
126  $exitCode = $this->doRun($input, $output);
127  } catch (\Exception $e) {
128  if (!$this->catchExceptions) {
129  throw $e;
130  }
131 
132  if ($output instanceof ConsoleOutputInterface) {
133  $this->renderException($e, $output->getErrorOutput());
134  } else {
135  $this->renderException($e, $output);
136  }
137 
138  $exitCode = $e->getCode();
139  if (is_numeric($exitCode)) {
140  $exitCode = (int) $exitCode;
141  if (0 === $exitCode) {
142  $exitCode = 1;
143  }
144  } else {
145  $exitCode = 1;
146  }
147  }
148 
149  if ($this->autoExit) {
150  if ($exitCode > 255) {
151  $exitCode = 255;
152  }
153 
154  exit($exitCode);
155  }
156 
157  return $exitCode;
158  }
159 
168  public function doRun(InputInterface $input, OutputInterface $output)
169  {
170  if (true === $input->hasParameterOption(array('--version', '-V'))) {
171  $output->writeln($this->getLongVersion());
172 
173  return 0;
174  }
175 
176  $name = $this->getCommandName($input);
177  if (true === $input->hasParameterOption(array('--help', '-h'))) {
178  if (!$name) {
179  $name = 'help';
180  $input = new ArrayInput(array('command' => 'help'));
181  } else {
182  $this->wantHelps = true;
183  }
184  }
185 
186  if (!$name) {
188  $input = new ArrayInput(array('command' => $this->defaultCommand));
189  }
190 
191  // the command name MUST be the first element of the input
192  $command = $this->find($name);
193 
194  $this->runningCommand = $command;
195  $exitCode = $this->doRunCommand($command, $input, $output);
196  $this->runningCommand = null;
197 
198  return $exitCode;
199  }
200 
209  {
210  $this->helperSet = $helperSet;
211  }
212 
220  public function getHelperSet()
221  {
222  return $this->helperSet;
223  }
224 
233  {
234  $this->definition = $definition;
235  }
236 
242  public function getDefinition()
243  {
244  return $this->definition;
245  }
246 
252  public function getHelp()
253  {
254  return $this->getLongVersion();
255  }
256 
264  public function setCatchExceptions($boolean)
265  {
266  $this->catchExceptions = (bool) $boolean;
267  }
268 
276  public function setAutoExit($boolean)
277  {
278  $this->autoExit = (bool) $boolean;
279  }
280 
288  public function getName()
289  {
290  return $this->name;
291  }
292 
300  public function setName($name)
301  {
302  $this->name = $name;
303  }
304 
312  public function getVersion()
313  {
314  return $this->version;
315  }
316 
324  public function setVersion($version)
325  {
326  $this->version = $version;
327  }
328 
336  public function getLongVersion()
337  {
338  if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) {
339  return sprintf('<info>%s</info> version <comment>%s</comment>', $this->getName(), $this->getVersion());
340  }
341 
342  return '<info>Console Tool</info>';
343  }
344 
354  public function register($name)
355  {
356  return $this->add(new Command($name));
357  }
358 
366  public function addCommands(array $commands)
367  {
368  foreach ($commands as $command) {
369  $this->add($command);
370  }
371  }
372 
384  public function add(Command $command)
385  {
386  $command->setApplication($this);
387 
388  if (!$command->isEnabled()) {
389  $command->setApplication(null);
390 
391  return;
392  }
393 
394  if (null === $command->getDefinition()) {
395  throw new \LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', get_class($command)));
396  }
397 
398  $this->commands[$command->getName()] = $command;
399 
400  foreach ($command->getAliases() as $alias) {
401  $this->commands[$alias] = $command;
402  }
403 
404  return $command;
405  }
406 
418  public function get($name)
419  {
420  if (!isset($this->commands[$name])) {
421  throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name));
422  }
423 
424  $command = $this->commands[$name];
425 
426  if ($this->wantHelps) {
427  $this->wantHelps = false;
428 
429  $helpCommand = $this->get('help');
430  $helpCommand->setCommand($command);
431 
432  return $helpCommand;
433  }
434 
435  return $command;
436  }
437 
447  public function has($name)
448  {
449  return isset($this->commands[$name]);
450  }
451 
459  public function getNamespaces()
460  {
461  $namespaces = array();
462  foreach ($this->commands as $command) {
463  $namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
464 
465  foreach ($command->getAliases() as $alias) {
466  $namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
467  }
468  }
469 
470  return array_values(array_unique(array_filter($namespaces)));
471  }
472 
482  public function findNamespace($namespace)
483  {
484  $allNamespaces = $this->getNamespaces();
485  $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $namespace);
486  $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);
487 
488  if (empty($namespaces)) {
489  $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
490 
491  if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
492  if (1 == count($alternatives)) {
493  $message .= "\n\nDid you mean this?\n ";
494  } else {
495  $message .= "\n\nDid you mean one of these?\n ";
496  }
497 
498  $message .= implode("\n ", $alternatives);
499  }
500 
501  throw new \InvalidArgumentException($message);
502  }
503 
504  $exact = in_array($namespace, $namespaces, true);
505  if (count($namespaces) > 1 && !$exact) {
506  throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))));
507  }
508 
509  return $exact ? $namespace : reset($namespaces);
510  }
511 
526  public function find($name)
527  {
528  $allCommands = array_keys($this->commands);
529  $expr = preg_replace_callback('{([^:]+|)}', function ($matches) { return preg_quote($matches[1]).'[^:]*'; }, $name);
530  $commands = preg_grep('{^'.$expr.'}', $allCommands);
531 
532  if (empty($commands) || count(preg_grep('{^'.$expr.'$}', $commands)) < 1) {
533  if (false !== $pos = strrpos($name, ':')) {
534  // check if a namespace exists and contains commands
535  $this->findNamespace(substr($name, 0, $pos));
536  }
537 
538  $message = sprintf('Command "%s" is not defined.', $name);
539 
540  if ($alternatives = $this->findAlternatives($name, $allCommands)) {
541  if (1 == count($alternatives)) {
542  $message .= "\n\nDid you mean this?\n ";
543  } else {
544  $message .= "\n\nDid you mean one of these?\n ";
545  }
546  $message .= implode("\n ", $alternatives);
547  }
548 
549  throw new \InvalidArgumentException($message);
550  }
551 
552  // filter out aliases for commands which are already on the list
553  if (count($commands) > 1) {
554  $commandList = $this->commands;
555  $commands = array_filter($commands, function ($nameOrAlias) use ($commandList, $commands) {
556  $commandName = $commandList[$nameOrAlias]->getName();
557 
558  return $commandName === $nameOrAlias || !in_array($commandName, $commands);
559  });
560  }
561 
562  $exact = in_array($name, $commands, true);
563  if (count($commands) > 1 && !$exact) {
564  $suggestions = $this->getAbbreviationSuggestions(array_values($commands));
565 
566  throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions));
567  }
568 
569  return $this->get($exact ? $name : reset($commands));
570  }
571 
583  public function all($namespace = null)
584  {
585  if (null === $namespace) {
586  return $this->commands;
587  }
588 
589  $commands = array();
590  foreach ($this->commands as $name => $command) {
591  if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) {
592  $commands[$name] = $command;
593  }
594  }
595 
596  return $commands;
597  }
598 
606  public static function getAbbreviations($names)
607  {
608  $abbrevs = array();
609  foreach ($names as $name) {
610  for ($len = strlen($name); $len > 0; --$len) {
611  $abbrev = substr($name, 0, $len);
612  $abbrevs[$abbrev][] = $name;
613  }
614  }
615 
616  return $abbrevs;
617  }
618 
629  public function asText($namespace = null, $raw = false)
630  {
631  @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED);
632 
633  $descriptor = new TextDescriptor();
634  $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, !$raw);
635  $descriptor->describe($output, $this, array('namespace' => $namespace, 'raw_output' => true));
636 
637  return $output->fetch();
638  }
639 
650  public function asXml($namespace = null, $asDom = false)
651  {
652  @trigger_error('The '.__METHOD__.' method is deprecated since version 2.3 and will be removed in 3.0.', E_USER_DEPRECATED);
653 
654  $descriptor = new XmlDescriptor();
655 
656  if ($asDom) {
657  return $descriptor->getApplicationDocument($this, $namespace);
658  }
659 
660  $output = new BufferedOutput();
661  $descriptor->describe($output, $this, array('namespace' => $namespace));
662 
663  return $output->fetch();
664  }
665 
672  public function renderException($e, $output)
673  {
674  do {
675  $title = sprintf(' [%s] ', get_class($e));
676 
677  $len = $this->stringWidth($title);
678 
679  $width = $this->getTerminalWidth() ? $this->getTerminalWidth() - 1 : PHP_INT_MAX;
680  // 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
681  if (defined('HHVM_VERSION') && $width > 1 << 31) {
682  $width = 1 << 31;
683  }
684  $formatter = $output->getFormatter();
685  $lines = array();
686  foreach (preg_split('/\r?\n/', $e->getMessage()) as $line) {
687  foreach ($this->splitStringByWidth($line, $width - 4) as $line) {
688  // pre-format lines to get the right string length
689  $lineLength = $this->stringWidth(preg_replace('/\[[^m]*m/', '', $formatter->format($line))) + 4;
690  $lines[] = array($line, $lineLength);
691 
692  $len = max($lineLength, $len);
693  }
694  }
695 
696  $messages = array('', '');
697  $messages[] = $emptyLine = $formatter->format(sprintf('<error>%s</error>', str_repeat(' ', $len)));
698  $messages[] = $formatter->format(sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - $this->stringWidth($title)))));
699  foreach ($lines as $line) {
700  $messages[] = $formatter->format(sprintf('<error> %s %s</error>', $line[0], str_repeat(' ', $len - $line[1])));
701  }
702  $messages[] = $emptyLine;
703  $messages[] = '';
704  $messages[] = '';
705 
706  $output->writeln($messages, OutputInterface::OUTPUT_RAW);
707 
708  if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
709  $output->writeln('<comment>Exception trace:</comment>');
710 
711  // exception related properties
712  $trace = $e->getTrace();
713  array_unshift($trace, array(
714  'function' => '',
715  'file' => $e->getFile() !== null ? $e->getFile() : 'n/a',
716  'line' => $e->getLine() !== null ? $e->getLine() : 'n/a',
717  'args' => array(),
718  ));
719 
720  for ($i = 0, $count = count($trace); $i < $count; ++$i) {
721  $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : '';
722  $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : '';
723  $function = $trace[$i]['function'];
724  $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a';
725  $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a';
726 
727  $output->writeln(sprintf(' %s%s%s() at <info>%s:%s</info>', $class, $type, $function, $file, $line));
728  }
729 
730  $output->writeln('');
731  $output->writeln('');
732  }
733  } while ($e = $e->getPrevious());
734 
735  if (null !== $this->runningCommand) {
736  $output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())));
737  $output->writeln('');
738  $output->writeln('');
739  }
740  }
741 
747  protected function getTerminalWidth()
748  {
749  $dimensions = $this->getTerminalDimensions();
750 
751  return $dimensions[0];
752  }
753 
759  protected function getTerminalHeight()
760  {
761  $dimensions = $this->getTerminalDimensions();
762 
763  return $dimensions[1];
764  }
765 
771  public function getTerminalDimensions()
772  {
773  if ($this->terminalDimensions) {
775  }
776 
777  if ('\\' === DIRECTORY_SEPARATOR) {
778  // extract [w, H] from "wxh (WxH)"
779  if (preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', trim(getenv('ANSICON')), $matches)) {
780  return array((int) $matches[1], (int) $matches[2]);
781  }
782  // extract [w, h] from "wxh"
783  if (preg_match('/^(\d+)x(\d+)$/', $this->getConsoleMode(), $matches)) {
784  return array((int) $matches[1], (int) $matches[2]);
785  }
786  }
787 
788  if ($sttyString = $this->getSttyColumns()) {
789  // extract [w, h] from "rows h; columns w;"
790  if (preg_match('/rows.(\d+);.columns.(\d+);/i', $sttyString, $matches)) {
791  return array((int) $matches[2], (int) $matches[1]);
792  }
793  // extract [w, h] from "; h rows; w columns"
794  if (preg_match('/;.(\d+).rows;.(\d+).columns/i', $sttyString, $matches)) {
795  return array((int) $matches[2], (int) $matches[1]);
796  }
797  }
798 
799  return array(null, null);
800  }
801 
812  public function setTerminalDimensions($width, $height)
813  {
814  $this->terminalDimensions = array($width, $height);
815 
816  return $this;
817  }
818 
825  protected function configureIO(InputInterface $input, OutputInterface $output)
826  {
827  if (true === $input->hasParameterOption(array('--ansi'))) {
828  $output->setDecorated(true);
829  } elseif (true === $input->hasParameterOption(array('--no-ansi'))) {
830  $output->setDecorated(false);
831  }
832 
833  if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) {
834  $input->setInteractive(false);
835  } elseif (function_exists('posix_isatty') && $this->getHelperSet()->has('question')) {
836  $inputStream = $this->getHelperSet()->get('question')->getInputStream();
837  if (!@posix_isatty($inputStream) && false === getenv('SHELL_INTERACTIVE')) {
838  $input->setInteractive(false);
839  }
840  }
841 
842  if (true === $input->hasParameterOption(array('--quiet', '-q'))) {
844  } else {
845  if ($input->hasParameterOption('-vvv') || $input->hasParameterOption('--verbose=3') || $input->getParameterOption('--verbose') === 3) {
847  } elseif ($input->hasParameterOption('-vv') || $input->hasParameterOption('--verbose=2') || $input->getParameterOption('--verbose') === 2) {
849  } elseif ($input->hasParameterOption('-v') || $input->hasParameterOption('--verbose=1') || $input->hasParameterOption('--verbose') || $input->getParameterOption('--verbose')) {
851  }
852  }
853  }
854 
869  protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
870  {
871  foreach ($command->getHelperSet() as $helper) {
872  if ($helper instanceof InputAwareInterface) {
873  $helper->setInput($input);
874  }
875  }
876 
877  if (null === $this->dispatcher) {
878  return $command->run($input, $output);
879  }
880 
881  $event = new ConsoleCommandEvent($command, $input, $output);
882  $this->dispatcher->dispatch(ConsoleEvents::COMMAND, $event);
883 
884  if ($event->commandShouldRun()) {
885  try {
886  $exitCode = $command->run($input, $output);
887  } catch (\Exception $e) {
888  $event = new ConsoleTerminateEvent($command, $input, $output, $e->getCode());
889  $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
890 
891  $event = new ConsoleExceptionEvent($command, $input, $output, $e, $event->getExitCode());
892  $this->dispatcher->dispatch(ConsoleEvents::EXCEPTION, $event);
893 
894  throw $event->getException();
895  }
896  } else {
898  }
899 
900  $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
901  $this->dispatcher->dispatch(ConsoleEvents::TERMINATE, $event);
902 
903  return $event->getExitCode();
904  }
905 
913  protected function getCommandName(InputInterface $input)
914  {
915  return $input->getFirstArgument();
916  }
917 
923  protected function getDefaultInputDefinition()
924  {
925  return new InputDefinition(array(
926  new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
927 
928  new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message'),
929  new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
930  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'),
931  new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
932  new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
933  new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
934  new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question'),
935  ));
936  }
937 
943  protected function getDefaultCommands()
944  {
945  return array(new HelpCommand(), new ListCommand());
946  }
947 
953  protected function getDefaultHelperSet()
954  {
955  return new HelperSet(array(
956  new FormatterHelper(),
957  new DialogHelper(false),
958  new ProgressHelper(false),
959  new TableHelper(false),
960  new DebugFormatterHelper(),
961  new ProcessHelper(),
962  new QuestionHelper(),
963  ));
964  }
965 
971  private function getSttyColumns()
972  {
973  if (!function_exists('proc_open')) {
974  return;
975  }
976 
977  $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
978  $process = proc_open('stty -a | grep columns', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
979  if (is_resource($process)) {
980  $info = stream_get_contents($pipes[1]);
981  fclose($pipes[1]);
982  fclose($pipes[2]);
983  proc_close($process);
984 
985  return $info;
986  }
987  }
988 
994  private function getConsoleMode()
995  {
996  if (!function_exists('proc_open')) {
997  return;
998  }
999 
1000  $descriptorspec = array(1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
1001  $process = proc_open('mode CON', $descriptorspec, $pipes, null, null, array('suppress_errors' => true));
1002  if (is_resource($process)) {
1003  $info = stream_get_contents($pipes[1]);
1004  fclose($pipes[1]);
1005  fclose($pipes[2]);
1006  proc_close($process);
1007 
1008  if (preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) {
1009  return $matches[2].'x'.$matches[1];
1010  }
1011  }
1012  }
1013 
1021  private function getAbbreviationSuggestions($abbrevs)
1022  {
1023  return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : '');
1024  }
1025 
1036  public function extractNamespace($name, $limit = null)
1037  {
1038  $parts = explode(':', $name);
1039  array_pop($parts);
1040 
1041  return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit));
1042  }
1043 
1053  private function findAlternatives($name, $collection)
1054  {
1055  $threshold = 1e3;
1056  $alternatives = array();
1057 
1058  $collectionParts = array();
1059  foreach ($collection as $item) {
1060  $collectionParts[$item] = explode(':', $item);
1061  }
1062 
1063  foreach (explode(':', $name) as $i => $subname) {
1064  foreach ($collectionParts as $collectionName => $parts) {
1065  $exists = isset($alternatives[$collectionName]);
1066  if (!isset($parts[$i]) && $exists) {
1067  $alternatives[$collectionName] += $threshold;
1068  continue;
1069  } elseif (!isset($parts[$i])) {
1070  continue;
1071  }
1072 
1073  $lev = levenshtein($subname, $parts[$i]);
1074  if ($lev <= strlen($subname) / 3 || '' !== $subname && false !== strpos($parts[$i], $subname)) {
1075  $alternatives[$collectionName] = $exists ? $alternatives[$collectionName] + $lev : $lev;
1076  } elseif ($exists) {
1077  $alternatives[$collectionName] += $threshold;
1078  }
1079  }
1080  }
1081 
1082  foreach ($collection as $item) {
1083  $lev = levenshtein($name, $item);
1084  if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
1085  $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev;
1086  }
1087  }
1088 
1089  $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
1090  asort($alternatives);
1091 
1092  return array_keys($alternatives);
1093  }
1094 
1100  public function setDefaultCommand($commandName)
1101  {
1102  $this->defaultCommand = $commandName;
1103  }
1104 
1105  private function stringWidth($string)
1106  {
1107  if (!function_exists('mb_strwidth')) {
1108  return strlen($string);
1109  }
1110 
1111  if (false === $encoding = mb_detect_encoding($string)) {
1112  return strlen($string);
1113  }
1114 
1115  return mb_strwidth($string, $encoding);
1116  }
1117 
1118  private function splitStringByWidth($string, $width)
1119  {
1120  // str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
1121  // additionally, array_slice() is not enough as some character has doubled width.
1122  // we need a function to split string not by character count but by string width
1123 
1124  if (!function_exists('mb_strwidth')) {
1125  return str_split($string, $width);
1126  }
1127 
1128  if (false === $encoding = mb_detect_encoding($string)) {
1129  return str_split($string, $width);
1130  }
1131 
1132  $utf8String = mb_convert_encoding($string, 'utf8', $encoding);
1133  $lines = array();
1134  $line = '';
1135  foreach (preg_split('//u', $utf8String) as $char) {
1136  // test if $char could be appended to current line
1137  if (mb_strwidth($line.$char, 'utf8') <= $width) {
1138  $line .= $char;
1139  continue;
1140  }
1141  // if not, push current line to array and make new line
1142  $lines[] = str_pad($line, $width);
1143  $line = $char;
1144  }
1145  if ('' !== $line) {
1146  $lines[] = count($lines) ? str_pad($line, $width) : $line;
1147  }
1148 
1149  mb_convert_variables($encoding, 'utf8', $lines);
1150 
1151  return $lines;
1152  }
1153 
1161  private function extractAllNamespaces($name)
1162  {
1163  // -1 as third argument is needed to skip the command short name when exploding
1164  $parts = explode(':', $name, -1);
1165  $namespaces = array();
1166 
1167  foreach ($parts as $part) {
1168  if (count($namespaces)) {
1169  $namespaces[] = end($namespaces).':'.$part;
1170  } else {
1171  $namespaces[] = $part;
1172  }
1173  }
1174 
1175  return $namespaces;
1176  }
1177 }