4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\DependencyInjection\ParameterBag;
14 use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
15 use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
16 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
21 * @author Fabien Potencier <fabien@symfony.com>
23 class ParameterBag implements ParameterBagInterface
25 protected $parameters = array();
26 protected $resolved = false;
28 private $normalizedNames = array();
31 * @param array $parameters An array of parameters
33 public function __construct(array $parameters = array())
35 $this->add($parameters);
39 * Clears all parameters.
41 public function clear()
43 $this->parameters = array();
47 * Adds parameters to the service container parameters.
49 * @param array $parameters An array of parameters
51 public function add(array $parameters)
53 foreach ($parameters as $key => $value) {
54 $this->set($key, $value);
63 return $this->parameters;
69 public function get($name)
71 $name = $this->normalizeName($name);
73 if (!array_key_exists($name, $this->parameters)) {
75 throw new ParameterNotFoundException($name);
78 $alternatives = array();
79 foreach ($this->parameters as $key => $parameterValue) {
80 $lev = levenshtein($name, $key);
81 if ($lev <= \strlen($name) / 3 || false !== strpos($key, $name)) {
82 $alternatives[] = $key;
86 $nonNestedAlternative = null;
87 if (!\count($alternatives) && false !== strpos($name, '.')) {
88 $namePartsLength = array_map('strlen', explode('.', $name));
89 $key = substr($name, 0, -1 * (1 + array_pop($namePartsLength)));
90 while (\count($namePartsLength)) {
91 if ($this->has($key)) {
92 if (\is_array($this->get($key))) {
93 $nonNestedAlternative = $key;
98 $key = substr($key, 0, -1 * (1 + array_pop($namePartsLength)));
102 throw new ParameterNotFoundException($name, null, null, null, $alternatives, $nonNestedAlternative);
105 return $this->parameters[$name];
109 * Sets a service container parameter.
111 * @param string $name The parameter name
112 * @param mixed $value The parameter value
114 public function set($name, $value)
116 $this->parameters[$this->normalizeName($name)] = $value;
122 public function has($name)
124 return array_key_exists($this->normalizeName($name), $this->parameters);
128 * Removes a parameter.
130 * @param string $name The parameter name
132 public function remove($name)
134 unset($this->parameters[$this->normalizeName($name)]);
140 public function resolve()
142 if ($this->resolved) {
146 $parameters = array();
147 foreach ($this->parameters as $key => $value) {
149 $value = $this->resolveValue($value);
150 $parameters[$key] = $this->unescapeValue($value);
151 } catch (ParameterNotFoundException $e) {
152 $e->setSourceKey($key);
158 $this->parameters = $parameters;
159 $this->resolved = true;
163 * Replaces parameter placeholders (%name%) by their values.
165 * @param mixed $value A value
166 * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
168 * @return mixed The resolved value
170 * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist
171 * @throws ParameterCircularReferenceException if a circular reference if detected
172 * @throws RuntimeException when a given parameter has a type problem
174 public function resolveValue($value, array $resolving = array())
176 if (\is_array($value)) {
178 foreach ($value as $k => $v) {
179 $args[\is_string($k) ? $this->resolveValue($k, $resolving) : $k] = $this->resolveValue($v, $resolving);
185 if (!\is_string($value) || 2 > \strlen($value)) {
189 return $this->resolveString($value, $resolving);
193 * Resolves parameters inside a string.
195 * @param string $value The string to resolve
196 * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
198 * @return string The resolved string
200 * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist
201 * @throws ParameterCircularReferenceException if a circular reference if detected
202 * @throws RuntimeException when a given parameter has a type problem
204 public function resolveString($value, array $resolving = array())
206 // we do this to deal with non string values (Boolean, integer, ...)
207 // as the preg_replace_callback throw an exception when trying
208 // a non-string in a parameter value
209 if (preg_match('/^%([^%\s]+)%$/', $value, $match)) {
211 $lcKey = strtolower($key); // strtolower() to be removed in 4.0
213 if (isset($resolving[$lcKey])) {
214 throw new ParameterCircularReferenceException(array_keys($resolving));
217 $resolving[$lcKey] = true;
219 return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving);
222 return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($resolving, $value) {
224 if (!isset($match[1])) {
229 $lcKey = strtolower($key); // strtolower() to be removed in 4.0
230 if (isset($resolving[$lcKey])) {
231 throw new ParameterCircularReferenceException(array_keys($resolving));
234 $resolved = $this->get($key);
236 if (!\is_string($resolved) && !is_numeric($resolved)) {
237 throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type %s inside string value "%s".', $key, \gettype($resolved), $value));
240 $resolved = (string) $resolved;
241 $resolving[$lcKey] = true;
243 return $this->isResolved() ? $resolved : $this->resolveString($resolved, $resolving);
247 public function isResolved()
249 return $this->resolved;
255 public function escapeValue($value)
257 if (\is_string($value)) {
258 return str_replace('%', '%%', $value);
261 if (\is_array($value)) {
263 foreach ($value as $k => $v) {
264 $result[$k] = $this->escapeValue($v);
276 public function unescapeValue($value)
278 if (\is_string($value)) {
279 return str_replace('%%', '%', $value);
282 if (\is_array($value)) {
284 foreach ($value as $k => $v) {
285 $result[$k] = $this->unescapeValue($v);
294 private function normalizeName($name)
296 if (isset($this->normalizedNames[$normalizedName = strtolower($name)])) {
297 $normalizedName = $this->normalizedNames[$normalizedName];
298 if ((string) $name !== $normalizedName) {
299 @trigger_error(sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), E_USER_DEPRECATED);
302 $normalizedName = $this->normalizedNames[$normalizedName] = (string) $name;
305 return $normalizedName;