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