ContainerDebugCommand.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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\Command;
  11. use Symfony\Component\Console\Input\InputArgument;
  12. use Symfony\Component\Console\Input\InputOption;
  13. use Symfony\Component\Console\Input\InputInterface;
  14. use Symfony\Component\Console\Output\OutputInterface;
  15. use Symfony\Component\Console\Output\Output;
  16. use Symfony\Component\DependencyInjection\Alias;
  17. use Symfony\Component\DependencyInjection\Definition;
  18. use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  19. use Symfony\Component\DependencyInjection\ContainerBuilder;
  20. use Symfony\Component\Config\FileLocator;
  21. /**
  22. * A console command for retrieving information about services
  23. *
  24. * @author Ryan Weaver <ryan@thatsquality.com>
  25. */
  26. class ContainerDebugCommand extends Command
  27. {
  28. /**
  29. * @var ContainerBuilder
  30. */
  31. private $containerBuilder;
  32. /**
  33. * @see Command
  34. */
  35. protected function configure()
  36. {
  37. $this
  38. ->setDefinition(array(
  39. new InputArgument('name', InputArgument::OPTIONAL, 'A service name (foo) or search (foo*)'),
  40. new InputOption('show-private', null, InputOption::VALUE_NONE, 'Use to show public *and* private services'),
  41. ))
  42. ->setName('container:debug')
  43. ->setDescription('Displays current services for an application')
  44. ->setHelp(<<<EOF
  45. The <info>container:debug</info> command displays all configured <comment>public</comment> services:
  46. <info>container:debug</info>
  47. To get specific information about a service, specify its name:
  48. <info>container:debug validator</info>
  49. By default, private services are hidden. You can display all services by
  50. using the --show-private flag:
  51. <info>container:debug --show-private</info>
  52. EOF
  53. )
  54. ;
  55. }
  56. /**
  57. * @see Command
  58. */
  59. protected function execute(InputInterface $input, OutputInterface $output)
  60. {
  61. $name = $input->getArgument('name');
  62. $this->containerBuilder = $this->getContainerBuilder();
  63. $serviceIds = $this->containerBuilder->getServiceIds();
  64. // sort so that it reads like an index of services
  65. asort($serviceIds);
  66. if ($name) {
  67. $this->outputService($output, $name);
  68. } else {
  69. $this->outputServices($output, $serviceIds, $input->getOption('show-private'));
  70. }
  71. }
  72. protected function outputServices(OutputInterface $output, $serviceIds, $showPrivate = false)
  73. {
  74. // set the label to specify public or public+private
  75. if ($showPrivate) {
  76. $label = '<comment>Public</comment> and <comment>private</comment> services';
  77. } else {
  78. $label = '<comment>Public</comment> services';
  79. }
  80. $output->writeln($this->getHelper('formatter')->formatSection('container', $label));
  81. // loop through to get space needed and filter private services
  82. $maxName = 4;
  83. $maxScope = 6;
  84. foreach ($serviceIds as $key => $serviceId) {
  85. $definition = $this->resolveServiceDefinition($serviceId);
  86. if ($definition instanceof Definition) {
  87. // filter out private services unless shown explicitly
  88. if (!$showPrivate && !$definition->isPublic()) {
  89. unset($serviceIds[$key]);
  90. continue;
  91. }
  92. if (strlen($definition->getScope()) > $maxScope) {
  93. $maxScope = strlen($definition->getScope());
  94. }
  95. }
  96. if (strlen($serviceId) > $maxName) {
  97. $maxName = strlen($serviceId);
  98. }
  99. }
  100. $format = '%-'.$maxName.'s %-'.$maxScope.'s %s';
  101. // the title field needs extra space to make up for comment tags
  102. $format1 = '%-'.($maxName + 19).'s %-'.($maxScope + 19).'s %s';
  103. $output->writeln(sprintf($format1, '<comment>Name</comment>', '<comment>Scope</comment>', '<comment>Class Name</comment>'));
  104. foreach ($serviceIds as $serviceId) {
  105. $definition = $this->resolveServiceDefinition($serviceId);
  106. if ($definition instanceof Definition) {
  107. $output->writeln(sprintf($format, $serviceId, $definition->getScope(), $definition->getClass()));
  108. } elseif ($definition instanceof Alias) {
  109. $alias = $definition;
  110. $output->writeln(sprintf($format, $serviceId, 'n/a', sprintf('<comment>alias for</comment> <info>%s</info>', (string) $alias)));
  111. } else {
  112. // we have no information (happens with "service_container")
  113. $service = $definition;
  114. $output->writeln(sprintf($format, $serviceId, '', get_class($service)));
  115. }
  116. }
  117. }
  118. /**
  119. * Renders detailed service information about one service
  120. */
  121. protected function outputService(OutputInterface $output, $serviceId)
  122. {
  123. $definition = $this->resolveServiceDefinition($serviceId);
  124. $label = sprintf('Information for service <info>%s</info>', $serviceId);
  125. $output->writeln($this->getHelper('formatter')->formatSection('container', $label));
  126. $output->writeln('');
  127. if ($definition instanceof Definition) {
  128. $output->writeln(sprintf('<comment>Service Id</comment> %s', $serviceId));
  129. $output->writeln(sprintf('<comment>Class</comment> %s', $definition->getClass()));
  130. $tags = $definition->getTags() ? implode(', ', array_keys($definition->getTags())) : '-';
  131. $output->writeln(sprintf('<comment>Tags</comment> %s', $tags));
  132. $output->writeln(sprintf('<comment>Scope</comment> %s', $definition->getScope()));
  133. $public = $definition->isPublic() ? 'yes' : 'no';
  134. $output->writeln(sprintf('<comment>Public</comment> %s', $public));
  135. } elseif ($definition instanceof Alias) {
  136. $alias = $definition;
  137. $output->writeln(sprintf('This service is an alias for the service <info>%s</info>', (string) $alias));
  138. } else {
  139. // edge case (but true for "service_container", all we have is the service itself
  140. $service = $definition;
  141. $output->writeln(sprintf('<comment>Service Id</comment> %s', $serviceId));
  142. $output->writeln(sprintf('<comment>Class</comment> %s', get_class($service)));
  143. }
  144. }
  145. /**
  146. * Loads the ContainerBuilder from the cache.
  147. *
  148. * @return ContainerBuilder
  149. */
  150. private function getContainerBuilder()
  151. {
  152. if (!$this->getApplication()->getKernel()->isDebug()) {
  153. throw new \LogicException(sprintf('Debug information about the container is only available in debug mode.'));
  154. }
  155. if (!file_exists($cachedFile = $this->container->getParameter('debug.container.dump'))) {
  156. throw new \LogicException(sprintf('Debug information about the container could not be found. Please clear the cache and try again.'));
  157. }
  158. $container = new ContainerBuilder();
  159. $loader = new XmlFileLoader($container, new FileLocator());
  160. $loader->load($cachedFile);
  161. return $container;
  162. }
  163. /**
  164. * Given an array of service IDs, this returns the array of corresponding
  165. * Definition and Alias objects that those ids represent.
  166. *
  167. * @param string $serviceId The service id to resolve
  168. * @return \Symfony\Component\DependencyInjection\Definition|\Symfony\Component\DependencyInjection\Alias
  169. */
  170. private function resolveServiceDefinition($serviceId)
  171. {
  172. if ($this->containerBuilder->hasDefinition($serviceId)) {
  173. return $this->containerBuilder->getDefinition($serviceId);
  174. }
  175. // Some service IDs don't have a Definition, they're simply an Alias
  176. if ($this->containerBuilder->hasAlias($serviceId)) {
  177. return $this->containerBuilder->getAlias($serviceId);
  178. }
  179. // the service has been injected in some special way, just return the service
  180. return $this->containerBuilder->get($serviceId);
  181. }
  182. }