Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b813a05

Browse files
ro0NLfabpot
authored andcommitted
[FrameworkBundle] Debug container environment variables
1 parent 041f60f commit b813a05

File tree

9 files changed

+235
-7
lines changed

9 files changed

+235
-7
lines changed

src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ protected function configure()
5858
new InputOption('parameter', null, InputOption::VALUE_REQUIRED, 'Displays a specific parameter for an application'),
5959
new InputOption('parameters', null, InputOption::VALUE_NONE, 'Displays parameters for an application'),
6060
new InputOption('types', null, InputOption::VALUE_NONE, 'Displays types (classes/interfaces) available in the container'),
61+
new InputOption('env-var', null, InputOption::VALUE_REQUIRED, 'Displays a specific environment variable used in the container'),
62+
new InputOption('env-vars', null, InputOption::VALUE_NONE, 'Displays environment variables used in the container'),
6163
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'),
6264
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'),
6365
])
@@ -75,6 +77,14 @@ protected function configure()
7577
7678
<info>php %command.full_name% --types</info>
7779
80+
To see environment variables used by the container, use the <info>--env-vars</info> flag:
81+
82+
<info>php %command.full_name% --env-vars</info>
83+
84+
Display a specific environment variable by specifying its name with the <info>--env-var</info> option:
85+
86+
<info>php %command.full_name% --env-var=APP_ENV</info>
87+
7888
Use the --tags option to display tagged <comment>public</comment> services grouped by tag:
7989
8090
<info>php %command.full_name% --tags</info>
@@ -116,7 +126,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
116126
$this->validateInput($input);
117127
$object = $this->getContainerBuilder();
118128

