ParameterBag.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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\HttpFoundation;
  11. use Symfony\Component\HttpFoundation\Exception\BadRequestException;
  12. use Symfony\Component\HttpFoundation\Exception\UnexpectedValueException;
  13. /**
  14. * ParameterBag is a container for key/value pairs.
  15. *
  16. * @author Fabien Potencier <fabien@symfony.com>
  17. *
  18. * @implements \IteratorAggregate<string, mixed>
  19. */
  20. class ParameterBag implements \IteratorAggregate, \Countable
  21. {
  22. /**
  23. * @param array<string, mixed> $parameters
  24. */
  25. public function __construct(
  26. protected array $parameters = [],
  27. ) {
  28. }
  29. /**
  30. * Returns the parameters.
  31. *
  32. * @template TKey of string|null
  33. *
  34. * @param TKey $key The name of the parameter to return or null to get them all
  35. *
  36. * @return (TKey is null ? array<string, mixed> : array<mixed>)
  37. *
  38. * @throws BadRequestException if the value is not an array
  39. */
  40. public function all(?string $key = null): array
  41. {
  42. if (null === $key) {
  43. return $this->parameters;
  44. }
  45. if (!\is_array($value = $this->parameters[$key] ?? [])) {
  46. throw new BadRequestException(\sprintf('Unexpected value for parameter "%s": expecting "array", got "%s".', $key, get_debug_type($value)));
  47. }
  48. return $value;
  49. }
  50. /**
  51. * Returns the parameter keys.
  52. *
  53. * @return list<string>
  54. */
  55. public function keys(): array
  56. {
  57. return array_keys($this->parameters);
  58. }
  59. /**
  60. * Replaces the current parameters by a new set.
  61. *
  62. * @param array<string, mixed> $parameters
  63. */
  64. public function replace(array $parameters = []): void
  65. {
  66. $this->parameters = $parameters;
  67. }
  68. /**
  69. * Adds parameters.
  70. *
  71. * @param array<string, mixed> $parameters
  72. */
  73. public function add(array $parameters = []): void
  74. {
  75. $this->parameters = array_replace($this->parameters, $parameters);
  76. }
  77. public function get(string $key, mixed $default = null): mixed
  78. {
  79. return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
  80. }
  81. public function set(string $key, mixed $value): void
  82. {
  83. $this->parameters[$key] = $value;
  84. }
  85. /**
  86. * Returns true if the parameter is defined.
  87. */
  88. public function has(string $key): bool
  89. {
  90. return \array_key_exists($key, $this->parameters);
  91. }
  92. /**
  93. * Removes a parameter.
  94. */
  95. public function remove(string $key): void
  96. {
  97. unset($this->parameters[$key]);
  98. }
  99. /**
  100. * Returns the alphabetic characters of the parameter value.
  101. *
  102. * @throws UnexpectedValueException if the value cannot be converted to string
  103. */
  104. public function getAlpha(string $key, string $default = ''): string
  105. {
  106. return preg_replace('/[^[:alpha:]]/', '', $this->getString($key, $default));
  107. }
  108. /**
  109. * Returns the alphabetic characters and digits of the parameter value.
  110. *
  111. * @throws UnexpectedValueException if the value cannot be converted to string
  112. */
  113. public function getAlnum(string $key, string $default = ''): string
  114. {
  115. return preg_replace('/[^[:alnum:]]/', '', $this->getString($key, $default));
  116. }
  117. /**
  118. * Returns the digits of the parameter value.
  119. *
  120. * @throws UnexpectedValueException if the value cannot be converted to string
  121. */
  122. public function getDigits(string $key, string $default = ''): string
  123. {
  124. return preg_replace('/[^[:digit:]]/', '', $this->getString($key, $default));
  125. }
  126. /**
  127. * Returns the parameter as string.
  128. *
  129. * @throws UnexpectedValueException if the value cannot be converted to string
  130. */
  131. public function getString(string $key, string $default = ''): string
  132. {
  133. $value = $this->get($key, $default);
  134. if (!\is_scalar($value) && !$value instanceof \Stringable) {
  135. throw new UnexpectedValueException(\sprintf('Parameter value "%s" cannot be converted to "string".', $key));
  136. }
  137. return (string) $value;
  138. }
  139. /**
  140. * Returns the parameter value converted to integer.
  141. *
  142. * @throws UnexpectedValueException if the value cannot be converted to integer
  143. */
  144. public function getInt(string $key, int $default = 0): int
  145. {
  146. return $this->filter($key, $default, \FILTER_VALIDATE_INT, ['flags' => \FILTER_REQUIRE_SCALAR]);
  147. }
  148. /**
  149. * Returns the parameter value converted to boolean.
  150. *
  151. * @throws UnexpectedValueException if the value cannot be converted to a boolean
  152. */
  153. public function getBoolean(string $key, bool $default = false): bool
  154. {
  155. return $this->filter($key, $default, \FILTER_VALIDATE_BOOL, ['flags' => \FILTER_REQUIRE_SCALAR]);
  156. }
  157. /**
  158. * Returns the parameter value converted to an enum.
  159. *
  160. * @template T of \BackedEnum
  161. *
  162. * @param class-string<T> $class
  163. * @param ?T $default
  164. *
  165. * @return ?T
  166. *
  167. * @psalm-return ($default is null ? T|null : T)
  168. *
  169. * @throws UnexpectedValueException if the parameter value cannot be converted to an enum
  170. */
  171. public function getEnum(string $key, string $class, ?\BackedEnum $default = null): ?\BackedEnum
  172. {
  173. $value = $this->get($key);
  174. if (null === $value) {
  175. return $default;
  176. }
  177. try {
  178. return $class::from($value);
  179. } catch (\ValueError|\TypeError $e) {
  180. throw new UnexpectedValueException(\sprintf('Parameter "%s" cannot be converted to enum: ', $key).$e->getMessage().'.', $e->getCode(), $e);
  181. }
  182. }
  183. /**
  184. * Filter key.
  185. *
  186. * @param int $filter FILTER_* constant
  187. * @param int|array{flags?: int, options?: array} $options Flags from FILTER_* constants
  188. *
  189. * @see https://php.net/filter-var
  190. *
  191. * @throws UnexpectedValueException if the parameter value is a non-stringable object
  192. * @throws UnexpectedValueException if the parameter value is invalid and \FILTER_NULL_ON_FAILURE is not set
  193. */
  194. public function filter(string $key, mixed $default = null, int $filter = \FILTER_DEFAULT, mixed $options = []): mixed
  195. {
  196. $value = $this->get($key, $default);
  197. // Always turn $options into an array - this allows filter_var option shortcuts.
  198. if (!\is_array($options) && $options) {
  199. $options = ['flags' => $options];
  200. }
  201. // Add a convenience check for arrays.
  202. if (\is_array($value) && !isset($options['flags'])) {
  203. $options['flags'] = \FILTER_REQUIRE_ARRAY;
  204. }
  205. if (\is_object($value) && !$value instanceof \Stringable) {
  206. throw new UnexpectedValueException(\sprintf('Parameter value "%s" cannot be filtered.', $key));
  207. }
  208. if ((\FILTER_CALLBACK & $filter) && !(($options['options'] ?? null) instanceof \Closure)) {
  209. throw new \InvalidArgumentException(\sprintf('A Closure must be passed to "%s()" when FILTER_CALLBACK is used, "%s" given.', __METHOD__, get_debug_type($options['options'] ?? null)));
  210. }
  211. $options['flags'] ??= 0;
  212. $nullOnFailure = $options['flags'] & \FILTER_NULL_ON_FAILURE;
  213. $options['flags'] |= \FILTER_NULL_ON_FAILURE;
  214. $value = filter_var($value, $filter, $options);
  215. if (null !== $value || $nullOnFailure) {
  216. return $value;
  217. }
  218. throw new \UnexpectedValueException(\sprintf('Parameter value "%s" is invalid and flag "FILTER_NULL_ON_FAILURE" was not set.', $key));
  219. }
  220. /**
  221. * Returns an iterator for parameters.
  222. *
  223. * @return \ArrayIterator<string, mixed>
  224. */
  225. public function getIterator(): \ArrayIterator
  226. {
  227. return new \ArrayIterator($this->parameters);
  228. }
  229. /**
  230. * Returns the number of parameters.
  231. */
  232. public function count(): int
  233. {
  234. return \count($this->parameters);
  235. }
  236. }