Skip to content

Commit c3a4064

Browse files
magnusnordlandernicolas-grekas
authored andcommitted
[DI][Config] Make accessing environment variables in configuration more convenient
1 parent 695549f commit c3a4064

File tree

9 files changed

+206
-1
lines changed

9 files changed

+206
-1
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Config\Definition\Builder;
13+
14+
use Symfony\Component\Config\Definition\ExpressionNode;
15+
16+
/**
17+
* This class provides a fluent interface for defining a node.
18+
*
19+
* @author Magnus Nordlander <[email protected]>
20+
*/
21+
class ExpressionNodeDefinition extends VariableNodeDefinition
22+
{
23+
/**
24+
* Instantiate a Node.
25+
*
26+
* @return VariableNode The node
27+
*/
28+
protected function instantiateNode()
29+
{
30+
return new ExpressionNode($this->name, $this->parent);
31+
}
32+
}

src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public function __construct()
3434
'float' => __NAMESPACE__.'\\FloatNodeDefinition',
3535
'array' => __NAMESPACE__.'\\ArrayNodeDefinition',
3636
'enum' => __NAMESPACE__.'\\EnumNodeDefinition',
37+
'expression' => __NAMESPACE__.'\\ExpressionNodeDefinition',
3738
);
3839
}
3940

@@ -135,6 +136,18 @@ public function variableNode($name)
135136
return $this->node($name, 'variable');
136137
}
137138