119-
if ($input->getOption('types')) {
129+
if ($input->getOption('env-vars')) {
130+
$options = ['env-vars' => true];
131+
} elseif ($envVar = $input->getOption('env-var')) {
132+
$options = ['env-vars' => true, 'name' => $envVar];
133+
} elseif ($input->getOption('types')) {
120134
$options = [];
121135
$options['filter'] = [$this, 'filterToServiceTypes'];
122136
} elseif ($input->getOption('parameters')) {
@@ -156,7 +170,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
156170
throw $e;
157171
}
158172

159-
if (!$input->getArgument('name') && !$input->getOption('tag') && !$input->getOption('parameter') && $input->isInteractive()) {
173+
if (!$input->getArgument('name') && !$input->getOption('tag') && !$input->getOption('parameter') && !$input->getOption('env-vars') && !$input->getOption('env-var') && $input->isInteractive()) {
160174
if ($input->getOption('tags')) {
161175
$errorIo->comment('To search for a specific tag, re-run this command with a search term. (e.g. <comment>debug:container --tag=form.type</comment>)');
162176
} elseif ($input->getOption('parameters')) {
@@ -209,12 +223,15 @@ protected function getContainerBuilder()
209223
if (!$kernel->isDebug() || !(new ConfigCache($kernel->getContainer()->getParameter('debug.container.dump'), true))->isFresh()) {
210224
$buildContainer = \Closure::bind(function () { return $this->buildContainer(); }, $kernel, \get_class($kernel));
211225
$container = $buildContainer();
212-
$container->getCompilerPassConfig()->setRemovingPasses([]);
213-
$container->compile();
214226
} else {
215227
(new XmlFileLoader($container = new ContainerBuilder(), new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump'));
228+
$container->setParameter('container.build_hash', $hash = ContainerBuilder::hash(__METHOD__));
229+
$container->setParameter('container.build_id', hash('crc32', $hash.time()));
216230
}
217231

232+
$container->getCompilerPassConfig()->setRemovingPasses([]);
233+
$container->compile();
234+
218235
return $this->containerBuilder = $container;
219236
}
220237

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ public function describe(OutputInterface $output, $object, array $options = [])
5252
case $object instanceof ParameterBag:
5353
$this->describeContainerParameters($object, $options);
5454
break;
55+
case $object instanceof ContainerBuilder && !empty($options['env-vars']):
56+
$this->describeContainerEnvVars($this->getContainerEnvVars($object), $options);
57+
break;
5558
case $object instanceof ContainerBuilder && isset($options['group_by']) && 'tags' === $options['group_by']:
5659
$this->describeContainerTags($object, $options);
5760
break;
@@ -157,6 +160,11 @@ abstract protected function describeContainerAlias(Alias $alias, array $options
157160
*/
158161
abstract protected function describeContainerParameter($parameter, array $options = []);
159162

163+
/**
164+
* Describes container environment variables.
165+
*/
166+
abstract protected function describeContainerEnvVars(array $envs, array $options = []);
167+
160168
/**
161169
* Describes event dispatcher listeners.
162170
*
@@ -311,4 +319,35 @@ public static function getClassDescription(string $class, string &$resolvedClass
311319

312320
return '';
313321
}
322+
323+
private function getContainerEnvVars(ContainerBuilder $container): array
324+
{
325+
$getEnvReflection = new \ReflectionMethod($container, 'getEnv');
326+
$getEnvReflection->setAccessible(true);
327+
$envs = [];
328+
foreach (array_keys($container->getEnvCounters()) as $env) {
329+
$processor = 'string';
330+
if (false !== $i = strrpos($name = $env, ':')) {
331+
$name = substr($env, $i + 1);
332+
$processor = substr($env, 0, $i);
333+
}
334+
$defaultValue = ($hasDefault = $container->hasParameter("env($name)")) ? $container->getParameter("env($name)") : null;
335+
if (false === ($runtimeValue = $_ENV[$name] ?? $_SERVER[$name] ?? getenv($name))) {
336+
$runtimeValue = null;
337+
}
338+
$processedValue = ($hasRuntime = null !== $runtimeValue) || $hasDefault ? $getEnvReflection->invoke($container, $env) : null;
339+
$envs[$name.$processor] = [
340+
'name' => $name,
341+
'processor' => $processor,
342+
'default_available' => $hasDefault,
343+
'default_value' => $defaultValue,
344+
'runtime_available' => $hasRuntime,
345+
'runtime_value' => $runtimeValue,
346+
'processed_value' => $processedValue,
347+
];
348+
}
349+
ksort($envs);
350+
351+
return array_values($envs);
352+
}
314353
}

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
1313

14+
use Symfony\Component\Console\Exception\LogicException;
1415
use Symfony\Component\DependencyInjection\Alias;
1516
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1617
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
@@ -177,6 +178,14 @@ protected function describeContainerParameter($parameter, array $options = [])
177178
$this->writeData([$key => $parameter], $options);
178179
}
179180

181+
/**
182+
* {@inheritdoc}
183+
*/
184+
protected function describeContainerEnvVars(array $envs, array $options = [])
185+
{
186+
throw new LogicException('Using the JSON format to debug environment variables is not supported.');
187+
}
188+
180189
/**
181190
* Writes data as json.
182191
*

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
1313

14+
use Symfony\Component\Console\Exception\LogicException;
1415
use Symfony\Component\DependencyInjection\Alias;
1516
use Symfony\Component\DependencyInjection\ContainerBuilder;
1617
use Symfony\Component\DependencyInjection\Definition;
@@ -273,6 +274,14 @@ protected function describeContainerParameter($parameter, array $options = [])
273274
$this->write(isset($options['parameter']) ? sprintf("%s\n%s\n\n%s", $options['parameter'], str_repeat('=', \strlen($options['parameter'])), $this->formatParameter($parameter)) : $parameter);
274275
}
275276

277+
/**
278+
* {@inheritdoc}
279+
*/
280+
protected function describeContainerEnvVars(array $envs, array $options = [])
281+
{
282+
throw new LogicException('Using the markdown format to debug environment variables is not supported.');
283+
}
284+
276285
/**
277286
* {@inheritdoc}
278287
*/

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
1313

1414
use Symfony\Component\Console\Formatter\OutputFormatter;
15+
use Symfony\Component\Console\Helper\Dumper;
1516
use Symfony\Component\Console\Helper\Table;
1617
use Symfony\Component\Console\Style\SymfonyStyle;
1718
use Symfony\Component\DependencyInjection\Alias;
@@ -384,6 +385,71 @@ protected function describeContainerParameter($parameter, array $options = [])
384385
]);
385386
}
386387

388+
/**
389+
* {@inheritdoc}
390+
*/
391+
protected function describeContainerEnvVars(array $envs, array $options = [])
392+
{
393+
$dump = new Dumper($this->output);
394+
$options['output']->title('Symfony Container Environment Variables');
395+
396+
if (null !== $name = $options['name'] ?? null) {
397+
$options['output']->comment('Displaying detailed environment variable usage matching '.$name);
398+
399+
$matches = false;
400+
foreach ($envs as $env) {
401+
if ($name === $env['name'] || false !== stripos($env['name'], $name)) {
402+
$matches = true;
403+
$options['output']->section('%env('.$env['processor'].':'.$env['name'].')%');
404+
$options['output']->table([], [
405+
['<info>Default value</>', $env['default_available'] ? $dump($env['default_value']) : 'n/a'],
406+
['<info>Real value</>', $env['runtime_available'] ? $dump($env['runtime_value']) : 'n/a'],
407+
['<info>Processed value</>', $env['default_available'] || $env['runtime_available'] ? $dump($env['processed_value']) : 'n/a'],
408+
]);
409+
}
410+
}
411+
412+
if (!$matches) {
413+
$options['output']->block('None of the environment variables match this name.');
414+
} else {
415+
$options['output']->comment('Note real values might be different between web and CLI.');
416+
}
417+
418+
return;
419+
}
420+
421+
if (!$envs) {
422+
$options['output']->block('No environment variables are being used.');
423+
424+
return;
425+
}
426+
427+
$rows = [];
428+
$missing = [];
429+
foreach ($envs as $env) {
430+
if (isset($rows[$env['name']])) {
431+
continue;
432+
}
433+
434+
$rows[$env['name']] = [
435+
$env['name'],
436+
$env['default_available'] ? $dump($env['default_value']) : 'n/a',
437+
$env['runtime_available'] ? $dump($env['runtime_value']) : 'n/a',
438+
];
439+
if (!$env['default_available'] && !$env['runtime_available']) {
440+
$missing[$env['name']] = true;
441+
}
442+
}
443+
444+
$options['output']->table(['Name', 'Default value', 'Real value'], $rows);
445+
$options['output']->comment('Note real values might be different between web and CLI.');
446+
447+
if ($missing) {
448+
$options['output']->warning('The following variables are missing:');
449+
$options['output']->listing(array_keys($missing));
450+
}
451+
}
452+
387453
/**
388454
* {@inheritdoc}
389455
*/

src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
1313

14+
use Symfony\Component\Console\Exception\LogicException;
1415
use Symfony\Component\DependencyInjection\Alias;
1516
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1617
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
@@ -131,6 +132,14 @@ protected function describeContainerParameter($parameter, array $options = [])
131132
$this->writeDocument($this->getContainerParameterDocument($parameter, $options));
132133
}
133134

135+
/**
136+
* {@inheritdoc}
137+
*/
138+
protected function describeContainerEnvVars(array $envs, array $options = [])
139+
{
140+
throw new LogicException('Using the XML format to debug environment variables is not supported.');
141+
}
142+
134143
/**
135144
* Writes DOM document.
136145
*

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,75 @@ public function testIgnoreBackslashWhenFindingService(string $validServiceId)
8080
$this->assertNotContains('No services found', $tester->getDisplay());
8181
}
8282

83+
public function testDescribeEnvVars()
84+
{
85+
putenv('REAL=value');
86+
static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml']);
87+
88+
$application = new Application(static::$kernel);
89+
$application->setAutoExit(false);
90+
91+
$tester = new ApplicationTester($application);
92+
$tester->run(['command' => 'debug:container', '--env-vars' => true], ['decorated' => false]);
93+
94+
$this->assertStringMatchesFormat(<<<'TXT'
95+
96+
Symfony Container Environment Variables
97+
=======================================
98+
99+
--------- ----------------- ------------
100+
Name Default value Real value
101+
--------- ----------------- ------------
102+
JSON "[1, "2.5", 3]" n/a
103+
REAL n/a "value"
104+
UNKNOWN n/a n/a
105+
--------- ----------------- ------------
106+
107+
// Note real values might be different between web and CLI.%w
108+
109+
[WARNING] The following variables are missing:%w
110+
111+
* UNKNOWN
112+
113+
TXT
114+
, $tester->getDisplay(true));
115+
116+
putenv('REAL');
117+
}
118+
119+
public function testDescribeEnvVar()
120+
{
121+
static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml']);
122+
123+
$application = new Application(static::$kernel);
124+
$application->setAutoExit(false);
125+
126+
$tester = new ApplicationTester($application);
127+
$tester->run(['command' => 'debug:container', '--env-var' => 'js'], ['decorated' => false]);
128+
129+
$this->assertContains(<<<'TXT'
130+
%env(float:key:2:json:JSON)%
131+
----------------------------
132+
133+
----------------- -----------------
134+
Default value "[1, "2.5", 3]"
135+
Real value n/a
136+
Processed value 3.0
137+
----------------- -----------------
138+
139+
%env(int:key:2:json:JSON)%
140+
--------------------------
141+
142+
----------------- -----------------
143+
Default value "[1, "2.5", 3]"
144+
Real value n/a
145+
Processed value 3
146+
----------------- -----------------
147+
148+
TXT
149+
, $tester->getDisplay(true));
150+
}
151+
83152
public function provideIgnoreBackslashWhenFindingService()
84153
{
85154
return [

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
imports:
22
- { resource: ../config/default.yml }
33

4+
parameters:
5+
env(JSON): '[1, "2.5", 3]'
6+
47
services:
58
_defaults: { public: true }
69
public:
@@ -10,3 +13,10 @@ services:
1013
public: false
1114
Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BackslashClass:
1215
class: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BackslashClass
16+
env:
17+
class: stdClass
18+
arguments:
19+
- '%env(UNKNOWN)%'
20+
- '%env(REAL)%'
21+
- '%env(int:key:2:json:JSON)%'
22+
- '%env(float:key:2:json:JSON)%'

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"fig/link-util": "^1.0",
3535
"symfony/asset": "~3.4|~4.0",
3636
"symfony/browser-kit": "^4.3",
37-
"symfony/console": "~3.4|~4.0",
37+
"symfony/console": "^4.3",
3838
"symfony/css-selector": "~3.4|~4.0",
3939
"symfony/dom-crawler": "~3.4|~4.0",
4040
"symfony/polyfill-intl-icu": "~1.0",
@@ -53,7 +53,7 @@
5353
"symfony/templating": "~3.4|~4.0",
5454
"symfony/twig-bundle": "~2.8|~3.2|~4.0",
5555
"symfony/validator": "^4.1",
56-
"symfony/var-dumper": "~3.4|~4.0",
56+
"symfony/var-dumper": "^4.3",
5757
"symfony/workflow": "^4.3",
5858
"symfony/yaml": "~3.4|~4.0",
5959
"symfony/property-info": "~3.4|~4.0",
@@ -69,7 +69,7 @@
6969
"phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
7070
"symfony/asset": "<3.4",
7171
"symfony/browser-kit": "<4.3",
72-
"symfony/console": "<3.4",
72+
"symfony/console": "<4.3",
7373
"symfony/dotenv": "<4.2",
7474
"symfony/form": "<4.3",
7575
"symfony/messenger": "<4.3",

0 commit comments

Comments
 (0)