[DependencyInjection] Handle env var placeholders in CheckTypeDeclarationsPass
This commit is contained in:
parent
bfe697bb86
commit
c3574858b5
@ -14,11 +14,13 @@ namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidParameterTypeException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ExpressionLanguage;
|
||||
use Symfony\Component\DependencyInjection\Parameter;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\ExpressionLanguage\Expression;
|
||||
@ -104,19 +106,21 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
|
||||
$reflectionParameters = $reflectionFunction->getParameters();
|
||||
$checksCount = min($reflectionFunction->getNumberOfParameters(), \count($values));
|
||||
|
||||
$envPlaceholderUniquePrefix = $this->container->getParameterBag() instanceof EnvPlaceholderParameterBag ? $this->container->getParameterBag()->getEnvPlaceholderUniquePrefix() : null;
|
||||
|
||||
for ($i = 0; $i < $checksCount; ++$i) {
|
||||
if (!$reflectionParameters[$i]->hasType() || $reflectionParameters[$i]->isVariadic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->checkType($checkedDefinition, $values[$i], $reflectionParameters[$i]);
|
||||
$this->checkType($checkedDefinition, $values[$i], $reflectionParameters[$i], $envPlaceholderUniquePrefix);
|
||||
}
|
||||
|
||||
if ($reflectionFunction->isVariadic() && ($lastParameter = end($reflectionParameters))->hasType()) {
|
||||
$variadicParameters = \array_slice($values, $lastParameter->getPosition());
|
||||
|
||||
foreach ($variadicParameters as $variadicParameter) {
|
||||
$this->checkType($checkedDefinition, $variadicParameter, $lastParameter);
|
||||
$this->checkType($checkedDefinition, $variadicParameter, $lastParameter, $envPlaceholderUniquePrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -124,7 +128,7 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
|
||||
/**
|
||||
* @throws InvalidParameterTypeException When a parameter is not compatible with the declared type
|
||||
*/
|
||||
private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter): void
|
||||
private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter, ?string $envPlaceholderUniquePrefix): void
|
||||
{
|
||||
$type = $parameter->getType()->getName();
|
||||
|
||||
@ -178,8 +182,22 @@ final class CheckTypeDeclarationsPass extends AbstractRecursivePass
|
||||
$value = $this->container->getParameter($value);
|
||||
} elseif ($value instanceof Expression) {
|
||||
$value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this->container]);
|
||||
} elseif (\is_string($value) && '%' === ($value[0] ?? '') && preg_match('/^%([^%]+)%$/', $value, $match)) {
|
||||
$value = $this->container->getParameter($match[1]);
|
||||
} elseif (\is_string($value)) {
|
||||
if ('%' === ($value[0] ?? '') && preg_match('/^%([^%]+)%$/', $value, $match)) {
|
||||
// Only array parameters are not inlined when dumped.
|
||||
$value = [];
|
||||
} elseif ($envPlaceholderUniquePrefix && false !== strpos($value, 'env_')) {
|
||||
// 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.
|
||||
// We don't need to change the value because it is already a string.
|
||||
if ('' === preg_replace('/'.$envPlaceholderUniquePrefix.'_\w+_[a-f0-9]{32}/U', '', $value, -1, $c) && 1 === $c) {
|
||||
try {
|
||||
$value = $this->container->resolveEnvPlaceholders($value, true);
|
||||
} catch (EnvNotFoundException | RuntimeException $e) {
|
||||
// If an env placeholder cannot be resolved, we skip the validation.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $value && $parameter->allowsNull()) {
|
||||
|
@ -14,8 +14,10 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Bar;
|
||||
use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall;
|
||||
@ -571,6 +573,20 @@ class CheckTypeDeclarationsPassTest extends TestCase
|
||||
$this->assertInstanceOf(\stdClass::class, $container->get('bar')->foo);
|
||||
}
|
||||
|
||||
public function testProcessResolveArrayParameters()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('ccc', ['foobar']);
|
||||
|
||||
$container
|
||||
->register('foobar', BarMethodCall::class)
|
||||
->addMethodCall('setArray', ['%ccc%']);
|
||||
|
||||
(new CheckTypeDeclarationsPass(true))->process($container);
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
public function testProcessResolveExpressions()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
@ -584,4 +600,73 @@ class CheckTypeDeclarationsPassTest extends TestCase
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
public function testProcessHandleMixedEnvPlaceholder()
|
||||
{
|
||||
$this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Invalid definition for service "foobar": argument 1 of "Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall::setArray" accepts "array", "string" passed.');
|
||||
|
||||
$container = new ContainerBuilder(new EnvPlaceholderParameterBag([
|
||||
'ccc' => '%env(FOO)%',
|
||||
]));
|
||||
|
||||
$container
|
||||
->register('foobar', BarMethodCall::class)
|
||||
->addMethodCall('setArray', ['foo%ccc%']);
|
||||
|
||||
(new CheckTypeDeclarationsPass(true))->process($container);
|
||||
}
|
||||
|
||||
public function testProcessHandleMultipleEnvPlaceholder()
|
||||
{
|
||||
$this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Invalid definition for service "foobar": argument 1 of "Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall::setArray" accepts "array", "string" passed.');
|
||||
|
||||
$container = new ContainerBuilder(new EnvPlaceholderParameterBag([
|
||||
'ccc' => '%env(FOO)%',
|
||||
'fcy' => '%env(int:BAR)%',
|
||||
]));
|
||||
|
||||
$container
|
||||
->register('foobar', BarMethodCall::class)
|
||||
->addMethodCall('setArray', ['%ccc%%fcy%']);
|
||||
|
||||
(new CheckTypeDeclarationsPass(true))->process($container);
|
||||
}
|
||||
|
||||
public function testProcessHandleExistingEnvPlaceholder()
|
||||
{
|
||||
putenv('ARRAY={"foo":"bar"}');
|
||||
|
||||
$container = new ContainerBuilder(new EnvPlaceholderParameterBag([
|
||||
'ccc' => '%env(json:ARRAY)%',
|
||||
]));
|
||||
|
||||
$container
|
||||
->register('foobar', BarMethodCall::class)
|
||||
->addMethodCall('setArray', ['%ccc%']);
|
||||
|
||||
(new ResolveParameterPlaceHoldersPass())->process($container);
|
||||
(new CheckTypeDeclarationsPass(true))->process($container);
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
|
||||
putenv('ARRAY=');
|
||||
}
|
||||
|
||||
public function testProcessHandleNotFoundEnvPlaceholder()
|
||||
{
|
||||
$container = new ContainerBuilder(new EnvPlaceholderParameterBag([
|
||||
'ccc' => '%env(json:ARRAY)%',
|
||||
]));
|
||||
|
||||
$container
|
||||
->register('foobar', BarMethodCall::class)
|
||||
->addMethodCall('setArray', ['%ccc%']);
|
||||
|
||||
(new ResolveParameterPlaceHoldersPass())->process($container);
|
||||
(new CheckTypeDeclarationsPass(true))->process($container);
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user