diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php index d4f8e69fe5d8c..5a3dc72b43cc3 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php @@ -14,11 +14,13 @@ 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 @@ private function checkTypeDeclarations(Definition $checkedDefinition, \Reflectio $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 @@ private function checkTypeDeclarations(Definition $checkedDefinition, \Reflectio /** * @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 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar $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()) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php index a2f66bf71c64d..4729c43b93318 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php @@ -14,8 +14,10 @@ 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 @@ public function testProcessThrowsOnIterableTypeWhenScalarPassed() $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 @@ public function testProcessResolveExpressions() $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); + } }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: