Skip to content

Commit

Permalink
bug #24488 [DI] Prefixed env vars and load time inlining are incompat…
Browse files Browse the repository at this point in the history
…ible (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
-------

91c9287 [DI] Prefixed env vars and load time inlining are incompatible
  • Loading branch information
fabpot committed Oct 10, 2017
2 parents 7f899a9 + 91c9287 commit 5904d34
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 10 deletions.
Expand Up @@ -13,6 +13,7 @@

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;
Expand Down Expand Up @@ -164,4 +165,30 @@ public function compile($resolveEnvPlaceholders = false)
{
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);
}
}
Expand Up @@ -34,16 +34,14 @@ public function process(ContainerBuilder $container)
$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);
}
}

Expand Down
Expand Up @@ -101,6 +101,19 @@ public function testOverriddenEnvsAreMerged()
$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
Expand Down Expand Up @@ -142,3 +155,11 @@ public function load(array $configs, ContainerBuilder $container)
}
}
}

class BarExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$container->resolveEnvPlaceholders('%env(int:FOO)%', true);
}
}

0 comments on commit 5904d34

Please sign in to comment.