TextDescriptor.php 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Console\Descriptor;
  11. use Symfony\Component\Console\Application;
  12. use Symfony\Component\Console\Command\Command;
  13. use Symfony\Component\Console\Input\InputArgument;
  14. use Symfony\Component\Console\Input\InputDefinition;
  15. use Symfony\Component\Console\Input\InputOption;
  16. /**
  17. * Text descriptor.
  18. *
  19. * @author Jean-François Simon <contact@jfsimon.fr>
  20. *
  21. * @internal
  22. */
  23. class TextDescriptor extends Descriptor
  24. {
  25. /**
  26. * {@inheritdoc}
  27. */
  28. protected function describeInputArgument(InputArgument $argument, array $options = array())
  29. {
  30. if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) {
  31. $default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
  32. } else {
  33. $default = '';
  34. }
  35. $totalWidth = isset($options['total_width']) ? $options['total_width'] : strlen($argument->getName());
  36. $spacingWidth = $totalWidth - strlen($argument->getName()) + 2;
  37. $this->writeText(sprintf(' <info>%s</info>%s%s%s',
  38. $argument->getName(),
  39. str_repeat(' ', $spacingWidth),
  40. // + 17 = 2 spaces + <info> + </info> + 2 spaces
  41. preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $argument->getDescription()),
  42. $default
  43. ), $options);
  44. }
  45. /**
  46. * {@inheritdoc}
  47. */
  48. protected function describeInputOption(InputOption $option, array $options = array())
  49. {
  50. if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) {
  51. $default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
  52. } else {
  53. $default = '';
  54. }
  55. $value = '';
  56. if ($option->acceptValue()) {
  57. $value = '='.strtoupper($option->getName());
  58. if ($option->isValueOptional()) {
  59. $value = '['.$value.']';
  60. }
  61. }
  62. $totalWidth = isset($options['total_width']) ? $options['total_width'] : $this->calculateTotalWidthForOptions(array($option));
  63. $synopsis = sprintf('%s%s',
  64. $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ',
  65. sprintf('--%s%s', $option->getName(), $value)
  66. );
  67. $spacingWidth = $totalWidth - strlen($synopsis) + 2;
  68. $this->writeText(sprintf(' <info>%s</info>%s%s%s%s',
  69. $synopsis,
  70. str_repeat(' ', $spacingWidth),
  71. // + 17 = 2 spaces + <info> + </info> + 2 spaces
  72. preg_replace('/\s*[\r\n]\s*/', "\n".str_repeat(' ', $totalWidth + 17), $option->getDescription()),
  73. $default,
  74. $option->isArray() ? '<comment> (multiple values allowed)</comment>' : ''
  75. ), $options);
  76. }
  77. /**
  78. * {@inheritdoc}
  79. */
  80. protected function describeInputDefinition(InputDefinition $definition, array $options = array())
  81. {
  82. $totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
  83. foreach ($definition->getArguments() as $argument) {
  84. $totalWidth = max($totalWidth, strlen($argument->getName()));
  85. }
  86. if ($definition->getArguments()) {
  87. $this->writeText('<comment>Arguments:</comment>', $options);
  88. $this->writeText("\n");
  89. foreach ($definition->getArguments() as $argument) {
  90. $this->describeInputArgument($argument, array_merge($options, array('total_width' => $totalWidth)));
  91. $this->writeText("\n");
  92. }
  93. }
  94. if ($definition->getArguments() && $definition->getOptions()) {
  95. $this->writeText("\n");
  96. }
  97. if ($definition->getOptions()) {
  98. $laterOptions = array();
  99. $this->writeText('<comment>Options:</comment>', $options);
  100. foreach ($definition->getOptions() as $option) {
  101. if (strlen($option->getShortcut()) > 1) {
  102. $laterOptions[] = $option;
  103. continue;
  104. }
  105. $this->writeText("\n");
  106. $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth)));
  107. }
  108. foreach ($laterOptions as $option) {
  109. $this->writeText("\n");
  110. $this->describeInputOption($option, array_merge($options, array('total_width' => $totalWidth)));
  111. }
  112. }
  113. }
  114. /**
  115. * {@inheritdoc}
  116. */
  117. protected function describeCommand(Command $command, array $options = array())
  118. {
  119. $command->getSynopsis(true);
  120. $command->getSynopsis(false);
  121. $command->mergeApplicationDefinition(false);
  122. $this->writeText('<comment>Usage:</comment>', $options);
  123. foreach (array_merge(array($command->getSynopsis(true)), $command->getAliases(), $command->getUsages()) as $usage) {
  124. $this->writeText("\n");
  125. $this->writeText(' '.$usage, $options);
  126. }
  127. $this->writeText("\n");
  128. $definition = $command->getNativeDefinition();
  129. if ($definition->getOptions() || $definition->getArguments()) {
  130. $this->writeText("\n");
  131. $this->describeInputDefinition($definition, $options);
  132. $this->writeText("\n");
  133. }
  134. if ($help = $command->getProcessedHelp()) {
  135. $this->writeText("\n");
  136. $this->writeText('<comment>Help:</comment>', $options);
  137. $this->writeText("\n");
  138. $this->writeText(' '.str_replace("\n", "\n ", $help), $options);
  139. $this->writeText("\n");
  140. }
  141. }
  142. /**
  143. * {@inheritdoc}
  144. */
  145. protected function describeApplication(Application $application, array $options = array())
  146. {
  147. $describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
  148. $description = new ApplicationDescription($application, $describedNamespace);
  149. if (isset($options['raw_text']) && $options['raw_text']) {
  150. $width = $this->getColumnWidth($description->getCommands());
  151. foreach ($description->getCommands() as $command) {
  152. $this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options);
  153. $this->writeText("\n");
  154. }
  155. } else {
  156. if ('' != $help = $application->getHelp()) {
  157. $this->writeText("$help\n\n", $options);
  158. }
  159. $this->writeText("<comment>Usage:</comment>\n", $options);
  160. $this->writeText(" command [options] [arguments]\n\n", $options);
  161. $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options);
  162. $this->writeText("\n");
  163. $this->writeText("\n");
  164. $width = $this->getColumnWidth($description->getCommands());
  165. if ($describedNamespace) {
  166. $this->writeText(sprintf('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
  167. } else {
  168. $this->writeText('<comment>Available commands:</comment>', $options);
  169. }
  170. // add commands by namespace
  171. foreach ($description->getNamespaces() as $namespace) {
  172. if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
  173. $this->writeText("\n");
  174. $this->writeText(' <comment>'.$namespace['id'].'</comment>', $options);
  175. }
  176. foreach ($namespace['commands'] as $name) {
  177. $this->writeText("\n");
  178. $spacingWidth = $width - strlen($name);
  179. $this->writeText(sprintf(' <info>%s</info>%s%s', $name, str_repeat(' ', $spacingWidth), $description->getCommand($name)->getDescription()), $options);
  180. }
  181. }
  182. $this->writeText("\n");
  183. }
  184. }
  185. /**
  186. * {@inheritdoc}
  187. */
  188. private function writeText($content, array $options = array())
  189. {
  190. $this->write(
  191. isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
  192. isset($options['raw_output']) ? !$options['raw_output'] : true
  193. );
  194. }
  195. /**
  196. * Formats input option/argument default value.
  197. *
  198. * @param mixed $default
  199. *
  200. * @return string
  201. */
  202. private function formatDefaultValue($default)
  203. {
  204. if (PHP_VERSION_ID < 50400) {
  205. return str_replace('\/', '/', json_encode($default));
  206. }
  207. return json_encode($default, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
  208. }
  209. /**
  210. * @param Command[] $commands
  211. *
  212. * @return int
  213. */
  214. private function getColumnWidth(array $commands)
  215. {
  216. $widths = array();
  217. foreach ($commands as $command) {
  218. $widths[] = strlen($command->getName());
  219. foreach ($command->getAliases() as $alias) {
  220. $widths[] = strlen($alias);
  221. }
  222. }
  223. return max($widths) + 2;
  224. }
  225. /**
  226. * @param InputOption[] $options
  227. *
  228. * @return int
  229. */
  230. private function calculateTotalWidthForOptions($options)
  231. {
  232. $totalWidth = 0;
  233. foreach ($options as $option) {
  234. // "-" + shortcut + ", --" + name
  235. $nameLength = 1 + max(strlen($option->getShortcut()), 1) + 4 + strlen($option->getName());
  236. if ($option->acceptValue()) {
  237. $valueLength = 1 + strlen($option->getName()); // = + value
  238. $valueLength += $option->isValueOptional() ? 2 : 0; // [ + ]
  239. $nameLength += $valueLength;
  240. }
  241. $totalWidth = max($totalWidth, $nameLength);
  242. }
  243. return $totalWidth;
  244. }
  245. }