Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[DependencyInjection] added a way to reference dynamic environment variables in service definitions #16403

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\EnvVariable;

/**
* Resolves all parameter placeholders "%somevalue%" to their real values.
Expand All @@ -36,7 +37,7 @@ public function process(ContainerBuilder $container)
try {
$definition->setClass($parameterBag->resolveValue($definition->getClass()));
$definition->setFile($parameterBag->resolveValue($definition->getFile()));
$definition->setArguments($parameterBag->resolveValue($definition->getArguments()));
$definition->setArguments($parameterBag->resolveValue($definition->getArguments(), array(), true));
if ($definition->getFactoryClass(false)) {
$definition->setFactoryClass($parameterBag->resolveValue($definition->getFactoryClass(false)));
}
Expand All @@ -50,11 +51,11 @@ public function process(ContainerBuilder $container)

$calls = array();
foreach ($definition->getMethodCalls() as $name => $arguments) {
$calls[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($arguments);
$calls[$parameterBag->resolveValue($name)] = $parameterBag->resolveValue($arguments, array(), true);
}
$definition->setMethodCalls($calls);

$definition->setProperties($parameterBag->resolveValue($definition->getProperties()));
$definition->setProperties($parameterBag->resolveValue($definition->getProperties(), array(), true));
} catch (ParameterNotFoundException $e) {
$e->setSourceId($id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\EnvVariable;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\ContainerInterface;
Expand Down Expand Up @@ -136,6 +137,8 @@ private function findEdges($id, $arguments, $required, $name)
foreach ($arguments as $argument) {
if ($argument instanceof Parameter) {
$argument = $this->container->hasParameter($argument) ? $this->container->getParameter($argument) : null;
} elseif ($argument instanceof EnvVariable) {
$argument = '$'.$argument.'$';
} elseif (is_string($argument) && preg_match('/^%([^%]+)%$/', $argument, $match)) {
$argument = $this->container->hasParameter($match[1]) ? $this->container->getParameter($match[1]) : null;
}
Expand Down
10 changes: 10 additions & 0 deletions 10 src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\EnvVariable;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
Expand Down Expand Up @@ -1379,13 +1380,17 @@ private function dumpValue($value, $interpolate = true)
return $this->getServiceCall((string) $value, $value);
} elseif ($value instanceof Expression) {
return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container'));
} elseif ($value instanceof EnvVariable) {
return $this->dumpEnvVariable($value);
} elseif ($value instanceof Parameter) {
return $this->dumpParameter($value);
} elseif (true === $interpolate && is_string($value)) {
if (preg_match('/^%([^%]+)%$/', $value, $match)) {
// we do this to deal with non string values (Boolean, integer, ...)
// the preg_replace_callback converts them to strings
return $this->dumpParameter(strtolower($match[1]));
} elseif (preg_match('/^\$([^\$]+)\$$/', $value, $match)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is will also match $foo'boom$ making it possible to unwillingly inject PHP code (as the value is not escaped or anything when it's used in dumpEnvVariable ).

And the $$ will not match, but is not normalized to a single $ in the else-block.

return $this->dumpEnvVariable($match[1]);
} else {
$that = $this;
$replaceParameters = function ($match) use ($that) {
Expand Down Expand Up @@ -1431,6 +1436,11 @@ public function dumpParameter($name)
return sprintf("\$this->getParameter('%s')", strtolower($name));
}

private function dumpEnvVariable($name)
{
return sprintf("getenv('%s')", $name);
}

/**
* @deprecated since version 2.6.2, to be removed in 3.0.
* Use \Symfony\Component\DependencyInjection\ContainerBuilder::addExpressionLanguageProvider instead.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Dumper;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\EnvVariable;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Definition;
Expand Down Expand Up @@ -371,6 +372,8 @@ public static function phpToXml($value)
return 'false';
case $value instanceof Parameter:
return '%'.$value.'%';
case $value instanceof EnvVariable:
return '$'.$value.'$';
case is_object($value) || is_resource($value):
throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\EnvVariable;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
Expand Down Expand Up @@ -274,6 +275,8 @@ private function dumpValue($value)
return $code;
} elseif ($value instanceof Reference) {
return $this->getServiceCall((string) $value, $value);
} elseif ($value instanceof EnvVariable) {
return sprintf('$%s$', (string) $value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The escape method should be updated to also escape a value starting with '$', no?

} elseif ($value instanceof Parameter) {
return $this->getParameterCall((string) $value);
} elseif ($value instanceof Expression) {
Expand Down
42 changes: 42 additions & 0 deletions 42 src/Symfony/Component/DependencyInjection/EnvVariable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection;

/**
* Represents an environment variable value.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class EnvVariable
{
private $id;

/**
* Constructor.
*
* @param string $id The environment variable name
*/
public function __construct($id)
{
$this->id = $id;
}

