ParameterBag.php 8.0 KB

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