/src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php

https://github.com/gimler/symfony · PHP · 289 lines · 163 code · 45 blank · 81 comment · 23 complexity · a504895b3ee239a9fbe5f9059aa83549 MD5 · raw file

  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\DependencyInjection\ParameterBag;
  11. use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
  12. use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
  13. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  14. /**
  15. * Holds parameters.
  16. *
  17. * @author Fabien Potencier <fabien@symfony.com>
  18. */
  19. class ParameterBag implements ParameterBagInterface
  20. {
  21. protected $parameters = array();
  22. protected $resolved = false;
  23. /**
  24. * @param array $parameters An array of parameters
  25. */
  26. public function __construct(array $parameters = array())
  27. {
  28. $this->add($parameters);
  29. }
  30. /**
  31. * Clears all parameters.
  32. */
  33. public function clear()
  34. {
  35. $this->parameters = array();
  36. }
  37. /**
  38. * Adds parameters to the service container parameters.
  39. *
  40. * @param array $parameters An array of parameters
  41. */
  42. public function add(array $parameters)
  43. {
  44. foreach ($parameters as $key => $value) {
  45. $this->set($key, $value);
  46. }
  47. }
  48. /**
  49. * {@inheritdoc}
  50. */
  51. public function all()
  52. {
  53. return $this->parameters;
  54. }
  55. /**
  56. * {@inheritdoc}
  57. */
  58. public function get($name)
  59. {
  60. $name = (string) $name;
  61. if (!array_key_exists($name, $this->parameters)) {
  62. if (!$name) {
  63. throw new ParameterNotFoundException($name);
  64. }
  65. $alternatives = array();
  66. foreach ($this->parameters as $key => $parameterValue) {
  67. $lev = levenshtein($name, $key);
  68. if ($lev <= \strlen($name) / 3 || false !== strpos($key, $name)) {
  69. $alternatives[] = $key;
  70. }
  71. }
  72. $nonNestedAlternative = null;
  73. if (!\count($alternatives) && false !== strpos($name, '.')) {
  74. $namePartsLength = array_map('strlen', explode('.', $name));
  75. $key = substr($name, 0, -1 * (1 + array_pop($namePartsLength)));
  76. while (\count($namePartsLength)) {
  77. if ($this->has($key)) {
  78. if (\is_array($this->get($key))) {
  79. $nonNestedAlternative = $key;
  80. }
  81. break;
  82. }
  83. $key = substr($key, 0, -1 * (1 + array_pop($namePartsLength)));
  84. }
  85. }
  86. throw new ParameterNotFoundException($name, null, null, null, $alternatives, $nonNestedAlternative);
  87. }
  88. return $this->parameters[$name];
  89. }
  90. /**
  91. * Sets a service container parameter.
  92. *
  93. * @param string $name The parameter name
  94. * @param mixed $value The parameter value
  95. */
  96. public function set($name, $value)
  97. {
  98. $this->parameters[(string) $name] = $value;
  99. }
  100. /**
  101. * {@inheritdoc}
  102. */
  103. public function has($name)
  104. {
  105. return array_key_exists((string) $name, $this->parameters);
  106. }
  107. /**
  108. * Removes a parameter.
  109. *
  110. * @param string $name The parameter name
  111. */
  112. public function remove($name)
  113. {
  114. unset($this->parameters[(string) $name]);
  115. }
  116. /**
  117. * {@inheritdoc}
  118. */
  119. public function resolve()
  120. {
  121. if ($this->resolved) {
  122. return;
  123. }
  124. $parameters = array();
  125. foreach ($this->parameters as $key => $value) {
  126. try {
  127. $value = $this->resolveValue($value);
  128. $parameters[$key] = $this->unescapeValue($value);
  129. } catch (ParameterNotFoundException $e) {
  130. $e->setSourceKey($key);
  131. throw $e;
  132. }
  133. }
  134. $this->parameters = $parameters;
  135. $this->resolved = true;
  136. }
  137. /**
  138. * Replaces parameter placeholders (%name%) by their values.
  139. *
  140. * @param mixed $value A value
  141. * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
  142. *
  143. * @return mixed The resolved value
  144. *
  145. * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist
  146. * @throws ParameterCircularReferenceException if a circular reference if detected
  147. * @throws RuntimeException when a given parameter has a type problem
  148. */
  149. public function resolveValue($value, array $resolving = array())
  150. {
  151. if (\is_array($value)) {
  152. $args = array();
  153. foreach ($value as $k => $v) {
  154. $args[\is_string($k) ? $this->resolveValue($k, $resolving) : $k] = $this->resolveValue($v, $resolving);
  155. }
  156. return $args;
  157. }
  158. if (!\is_string($value) || 2 > \strlen($value)) {
  159. return $value;
  160. }
  161. return $this->resolveString($value, $resolving);
  162. }
  163. /**
  164. * Resolves parameters inside a string.
  165. *
  166. * @param string $value The string to resolve
  167. * @param array $resolving An array of keys that are being resolved (used internally to detect circular references)
  168. *
  169. * @return string The resolved string
  170. *
  171. * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist
  172. * @throws ParameterCircularReferenceException if a circular reference if detected
  173. * @throws RuntimeException when a given parameter has a type problem
  174. */
  175. public function resolveString($value, array $resolving = array())
  176. {
  177. // we do this to deal with non string values (Boolean, integer, ...)
  178. // as the preg_replace_callback throw an exception when trying
  179. // a non-string in a parameter value
  180. if (preg_match('/^%([^%\s]+)%$/', $value, $match)) {
  181. $key = $match[1];
  182. if (isset($resolving[$key])) {
  183. throw new ParameterCircularReferenceException(array_keys($resolving));
  184. }
  185. $resolving[$key] = true;
  186. return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving);
  187. }
  188. return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($resolving, $value) {
  189. // skip %%
  190. if (!isset($match[1])) {
  191. return '%%';
  192. }
  193. $key = $match[1];
  194. if (isset($resolving[$key])) {
  195. throw new ParameterCircularReferenceException(array_keys($resolving));
  196. }
  197. $resolved = $this->get($key);
  198. if (!\is_string($resolved) && !is_numeric($resolved)) {
  199. 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));
  200. }
  201. $resolved = (string) $resolved;
  202. $resolving[$key] = true;
  203. return $this->isResolved() ? $resolved : $this->resolveString($resolved, $resolving);
  204. }, $value);
  205. }
  206. public function isResolved()
  207. {
  208. return $this->resolved;
  209. }
  210. /**
  211. * {@inheritdoc}
  212. */
  213. public function escapeValue($value)
  214. {
  215. if (\is_string($value)) {
  216. return str_replace('%', '%%', $value);
  217. }
  218. if (\is_array($value)) {
  219. $result = array();
  220. foreach ($value as $k => $v) {
  221. $result[$k] = $this->escapeValue($v);
  222. }
  223. return $result;
  224. }
  225. return $value;
  226. }
  227. /**
  228. * {@inheritdoc}
  229. */
  230. public function unescapeValue($value)
  231. {
  232. if (\is_string($value)) {
  233. return str_replace('%%', '%', $value);
  234. }
  235. if (\is_array($value)) {
  236. $result = array();
  237. foreach ($value as $k => $v) {
  238. $result[$k] = $this->unescapeValue($v);
  239. }
  240. return $result;
  241. }
  242. return $value;
  243. }
  244. }