/**
* __toString.
*
* @return string The environment variable name
*/
public function __toString()
{
return (string) $this->id;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO is better to cast to a string in the constructor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or dont cast at all. The parameter is already defined as string.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Component\DependencyInjection\ParameterBag;

use Symfony\Component\DependencyInjection\EnvVariable;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
Expand Down Expand Up @@ -167,12 +168,12 @@ public function resolve()
* @throws ParameterCircularReferenceException if a circular reference if detected
* @throws RuntimeException when a given parameter has a type problem.
*/
public function resolveValue($value, array $resolving = array())
public function resolveValue($value, array $resolving = array(), $protectEnvVariables = false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new protectEnvVariables param is missing from the docblock.

{
if (is_array($value)) {
$args = array();
foreach ($value as $k => $v) {
$args[$this->resolveValue($k, $resolving)] = $this->resolveValue($v, $resolving);
$args[$this->resolveValue($k, $resolving, $protectEnvVariables)] = $this->resolveValue($v, $resolving, $protectEnvVariables);
}

return $args;
Expand All @@ -182,7 +183,7 @@ public function resolveValue($value, array $resolving = array())
return $value;
}

return $this->resolveString($value, $resolving);
return $this->resolveString($value, $resolving, $protectEnvVariables);
}

/**
Expand All @@ -197,7 +198,7 @@ public function resolveValue($value, array $resolving = array())
* @throws ParameterCircularReferenceException if a circular reference if detected
* @throws RuntimeException when a given parameter has a type problem.
*/
public function resolveString($value, array $resolving = array())
public function resolveString($value, array $resolving = array(), $protectEnvVariables = false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

{
// we do this to deal with non string values (Boolean, integer, ...)
// as the preg_replace_callback throw an exception when trying
Expand All @@ -211,12 +212,20 @@ public function resolveString($value, array $resolving = array())

$resolving[$key] = true;

return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving);
return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving, $protectEnvVariables);
}

if (preg_match('/^\$([^\$\s]+)\$$/', $value, $match)) {
if ($protectEnvVariables) {
return new EnvVariable($match[1]);
}

return getenv($match[1]);
}

$self = $this;

return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($self, $resolving, $value) {
return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($self, $resolving, $value, $protectEnvVariables) {
// skip %%
if (!isset($match[1])) {
return '%%';
Expand All @@ -236,7 +245,7 @@ public function resolveString($value, array $resolving = array())
$resolved = (string) $resolved;
$resolving[$key] = true;

return $self->isResolved() ? $resolved : $self->resolveString($resolved, $resolving);
return $self->isResolved() ? $resolved : $self->resolveString($resolved, $resolving, $protectEnvVariables);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable is missing from the use() statement!

}, $value);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\DependencyInjection\Tests;

use Symfony\Component\DependencyInjection\EnvVariable;

class EnvVariableTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
$ref = new EnvVariable('foo');
$this->assertEquals('foo', (string) $ref, '__construct() sets the id of the env variable, which is used for the __toString() method');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\EnvVariable;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Parameter;
use Symfony\Component\ExpressionLanguage\Expression;
Expand All @@ -14,7 +15,7 @@
->addTag('foo', array('foo' => 'foo'))
->addTag('foo', array('bar' => 'bar', 'baz' => 'baz'))
->setFactory(array('Bar\\FooClass', 'getInstance'))
->setArguments(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, new Reference('service_container')))
->setArguments(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%', 'foo_env' => new EnvVariable('FOO')), true, new Reference('service_container')))
->setProperties(array('foo' => 'bar', 'moo' => new Reference('foo.baz'), 'qux' => array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%')))
->addMethodCall('setBar', array(new Reference('bar')))
->addMethodCall('initialize')
Expand All @@ -27,7 +28,7 @@
;
$container
->register('bar', 'Bar\FooClass')
->setArguments(array('foo', new Reference('foo.baz'), new Parameter('foo_bar')))
->setArguments(array('foo', new Reference('foo.baz'), new Parameter('foo_bar'), new EnvVariable('FOO')))
->setConfigurator(array(new Reference('foo.baz'), 'configure'))
;
$container
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ protected function getBarService()
{
$a = $this->get('foo.baz');

$this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar'));
$this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar'), getenv('FOO'));

$a->configure($instance);

Expand Down Expand Up @@ -186,7 +186,7 @@ protected function getFooService()
{
$a = $this->get('foo.baz');

$this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo')), true, $this);
$this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo'), 'foo_env' => getenv('FOO')), true, $this);

$instance->setBar($this->get('bar'));
$instance->initialize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ protected function getBarService()
{
$a = $this->get('foo.baz');

$this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar'));
$this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar'), getenv('FOO'));

$a->configure($instance);

Expand Down Expand Up @@ -187,7 +187,7 @@ protected function getFooService()
{
$a = $this->get('foo.baz');

$this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this);
$this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar', 'foo_env' => getenv('FOO')), true, $this);

$instance->setBar($this->get('bar'));
$instance->initialize();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<argument type="collection">
<argument key="%foo%">foo is %foo%</argument>
<argument key="foobar">%foo%</argument>
<argument key="foo_env">$FOO$</argument>
</argument>
<argument>true</argument>
<argument type="service" id="service_container"/>
Expand All @@ -38,6 +39,7 @@
<argument>foo</argument>
<argument type="service" id="foo.baz"/>
<argument>%foo_bar%</argument>
<argument>$FOO$</argument>
<configurator service="foo.baz" method="configure"/>
</service>
<service id="foo_bar" class="%foo_class%" shared="false"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ services:
tags:
- { name: foo, foo: foo }
- { name: foo, bar: bar, baz: baz }
arguments: [foo, '@foo.baz', { '%foo%': 'foo is %foo%', foobar: '%foo%' }, true, '@service_container']
arguments: [foo, '@foo.baz', { '%foo%': 'foo is %foo%', foobar: '%foo%', foo_env: $FOO$ }, true, '@service_container']
properties: { foo: bar, moo: '@foo.baz', qux: { '%foo%': 'foo is %foo%', foobar: '%foo%' } }
calls:
- [setBar, ['@bar']]
Expand All @@ -23,7 +23,7 @@ services:
configurator: ['%baz_class%', configureStatic1]
bar:
class: Bar\FooClass
arguments: [foo, '@foo.baz', '%foo_bar%']
arguments: [foo, '@foo.baz', '%foo_bar%', $FOO$]
configurator: ['@foo.baz', configure]
foo_bar:
class: %foo_class%
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.