TextDescriptor.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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\Form\Console\Descriptor;
  11. use Symfony\Component\Console\Helper\Dumper;
  12. use Symfony\Component\Console\Helper\TableSeparator;
  13. use Symfony\Component\Form\ResolvedFormTypeInterface;
  14. use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
  15. use Symfony\Component\OptionsResolver\OptionsResolver;
  16. /**
  17. * @author Yonel Ceruto <yonelceruto@gmail.com>
  18. *
  19. * @internal
  20. */
  21. class TextDescriptor extends Descriptor
  22. {
  23. private $fileLinkFormatter;
  24. public function __construct(FileLinkFormatter $fileLinkFormatter = null)
  25. {
  26. $this->fileLinkFormatter = $fileLinkFormatter;
  27. }
  28. protected function describeDefaults(array $options)
  29. {
  30. if ($options['core_types']) {
  31. $this->output->section('Built-in form types (Symfony\Component\Form\Extension\Core\Type)');
  32. $shortClassNames = array_map(function ($fqcn) {
  33. return $this->formatClassLink($fqcn, \array_slice(explode('\\', $fqcn), -1)[0]);
  34. }, $options['core_types']);
  35. for ($i = 0, $loopsMax = \count($shortClassNames); $i * 5 < $loopsMax; ++$i) {
  36. $this->output->writeln(' '.implode(', ', \array_slice($shortClassNames, $i * 5, 5)));
  37. }
  38. }
  39. if ($options['service_types']) {
  40. $this->output->section('Service form types');
  41. $this->output->listing(array_map([$this, 'formatClassLink'], $options['service_types']));
  42. }
  43. if (!$options['show_deprecated']) {
  44. if ($options['extensions']) {
  45. $this->output->section('Type extensions');
  46. $this->output->listing(array_map([$this, 'formatClassLink'], $options['extensions']));
  47. }
  48. if ($options['guessers']) {
  49. $this->output->section('Type guessers');
  50. $this->output->listing(array_map([$this, 'formatClassLink'], $options['guessers']));
  51. }
  52. }
  53. }
  54. protected function describeResolvedFormType(ResolvedFormTypeInterface $resolvedFormType, array $options = [])
  55. {
  56. $this->collectOptions($resolvedFormType);
  57. if ($options['show_deprecated']) {
  58. $this->filterOptionsByDeprecated($resolvedFormType);
  59. }
  60. $formOptions = $this->normalizeAndSortOptionsColumns(array_filter([
  61. 'own' => $this->ownOptions,
  62. 'overridden' => $this->overriddenOptions,
  63. 'parent' => $this->parentOptions,
  64. 'extension' => $this->extensionOptions,
  65. ]));
  66. // setting headers and column order
  67. $tableHeaders = array_intersect_key([
  68. 'own' => 'Options',
  69. 'overridden' => 'Overridden options',
  70. 'parent' => 'Parent options',
  71. 'extension' => 'Extension options',
  72. ], $formOptions);
  73. $this->output->title(sprintf('%s (Block prefix: "%s")', \get_class($resolvedFormType->getInnerType()), $resolvedFormType->getInnerType()->getBlockPrefix()));
  74. if ($formOptions) {
  75. $this->output->table($tableHeaders, $this->buildTableRows($tableHeaders, $formOptions));
  76. }
  77. if ($this->parents) {
  78. $this->output->section('Parent types');
  79. $this->output->listing(array_map([$this, 'formatClassLink'], $this->parents));
  80. }
  81. if ($this->extensions) {
  82. $this->output->section('Type extensions');
  83. $this->output->listing(array_map([$this, 'formatClassLink'], $this->extensions));
  84. }
  85. }
  86. protected function describeOption(OptionsResolver $optionsResolver, array $options)
  87. {
  88. $definition = $this->getOptionDefinition($optionsResolver, $options['option']);
  89. $dump = new Dumper($this->output);
  90. $map = [];
  91. if ($definition['deprecated']) {
  92. $map = [
  93. 'Deprecated' => 'deprecated',
  94. 'Deprecation package' => 'deprecationPackage',
  95. 'Deprecation version' => 'deprecationVersion',
  96. 'Deprecation message' => 'deprecationMessage',
  97. ];
  98. }
  99. $map += [
  100. 'Info' => 'info',
  101. 'Required' => 'required',
  102. 'Default' => 'default',
  103. 'Allowed types' => 'allowedTypes',
  104. 'Allowed values' => 'allowedValues',
  105. 'Normalizers' => 'normalizers',
  106. ];
  107. $rows = [];
  108. foreach ($map as $label => $name) {
  109. $value = \array_key_exists($name, $definition) ? $dump($definition[$name]) : '-';
  110. if ('default' === $name && isset($definition['lazy'])) {
  111. $value = "Value: $value\n\nClosure(s): ".$dump($definition['lazy']);
  112. }
  113. $rows[] = ["<info>$label</info>", $value];
  114. $rows[] = new TableSeparator();
  115. }
  116. array_pop($rows);
  117. $this->output->title(sprintf('%s (%s)', \get_class($options['type']), $options['option']));
  118. $this->output->table([], $rows);
  119. }
  120. private function buildTableRows(array $headers, array $options): array
  121. {
  122. $tableRows = [];
  123. $count = \count(max($options));
  124. for ($i = 0; $i < $count; ++$i) {
  125. $cells = [];
  126. foreach (array_keys($headers) as $group) {
  127. $option = $options[$group][$i] ?? null;
  128. if (\is_string($option) && \in_array($option, $this->requiredOptions, true)) {
  129. $option .= ' <info>(required)</info>';
  130. }
  131. $cells[] = $option;
  132. }
  133. $tableRows[] = $cells;
  134. }
  135. return $tableRows;
  136. }
  137. private function normalizeAndSortOptionsColumns(array $options): array
  138. {
  139. foreach ($options as $group => $opts) {
  140. $sorted = false;
  141. foreach ($opts as $class => $opt) {
  142. if (\is_string($class)) {
  143. unset($options[$group][$class]);
  144. }
  145. if (!\is_array($opt) || 0 === \count($opt)) {
  146. continue;
  147. }
  148. if (!$sorted) {
  149. $options[$group] = [];
  150. } else {
  151. $options[$group][] = null;
  152. }
  153. $options[$group][] = sprintf('<info>%s</info>', (new \ReflectionClass($class))->getShortName());
  154. $options[$group][] = new TableSeparator();
  155. sort($opt);
  156. $sorted = true;
  157. $options[$group] = array_merge($options[$group], $opt);
  158. }
  159. if (!$sorted) {
  160. sort($options[$group]);
  161. }
  162. }
  163. return $options;
  164. }
  165. private function formatClassLink(string $class, string $text = null): string
  166. {
  167. if (null === $text) {
  168. $text = $class;
  169. }
  170. if ('' === $fileLink = $this->getFileLink($class)) {
  171. return $text;
  172. }
  173. return sprintf('<href=%s>%s</>', $fileLink, $text);
  174. }
  175. private function getFileLink(string $class): string
  176. {
  177. if (null === $this->fileLinkFormatter) {
  178. return '';
  179. }
  180. try {
  181. $r = new \ReflectionClass($class);
  182. } catch (\ReflectionException $e) {
  183. return '';
  184. }
  185. return (string) $this->fileLinkFormatter->format($r->getFileName(), $r->getStartLine());
  186. }
  187. }