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

Skip to content

Commit c357485

Browse files
committed
[DependencyInjection] Handle env var placeholders in CheckTypeDeclarationsPass
1 parent bfe697b commit c357485

File tree

2 files changed

+108
-5
lines changed

2 files changed

+108
-5
lines changed

src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1515
use Symfony\Component\DependencyInjection\Container;
1616
use Symfony\Component\DependencyInjection\Definition;
17+
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
1718
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1819
use Symfony\Component\DependencyInjection\Exception\InvalidParameterTypeException;
1920
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
2021
use Symfony\Component\DependencyInjection\ExpressionLanguage;
2122
use Symfony\Component\DependencyInjection\Parameter;
23+
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
2224
use Symfony\Component\DependencyInjection\Reference;
2325
use Symfony\Component\DependencyInjection\ServiceLocator;
2426
use Symfony\Component\ExpressionLanguage\Expression;
@@ -104,27 +106,29 @@ private function checkTypeDeclarations(Definition $checkedDefinition, \Reflectio
104106
$reflectionParameters = $reflectionFunction->getParameters();
105107
$checksCount = min($reflectionFunction->getNumberOfParameters(), \count($values));
106108

109+
$envPlaceholderUniquePrefix = $this->container->getParameterBag() instanceof EnvPlaceholderParameterBag ? $this->container->getParameterBag()->getEnvPlaceholderUniquePrefix() : null;
110+
107111
for ($i = 0; $i < $checksCount; ++$i) {
108112
if (!$reflectionParameters[$i]->hasType() || $reflectionParameters[$i]->isVariadic()) {
109113
continue;
110114
}
111115

112-
$this->checkType($checkedDefinition, $values[$i], $reflectionParameters[$i]);
116+
$this->checkType($checkedDefinition, $values[$i], $reflectionParameters[$i], $envPlaceholderUniquePrefix);
113117
}
114118

115119
if ($reflectionFunction->isVariadic() && ($lastParameter = end($reflectionParameters))->hasType()) {
116120
$variadicParameters = \array_slice($values, $lastParameter->getPosition());
117121

118122
foreach ($variadicParameters as $variadicParameter) {
119-
$this->checkType($checkedDefinition, $variadicParameter, $lastParameter);
123+
$this->checkType($checkedDefinition, $variadicParameter, $lastParameter, $envPlaceholderUniquePrefix);
120124
}
121125
}
122126
}
123127

124128
/**
125129
* @throws InvalidParameterTypeException When a parameter is not compatible with the declared type
126130
*/
127-
private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter): void
131+
private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix): void
128132
{
129133
$type = $parameter->getType()->getName();
130134

@@ -178,8 +182,22 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
178182
$value = $this->container->getParameter($value);
179183
} elseif ($value instanceof Expression) {
180184
$value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this->container]);
181-
} elseif (\is_string($value) && '%' === ($value[0] ?? '') && preg_match('/^%([^%]+)%$/', $value, $match)) {
182-
$value = $this->container->getParameter($match[1]);
185+
} elseif (\is_string($value)) {
186+
if ('%' === ($value[0] ?? '') && preg_match('/^%([^%]+)%$/', $value, $match)) {
187+
// Only array parameters are not inlined when dumped.
188+
$value = [];
189+
} elseif ($envPlaceholderUniquePrefix && false !== strpos($value, 'env_')) {
190+
// If the value is an env placeholder that is either mixed with a string or with another env placeholder, then its resolved value will always be a string, so we don't need to resolve it.
191+
// We don't need to change the value because it is already a string.
192+
if ('' === preg_replace('/'.$envPlaceholderUniquePrefix.'_\w+_[a-f0-9]{32}/U', '', $value, -1, $c) && 1 === $c) {
193+
try {
194+
$value = $this->container->resolveEnvPlaceholders($value, true);
195+
} catch (EnvNotFoundException | RuntimeException $e) {
196+
// If an env placeholder cannot be resolved, we skip the validation.
197+
return;
198+
}
199+
}
200+
}
183201
}
184202

