MarkdownDescriptor.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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\Bundle\FrameworkBundle\Console\Descriptor;
  11. use Symfony\Component\DependencyInjection\Alias;
  12. use Symfony\Component\DependencyInjection\ContainerBuilder;
  13. use Symfony\Component\DependencyInjection\Definition;
  14. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
  15. use Symfony\Component\DependencyInjection\Reference;
  16. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  17. use Symfony\Component\Routing\Route;
  18. use Symfony\Component\Routing\RouteCollection;
  19. /**
  20. * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
  21. *
  22. * @internal
  23. */
  24. class MarkdownDescriptor extends Descriptor
  25. {
  26. /**
  27. * {@inheritdoc}
  28. */
  29. protected function describeRouteCollection(RouteCollection $routes, array $options = array())
  30. {
  31. $first = true;
  32. foreach ($routes->all() as $name => $route) {
  33. if ($first) {
  34. $first = false;
  35. } else {
  36. $this->write("\n\n");
  37. }
  38. $this->describeRoute($route, array('name' => $name));
  39. }
  40. $this->write("\n");
  41. }
  42. /**
  43. * {@inheritdoc}
  44. */
  45. protected function describeRoute(Route $route, array $options = array())
  46. {
  47. $requirements = $route->getRequirements();
  48. unset($requirements['_scheme'], $requirements['_method']);
  49. $output = '- Path: '.$route->getPath()
  50. ."\n".'- Path Regex: '.$route->compile()->getRegex()
  51. ."\n".'- Host: '.('' !== $route->getHost() ? $route->getHost() : 'ANY')
  52. ."\n".'- Host Regex: '.('' !== $route->getHost() ? $route->compile()->getHostRegex() : '')
  53. ."\n".'- Scheme: '.($route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY')
  54. ."\n".'- Method: '.($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY')
  55. ."\n".'- Class: '.\get_class($route)
  56. ."\n".'- Defaults: '.$this->formatRouterConfig($route->getDefaults())
  57. ."\n".'- Requirements: '.($requirements ? $this->formatRouterConfig($requirements) : 'NO CUSTOM')
  58. ."\n".'- Options: '.$this->formatRouterConfig($route->getOptions());
  59. $this->write(isset($options['name'])
  60. ? $options['name']."\n".str_repeat('-', \strlen($options['name']))."\n\n".$output
  61. : $output);
  62. $this->write("\n");
  63. }
  64. /**
  65. * {@inheritdoc}
  66. */
  67. protected function describeContainerParameters(ParameterBag $parameters, array $options = array())
  68. {
  69. $this->write("Container parameters\n====================\n");
  70. foreach ($this->sortParameters($parameters) as $key => $value) {
  71. $this->write(sprintf("\n- `%s`: `%s`", $key, $this->formatParameter($value)));
  72. }
  73. }
  74. /**
  75. * {@inheritdoc}
  76. */
  77. protected function describeContainerTags(ContainerBuilder $builder, array $options = array())
  78. {
  79. $showPrivate = isset($options['show_private']) && $options['show_private'];
  80. $this->write("Container tags\n==============");
  81. foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
  82. $this->write("\n\n".$tag."\n".str_repeat('-', \strlen($tag)));
  83. foreach ($definitions as $serviceId => $definition) {
  84. $this->write("\n\n");
  85. $this->describeContainerDefinition($definition, array('omit_tags' => true, 'id' => $serviceId));
  86. }
  87. }
  88. }
  89. /**
  90. * {@inheritdoc}
  91. */
  92. protected function describeContainerService($service, array $options = array())
  93. {
  94. if (!isset($options['id'])) {
  95. throw new \InvalidArgumentException('An "id" option must be provided.');
  96. }
  97. $childOptions = array('id' => $options['id'], 'as_array' => true);
  98. if ($service instanceof Alias) {
  99. $this->describeContainerAlias($service, $childOptions);
  100. } elseif ($service instanceof Definition) {
  101. $this->describeContainerDefinition($service, $childOptions);
  102. } else {
  103. $this->write(sprintf('**`%s`:** `%s`', $options['id'], \get_class($service)));
  104. }
  105. }
  106. /**
  107. * {@inheritdoc}
  108. */
  109. protected function describeContainerServices(ContainerBuilder $builder, array $options = array())
  110. {
  111. $showPrivate = isset($options['show_private']) && $options['show_private'];
  112. $title = $showPrivate ? 'Public and private services' : 'Public services';
  113. if (isset($options['tag'])) {
  114. $title .= ' with tag `'.$options['tag'].'`';
  115. }
  116. $this->write($title."\n".str_repeat('=', \strlen($title)));
  117. $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds();
  118. $showPrivate = isset($options['show_private']) && $options['show_private'];
  119. $services = array('definitions' => array(), 'aliases' => array(), 'services' => array());
  120. foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
  121. $service = $this->resolveServiceDefinition($builder, $serviceId);
  122. if ($service instanceof Alias) {
  123. $services['aliases'][$serviceId] = $service;
  124. } elseif ($service instanceof Definition) {
  125. if (($showPrivate || $service->isPublic())) {
  126. $services['definitions'][$serviceId] = $service;
  127. }
  128. } else {
  129. $services['services'][$serviceId] = $service;
  130. }
  131. }
  132. if (!empty($services['definitions'])) {
  133. $this->write("\n\nDefinitions\n-----------\n");
  134. foreach ($services['definitions'] as $id => $service) {
  135. $this->write("\n");
  136. $this->describeContainerDefinition($service, array('id' => $id));
  137. }
  138. }
  139. if (!empty($services['aliases'])) {
  140. $this->write("\n\nAliases\n-------\n");
  141. foreach ($services['aliases'] as $id => $service) {
  142. $this->write("\n");
  143. $this->describeContainerAlias($service, array('id' => $id));
  144. }
  145. }
  146. if (!empty($services['services'])) {
  147. $this->write("\n\nServices\n--------\n");
  148. foreach ($services['services'] as $id => $service) {
  149. $this->write("\n");
  150. $this->write(sprintf('- `%s`: `%s`', $id, \get_class($service)));
  151. }
  152. }
  153. }
  154. /**
  155. * {@inheritdoc}
  156. */
  157. protected function describeContainerDefinition(Definition $definition, array $options = array())
  158. {
  159. $output = '- Class: `'.$definition->getClass().'`'
  160. ."\n".'- Scope: `'.$definition->getScope(false).'`'
  161. ."\n".'- Public: '.($definition->isPublic() ? 'yes' : 'no')
  162. ."\n".'- Synthetic: '.($definition->isSynthetic() ? 'yes' : 'no')
  163. ."\n".'- Lazy: '.($definition->isLazy() ? 'yes' : 'no')
  164. ."\n".'- Shared: '.($definition->isShared() ? 'yes' : 'no')
  165. ."\n".'- Synchronized: '.($definition->isSynchronized(false) ? 'yes' : 'no')
  166. ."\n".'- Abstract: '.($definition->isAbstract() ? 'yes' : 'no')
  167. ."\n".'- Autowired: '.($definition->isAutowired() ? 'yes' : 'no')
  168. ;
  169. foreach ($definition->getAutowiringTypes() as $autowiringType) {
  170. $output .= "\n".'- Autowiring Type: `'.$autowiringType.'`';
  171. }
  172. if ($definition->getFile()) {
  173. $output .= "\n".'- File: `'.$definition->getFile().'`';
  174. }
  175. if ($definition->getFactoryClass(false)) {
  176. $output .= "\n".'- Factory Class: `'.$definition->getFactoryClass(false).'`';
  177. }
  178. if ($definition->getFactoryService(false)) {
  179. $output .= "\n".'- Factory Service: `'.$definition->getFactoryService(false).'`';
  180. }
  181. if ($definition->getFactoryMethod(false)) {
  182. $output .= "\n".'- Factory Method: `'.$definition->getFactoryMethod(false).'`';
  183. }
  184. if ($factory = $definition->getFactory()) {
  185. if (\is_array($factory)) {
  186. if ($factory[0] instanceof Reference) {
  187. $output .= "\n".'- Factory Service: `'.$factory[0].'`';
  188. } elseif ($factory[0] instanceof Definition) {
  189. throw new \InvalidArgumentException('Factory is not describable.');
  190. } else {
  191. $output .= "\n".'- Factory Class: `'.$factory[0].'`';
  192. }
  193. $output .= "\n".'- Factory Method: `'.$factory[1].'`';
  194. } else {
  195. $output .= "\n".'- Factory Function: `'.$factory.'`';
  196. }
  197. }
  198. if (!(isset($options['omit_tags']) && $options['omit_tags'])) {
  199. foreach ($definition->getTags() as $tagName => $tagData) {
  200. foreach ($tagData as $parameters) {
  201. $output .= "\n".'- Tag: `'.$tagName.'`';
  202. foreach ($parameters as $name => $value) {
  203. $output .= "\n".' - '.ucfirst($name).': '.$value;
  204. }
  205. }
  206. }
  207. }
  208. $this->write(isset($options['id']) ? sprintf("### %s\n\n%s\n", $options['id'], $output) : $output);
  209. }
  210. /**
  211. * {@inheritdoc}
  212. */
  213. protected function describeContainerAlias(Alias $alias, array $options = array())
  214. {
  215. $output = '- Service: `'.$alias.'`'
  216. ."\n".'- Public: '.($alias->isPublic() ? 'yes' : 'no');
  217. $this->write(isset($options['id']) ? sprintf("### %s\n\n%s\n", $options['id'], $output) : $output);
  218. }
  219. /**
  220. * {@inheritdoc}
  221. */
  222. protected function describeContainerParameter($parameter, array $options = array())
  223. {
  224. $this->write(isset($options['parameter']) ? sprintf("%s\n%s\n\n%s", $options['parameter'], str_repeat('=', \strlen($options['parameter'])), $this->formatParameter($parameter)) : $parameter);
  225. }
  226. /**
  227. * {@inheritdoc}
  228. */
  229. protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array())
  230. {
  231. $event = array_key_exists('event', $options) ? $options['event'] : null;
  232. $title = 'Registered listeners';
  233. if (null !== $event) {
  234. $title .= sprintf(' for event `%s` ordered by descending priority', $event);
  235. }
  236. $this->write(sprintf('# %s', $title)."\n");
  237. $registeredListeners = $eventDispatcher->getListeners($event);
  238. if (null !== $event) {
  239. foreach ($registeredListeners as $order => $listener) {
  240. $this->write("\n".sprintf('## Listener %d', $order + 1)."\n");
  241. $this->describeCallable($listener);
  242. $this->write(sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($event, $listener))."\n");
  243. }
  244. } else {
  245. ksort($registeredListeners);
  246. foreach ($registeredListeners as $eventListened => $eventListeners) {
  247. $this->write("\n".sprintf('## %s', $eventListened)."\n");
  248. foreach ($eventListeners as $order => $eventListener) {
  249. $this->write("\n".sprintf('### Listener %d', $order + 1)."\n");
  250. $this->describeCallable($eventListener);
  251. $this->write(sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($eventListened, $eventListener))."\n");
  252. }
  253. }
  254. }
  255. }
  256. /**
  257. * {@inheritdoc}
  258. */
  259. protected function describeCallable($callable, array $options = array())
  260. {
  261. $string = '';
  262. if (\is_array($callable)) {
  263. $string .= "\n- Type: `function`";
  264. if (\is_object($callable[0])) {
  265. $string .= "\n".sprintf('- Name: `%s`', $callable[1]);
  266. $string .= "\n".sprintf('- Class: `%s`', \get_class($callable[0]));
  267. } else {
  268. if (0 !== strpos($callable[1], 'parent::')) {
  269. $string .= "\n".sprintf('- Name: `%s`', $callable[1]);
  270. $string .= "\n".sprintf('- Class: `%s`', $callable[0]);
  271. $string .= "\n- Static: yes";
  272. } else {
  273. $string .= "\n".sprintf('- Name: `%s`', substr($callable[1], 8));
  274. $string .= "\n".sprintf('- Class: `%s`', $callable[0]);
  275. $string .= "\n- Static: yes";
  276. $string .= "\n- Parent: yes";
  277. }
  278. }
  279. return $this->write($string."\n");
  280. }
  281. if (\is_string($callable)) {
  282. $string .= "\n- Type: `function`";
  283. if (false === strpos($callable, '::')) {
  284. $string .= "\n".sprintf('- Name: `%s`', $callable);
  285. } else {
  286. $callableParts = explode('::', $callable);
  287. $string .= "\n".sprintf('- Name: `%s`', $callableParts[1]);
  288. $string .= "\n".sprintf('- Class: `%s`', $callableParts[0]);
  289. $string .= "\n- Static: yes";
  290. }
  291. return $this->write($string."\n");
  292. }
  293. if ($callable instanceof \Closure) {
  294. $string .= "\n- Type: `closure`";
  295. return $this->write($string."\n");
  296. }
  297. if (method_exists($callable, '__invoke')) {
  298. $string .= "\n- Type: `object`";
  299. $string .= "\n".sprintf('- Name: `%s`', \get_class($callable));
  300. return $this->write($string."\n");
  301. }
  302. throw new \InvalidArgumentException('Callable is not describable.');
  303. }
  304. /**
  305. * @return string
  306. */
  307. private function formatRouterConfig(array $array)
  308. {
  309. if (!$array) {
  310. return 'NONE';
  311. }
  312. $string = '';
  313. ksort($array);
  314. foreach ($array as $name => $value) {
  315. $string .= "\n".' - `'.$name.'`: '.$this->formatValue($value);
  316. }
  317. return $string;
  318. }
  319. }