|
0
|
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 |
} |