185203
if (null === $value && $parameter->allowsNull()) {

src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1616
use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass;
17+
use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass;
1718
use Symfony\Component\DependencyInjection\ContainerBuilder;
1819
use Symfony\Component\DependencyInjection\Definition;
20+
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
1921
use Symfony\Component\DependencyInjection\Reference;
2022
use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Bar;
2123
use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall;
@@ -571,6 +573,20 @@ public function testProcessThrowsOnIterableTypeWhenScalarPassed()
571573
$this->assertInstanceOf(\stdClass::class, $container->get('bar')->foo);
572574
}
573575

576+
public function testProcessResolveArrayParameters()
577+
{
578+
$container = new ContainerBuilder();
579+
$container->setParameter('ccc', ['foobar']);
580+
581+
$container
582+
->register('foobar', BarMethodCall::class)
583+
->addMethodCall('setArray', ['%ccc%']);
584+
585+
(new CheckTypeDeclarationsPass(true))->process($container);
586+
587+
$this->addToAssertionCount(1);
588+
}
589+
574590
public function testProcessResolveExpressions()
575591
{
576592
$container = new ContainerBuilder();
@@ -584,4 +600,73 @@ public function testProcessResolveExpressions()
584600

585601
$this->addToAssertionCount(1);
586602
}
603+
604+
public function testProcessHandleMixedEnvPlaceholder()
605+
{
606+
$this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
607+
$this->expectExceptionMessage('Invalid definition for service "foobar": argument 1 of "Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall::setArray" accepts "array", "string" passed.');
608+
609+
$container = new ContainerBuilder(new EnvPlaceholderParameterBag([
610+
'ccc' => '%env(FOO)%',
611+
]));
612+
613+
$container
614+
->register('foobar', BarMethodCall::class)
615+
->addMethodCall('setArray', ['foo%ccc%']);
616+
617+
(new CheckTypeDeclarationsPass(true))->process($container);
618+
}
619+
620+
public function testProcessHandleMultipleEnvPlaceholder()
621+
{
622+
$this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
623+
$this->expectExceptionMessage('Invalid definition for service "foobar": argument 1 of "Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall::setArray" accepts "array", "string" passed.');
624+
625+
$container = new ContainerBuilder(new EnvPlaceholderParameterBag([
626+
'ccc' => '%env(FOO)%',
627+
'fcy' => '%env(int:BAR)%',
628+
]));
629+
630+
$container
631+
->register('foobar', BarMethodCall::class)
632+
->addMethodCall('setArray', ['%ccc%%fcy%']);
633+
634+
(new CheckTypeDeclarationsPass(true))->process($container);
635+
}
636+
637+
public function testProcessHandleExistingEnvPlaceholder()
638+
{
639+
putenv('ARRAY={"foo":"bar"}');
640+
641+
$container = new ContainerBuilder(new EnvPlaceholderParameterBag([
642+
'ccc' => '%env(json:ARRAY)%',
643+
]));
644+
645+
$container
646+
->register('foobar', BarMethodCall::class)
647+
->addMethodCall('setArray', ['%ccc%']);
648+
649+
(new ResolveParameterPlaceHoldersPass())->process($container);
650+
(new CheckTypeDeclarationsPass(true))->process($container);
651+
652+
$this->addToAssertionCount(1);
653+
654+
putenv('ARRAY=');
655+
}
656+
657+
public function testProcessHandleNotFoundEnvPlaceholder()
658+
{
659+
$container = new ContainerBuilder(new EnvPlaceholderParameterBag([
660+
'ccc' => '%env(json:ARRAY)%',
661+
]));
662+
663+
$container
664+
->register('foobar', BarMethodCall::class)
665+
->addMethodCall('setArray', ['%ccc%']);
666+
667+
(new ResolveParameterPlaceHoldersPass())->process($container);
668+
(new CheckTypeDeclarationsPass(true))->process($container);
669+
670+
$this->addToAssertionCount(1);
671+
}
587672
}

0 commit comments

Comments
 (0)