From aee4e33cdb493e7c1cbdca715e6d014dcceee06d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Thu, 25 Oct 2018 09:05:21 +0200 Subject: [PATCH] =?UTF-8?q?[DI]=C2=A0Add=20a=20\"default\"=20EnvProcessor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DependencyInjection/CHANGELOG.md | 5 + .../DependencyInjection/EnvVarProcessor.php | 31 ++++- .../Exception/EnvNotFoundException.php | 4 - .../RegisterEnvVarProcessorsPassTest.php | 1 + .../Tests/Dumper/PhpDumperTest.php | 22 +++ .../Tests/EnvVarProcessorTest.php | 4 +- .../Fixtures/php/services_default_env.php | 130 ++++++++++++++++++ 7 files changed, 186 insertions(+), 11 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_default_env.php diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 2cc1a49725930..470331d9b7cec 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.3.0 +----- + + * added `%env(default:...)%` processor to fallback to a default value + 4.2.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php index 67a0687c24586..912091b217188 100644 --- a/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php +++ b/src/Symfony/Component/DependencyInjection/EnvVarProcessor.php @@ -44,6 +44,7 @@ public static function getProvidedTypes() 'json' => 'array', 'key' => 'bool|int|float|string|array', 'resolve' => 'string', + 'default' => 'bool|int|float|string|array', 'string' => 'string', ); } @@ -57,7 +58,7 @@ public function getEnv($prefix, $name, \Closure $getEnv) if ('key' === $prefix) { if (false === $i) { - throw new RuntimeException(sprintf('Invalid configuration: env var "key:%s" does not contain a key specifier.', $name)); + throw new RuntimeException(sprintf('Invalid env "key:%s": a key specifier should be provided.', $name)); } $next = substr($name, $i + 1); @@ -67,19 +68,39 @@ public function getEnv($prefix, $name, \Closure $getEnv) if (!\is_array($array)) { throw new RuntimeException(sprintf('Resolved value of "%s" did not result in an array value.', $next)); } - if (!array_key_exists($key, $array)) { - throw new RuntimeException(sprintf('Key "%s" not found in "%s" (resolved from "%s")', $key, json_encode($array), $next)); + + if (!isset($array[$key]) && !array_key_exists($key, $array)) { + throw new EnvNotFoundException(sprintf('Key "%s" not found in "%s" (resolved from "%s").', $key, json_encode($array), $next)); } return $array[$key]; } + if ('default' === $prefix) { + if (false === $i) { + throw new RuntimeException(sprintf('Invalid env "default:%s": a fallback parameter should be provided.', $name)); + } + + $next = substr($name, $i + 1); + $default = substr($name, 0, $i); + + if (!$this->container->hasParameter($default)) { + throw new RuntimeException(sprintf('Invalid env fallback in "default:%s": parameter "%s" not found.', $name, $default)); + } + + try { + return $getEnv($next); + } catch (EnvNotFoundException $e) { + return $this->container->getParameter($default); + } + } + if ('file' === $prefix) { if (!is_scalar($file = $getEnv($name))) { throw new RuntimeException(sprintf('Invalid file name: env var "%s" is non-scalar.', $name)); } if (!file_exists($file)) { - throw new RuntimeException(sprintf('Env "file:%s" not found: %s does not exist.', $name, $file)); + throw new EnvNotFoundException(sprintf('File "%s" not found (resolved from "%s").', $file, $name)); } return file_get_contents($file); @@ -95,7 +116,7 @@ public function getEnv($prefix, $name, \Closure $getEnv) $env = $_SERVER[$name]; } elseif (false === ($env = getenv($name)) || null === $env) { // null is a possible value because of thread safety issues if (!$this->container->hasParameter("env($name)")) { - throw new EnvNotFoundException($name); + throw new EnvNotFoundException(sprintf('Environment variable not found: "%s".', $name)); } if (null === $env = $this->container->getParameter("env($name)")) { diff --git a/src/Symfony/Component/DependencyInjection/Exception/EnvNotFoundException.php b/src/Symfony/Component/DependencyInjection/Exception/EnvNotFoundException.php index 6ed18e06807f5..04ac84800abc1 100644 --- a/src/Symfony/Component/DependencyInjection/Exception/EnvNotFoundException.php +++ b/src/Symfony/Component/DependencyInjection/Exception/EnvNotFoundException.php @@ -18,8 +18,4 @@ */ class EnvNotFoundException extends InvalidArgumentException { - public function __construct(string $name) - { - parent::__construct(sprintf('Environment variable not found: "%s".', $name)); - } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php index 4681092ca7849..93d3c16ff94dc 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php @@ -40,6 +40,7 @@ public function testSimpleProcessor() 'json' => array('array'), 'key' => array('bool', 'int', 'float', 'string', 'array'), 'resolve' => array('string'), + 'default' => array('bool', 'int', 'float', 'string', 'array'), 'string' => array('string'), ); diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 941404c4107e3..4f138b9fe6b18 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -439,6 +439,28 @@ public function testDumpedCsvEnvParameters() $this->assertSame(array('foo', 'bar'), $container->getParameter('hello')); } + public function testDumpedDefaultEnvParameters() + { + $container = new ContainerBuilder(); + $container->setParameter('fallback_param', 'baz'); + $container->setParameter('fallback_env', '%env(foobar)%'); + $container->setParameter('env(foobar)', 'foobaz'); + $container->setParameter('env(foo)', '{"foo": "bar"}'); + $container->setParameter('hello', '%env(default:fallback_param:bar)%'); + $container->setParameter('hello-bar', '%env(default:fallback_env:key:baz:json:foo)%'); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->dump(); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_default_env.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_DefaultParameters'))); + + require self::$fixturesPath.'/php/services_default_env.php'; + $container = new \Symfony_DI_PhpDumper_Test_DefaultParameters(); + $this->assertSame('baz', $container->getParameter('hello')); + $this->assertSame('foobaz', $container->getParameter('hello-bar')); + } + public function testDumpedJsonEnvParameters() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php index 5cd3a68b21baa..b244537354e43 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/EnvVarProcessorTest.php @@ -317,7 +317,7 @@ public function testGetEnvUnknown() /** * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException - * @expectedExceptionMessage Invalid configuration: env var "key:foo" does not contain a key specifier. + * @expectedExceptionMessage Invalid env "key:foo": a key specifier should be provided. */ public function testGetEnvKeyInvalidKey() { @@ -355,7 +355,7 @@ public function noArrayValues() } /** - * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedException \Symfony\Component\DependencyInjection\Exception\EnvNotFoundException * @expectedExceptionMessage Key "index" not found in * @dataProvider invalidArrayValues */ diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_default_env.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_default_env.php new file mode 100644 index 0000000000000..f0a82838481be --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_default_env.php @@ -0,0 +1,130 @@ +parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + + $this->aliases = array(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array( + 'fallback_env' => false, + 'hello' => false, + 'hello-bar' => false, + ); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + switch ($name) { + case 'fallback_env': $value = $this->getEnv('foobar'); break; + case 'hello': $value = $this->getEnv('default:fallback_param:bar'); break; + case 'hello-bar': $value = $this->getEnv('default:fallback_env:key:baz:json:foo'); break; + default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + $this->loadedDynamicParameters[$name] = true; + + return $this->dynamicParameters[$name] = $value; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'fallback_param' => 'baz', + 'env(foobar)' => 'foobaz', + 'env(foo)' => '{"foo": "bar"}', + ); + } +}