bug #24488 [DI] Prefixed env vars and load time inlining are incompatible (nicolas-grekas)

This PR was merged into the 3.4 branch.

Discussion
----------

[DI] Prefixed env vars and load time inlining are incompatible

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

That's because env var processors are not registered yet.

Commits
-------

91c9287c55 [DI] Prefixed env vars and load time inlining are incompatible
This commit is contained in:
Fabien Potencier 2017-10-09 21:05:35 -07:00
commit 5904d34125
3 changed files with 56 additions and 10 deletions

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
@ -164,4 +165,30 @@ class MergeExtensionConfigurationContainerBuilder extends ContainerBuilder
{
throw new LogicException(sprintf('Cannot compile the container in extension "%s".', $this->extensionClass));
}
/**
* {@inheritdoc}
*/
public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null)
{
if (true !== $format || !\is_string($value)) {
return parent::resolveEnvPlaceholders($value, $format, $usedEnvs);
}
$bag = $this->getParameterBag();
$value = $bag->resolveValue($value);
foreach ($bag->getEnvPlaceholders() as $env => $placeholders) {
if (false === strpos($env, ':')) {
continue;
}
foreach ($placeholders as $placeholder) {
if (false !== stripos($value, $placeholder)) {
throw new RuntimeException(sprintf('Using a cast in "env(%s)" is incompatible with resolution at compile time in "%s". The logic in the extension should be moved to a compiler pass, or an env parameter with no cast should be used instead.', $env, $this->extensionClass));
}
}
}
return parent::resolveEnvPlaceholders($value, $format, $usedEnvs);
}
}

View File

@ -34,16 +34,14 @@ class RegisterEnvVarProcessorsPass implements CompilerPassInterface
$types = array();
$processors = array();
foreach ($container->findTaggedServiceIds('container.env_var_processor') as $id => $tags) {
foreach ($tags as $attr) {
if (!$r = $container->getReflectionClass($class = $container->getDefinition($id)->getClass())) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
} elseif (!$r->isSubclassOf(EnvVarProcessorInterface::class)) {
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EnvVarProcessorInterface::class));
}
foreach ($class::getProvidedTypes() as $prefix => $type) {
$processors[$prefix] = new ServiceClosureArgument(new Reference($id));
$types[$prefix] = self::validateProvidedTypes($type, $class);
}
if (!$r = $container->getReflectionClass($class = $container->getDefinition($id)->getClass())) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
} elseif (!$r->isSubclassOf(EnvVarProcessorInterface::class)) {
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EnvVarProcessorInterface::class));
}
foreach ($class::getProvidedTypes() as $prefix => $type) {
$processors[$prefix] = new ServiceClosureArgument(new Reference($id));
$types[$prefix] = self::validateProvidedTypes($type, $class);
}
}

View File

@ -101,6 +101,19 @@ class MergeExtensionConfigurationPassTest extends TestCase
$this->assertSame(array('BAZ', 'FOO'), array_keys($container->getParameterBag()->getEnvPlaceholders()));
$this->assertSame(array('BAZ' => 1, 'FOO' => 0), $container->getEnvCounters());
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException
* @expectedExceptionMessage Using a cast in "env(int:FOO)" is incompatible with resolution at compile time in "Symfony\Component\DependencyInjection\Tests\Compiler\BarExtension". The logic in the extension should be moved to a compiler pass, or an env parameter with no cast should be used instead.
*/
public function testProcessedEnvsAreIncompatibleWithResolve()
{
$container = new ContainerBuilder();
$container->registerExtension(new BarExtension());
$container->prependExtensionConfig('bar', array());
(new MergeExtensionConfigurationPass())->process($container);
}
}
class FooConfiguration implements ConfigurationInterface
@ -142,3 +155,11 @@ class FooExtension extends Extension
}
}
}
class BarExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$container->resolveEnvPlaceholders('%env(int:FOO)%', true);
}
}