139+
/**
140+
* Creates a child variable node.
141+
*
142+
* @param string $name The name of the node
143+
*
144+
* @return VariableNodeDefinition The builder of the child node
145+
*/
146+
public function expressionNode($name)
147+
{
148+
return $this->node($name, 'expression');
149+
}
150+
138151
/**
139152
* Returns the parent node.
140153
*

src/Symfony/Component/Config/Definition/Dumper/XmlReferenceDumper.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\Config\Definition\ArrayNode;
1717
use Symfony\Component\Config\Definition\EnumNode;
1818
use Symfony\Component\Config\Definition\PrototypedArrayNode;
19+
use Symfony\Component\ExpressionLanguage\Expression;
1920

2021
/**
2122
* Dumps a XML reference configuration for the given configuration/node instance.
@@ -120,6 +121,10 @@ private function writeNode(NodeInterface $node, $depth = 0, $root = false, $name
120121
$prototypeValue = implode('|', array_map('json_encode', $prototype->getValues()));
121122
break;
122123

124+
case 'Symfony\Component\Config\Definition\ExpressionNode':
125+
$prototypeValue = 'expression';
126+
break;
127+
123128
default:
124129
$prototypeValue = 'value';
125130
}
@@ -297,6 +302,10 @@ private function writeValue($value)
297302
return '';
298303
}
299304

305+
if ($value instanceof Expression) {
306+
return (string) $value;
307+
}
308+
300309
if (is_array($value)) {
301310
return implode(',', $value);
302311
}

src/Symfony/Component/Config/Definition/Dumper/YamlReferenceDumper.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\Config\Definition\ArrayNode;
1717
use Symfony\Component\Config\Definition\EnumNode;
1818
use Symfony\Component\Config\Definition\PrototypedArrayNode;
19+
use Symfony\Component\ExpressionLanguage\Expression;
1920
use Symfony\Component\Yaml\Inline;
2021

2122
/**
@@ -106,6 +107,8 @@ private function writeNode(NodeInterface $node, $depth = 0)
106107
} elseif (!is_array($example)) {
107108
$default = '[]';
108109
}
110+
} elseif ($default instanceof Expression) {
111+
$default = Inline::dump((string) $default);
109112
} else {
110113
$default = Inline::dump($default);
111114
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Config\Definition;
13+
14+
use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
15+
use Symfony\Component\ExpressionLanguage\Expression;
16+
17+
/**
18+
* This node represents a Symfony Expression Language Expression.
19+
*
20+
* @author Magnus Nordlander <[email protected]>
21+
*/
22+
class ExpressionNode extends VariableNode
23+
{
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
protected function validateType($value)
28+
{
29+
if (!$value instanceof Expression) {
30+
$e = new InvalidTypeException(sprintf('Invalid type for path "%s": expected Symfony\\Component\\ExpressionLanguage\\Expression , but got %s.', $this->getPath(), gettype($value)));
31+
32+
if ($hint = $this->getInfo()) {
33+
$e->addHint($hint);
34+
}
35+
$e->setPath($this->getPath());
36+
37+
throw $e;
38+
}
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
protected function preNormalize($value)
45+
{
46+
$value = parent::preNormalize($value);
47+
48+
return $this->transformToExpression($value);
49+
}
50+
51+
/**
52+
* {@inheritdoc}
53+
*/
54+
public function getDefaultValue()
55+
{
56+
$value = parent::getDefaultValue();
57+
58+
return $this->transformToExpression($value);
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
protected function isValueEmpty($value)
65+
{
66+
// a boolean value cannot be empty
67+
return null === $value;
68+
}
69+
70+
private function transformToExpression($value)
71+
{
72+
if (null === $value) {
73+
return;
74+
}
75+
76+
if ($value instanceof Expression) {
77+
return $value;
78+
}
79+
80+
if (!class_exists(Expression::class) {
81+
throw new \RuntimeException('The Symfony Expression Language component must be installed to use expression nodes in configurations.');
82+
}
83+
84+
return new Expression($value);
85+
}
86+
}

src/Symfony/Component/Config/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"symfony/filesystem": "~2.8|~3.0"
2121
},
2222
"suggest": {
23-
"symfony/yaml": "To use the yaml reference dumper"
23+
"symfony/yaml": "To use the yaml reference dumper",
24+
"symfony/expression-language": "To use the expression node type"
2425
},
2526
"autoload": {
2627
"psr-4": { "Symfony\\Component\\Config\\": "" },

src/Symfony/Component/DependencyInjection/Container.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection;
1313

14+
use Symfony\Component\DependencyInjection\Exception\EnvironmentVariableNotFoundException;
1415
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1516
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
1617
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
@@ -151,6 +152,26 @@ public function setParameter($name, $value)
151152
$this->parameterBag->set($name, $value);
152153
}
153154

155+
/**
156+
* @internal
157+
*/
158+
public function getEnvironmentVariable($name, $default = null)
159+
{
160+
if (isset($_ENV[$name])) {
161+
return $_ENV[$name];
162+
}
163+
164+
if (false !== $value = getenv($name)) {
165+
return $value;
166+
}
167+
168+
if (2 > func_num_args()) {
169+
throw new EnvironmentVariableNotFoundException($name);
170+
}
171+
172+
return $default;
173+
}
174+
154175
/**
155176
* Sets a service.
156177
*
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Exception;
13+
14+
/**
15+
* This exception is thrown when an environment variable was not found.
16+
*
17+
* @author Magnus Nordlander <[email protected]>
18+
*/
19+
class EnvironmentVariableNotFoundException extends InvalidArgumentException
20+
{
21+
public function __construct($name)
22+
{
23+
parent::__construct(sprintf('You have requested a non-existent environment variable "%s".', $sourceId, $name));
24+
}
25+
}

src/Symfony/Component/DependencyInjection/ExpressionLanguageProvider.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*
2020
* To get a service, use service('request').
2121
* To get a parameter, use parameter('kernel.debug').
22+
* To get an environment variable, use env('UID').
2223
*
2324
* @author Fabien Potencier <[email protected]>
2425
*/
@@ -38,6 +39,20 @@ public function getFunctions()
3839
}, function (array $variables, $value) {
3940
return $variables['container']->getParameter($value);
4041
}),
42+
43+
new ExpressionFunction('env', function ($arg, $default = null) {
44+
if (2 > func_num_args()) {
45+
return sprintf('$this->getEnvironmentVariable(%s)', $arg);
46+
}
47+
48+
return sprintf('$this->getEnvironmentVariable(%s, %s)', $arg, $default);
49+
}, function (array $variables, $value, $default = null) {
50+
if (2 > func_num_args()) {
51+
return $variables['container']->getEnvironmentVariable($value);
52+
}
53+
54+
return $variables['container']->getEnvironmentVariable($value, $default);
55+
}),
4156
);
4257
}
4358
}

0 commit comments

Comments
 (0)