You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

306 lines
6.9 KiB

3 years ago
  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\Console\Question;
  11. use Symfony\Component\Console\Exception\InvalidArgumentException;
  12. use Symfony\Component\Console\Exception\LogicException;
  13. /**
  14. * Represents a Question.
  15. *
  16. * @author Fabien Potencier <fabien@symfony.com>
  17. */
  18. class Question
  19. {
  20. private $question;
  21. private $attempts;
  22. private $hidden = false;
  23. private $hiddenFallback = true;
  24. private $autocompleterCallback;
  25. private $validator;
  26. private $default;
  27. private $normalizer;
  28. private $trimmable = true;
  29. private $multiline = false;
  30. /**
  31. * @param string $question The question to ask to the user
  32. * @param mixed $default The default answer to return if the user enters nothing
  33. */
  34. public function __construct(string $question, $default = null)
  35. {
  36. $this->question = $question;
  37. $this->default = $default;
  38. }
  39. /**
  40. * Returns the question.
  41. *
  42. * @return string
  43. */
  44. public function getQuestion()
  45. {
  46. return $this->question;
  47. }
  48. /**
  49. * Returns the default answer.
  50. *
  51. * @return mixed
  52. */
  53. public function getDefault()
  54. {
  55. return $this->default;
  56. }
  57. /**
  58. * Returns whether the user response accepts newline characters.
  59. */
  60. public function isMultiline(): bool
  61. {
  62. return $this->multiline;
  63. }
  64. /**
  65. * Sets whether the user response should accept newline characters.
  66. *
  67. * @return $this
  68. */
  69. public function setMultiline(bool $multiline): self
  70. {
  71. $this->multiline = $multiline;
  72. return $this;
  73. }
  74. /**
  75. * Returns whether the user response must be hidden.
  76. *
  77. * @return bool
  78. */
  79. public function isHidden()
  80. {
  81. return $this->hidden;
  82. }
  83. /**
  84. * Sets whether the user response must be hidden or not.
  85. *
  86. * @param bool $hidden
  87. *
  88. * @return $this
  89. *
  90. * @throws LogicException In case the autocompleter is also used
  91. */
  92. public function setHidden($hidden)
  93. {
  94. if ($this->autocompleterCallback) {
  95. throw new LogicException('A hidden question cannot use the autocompleter.');
  96. }
  97. $this->hidden = (bool) $hidden;
  98. return $this;
  99. }
  100. /**
  101. * In case the response can not be hidden, whether to fallback on non-hidden question or not.
  102. *
  103. * @return bool
  104. */
  105. public function isHiddenFallback()
  106. {
  107. return $this->hiddenFallback;
  108. }
  109. /**
  110. * Sets whether to fallback on non-hidden question if the response can not be hidden.
  111. *
  112. * @param bool $fallback
  113. *
  114. * @return $this
  115. */
  116. public function setHiddenFallback($fallback)
  117. {
  118. $this->hiddenFallback = (bool) $fallback;
  119. return $this;
  120. }
  121. /**
  122. * Gets values for the autocompleter.
  123. *
  124. * @return iterable|null
  125. */
  126. public function getAutocompleterValues()
  127. {
  128. $callback = $this->getAutocompleterCallback();
  129. return $callback ? $callback('') : null;
  130. }
  131. /**
  132. * Sets values for the autocompleter.
  133. *
  134. * @return $this
  135. *
  136. * @throws LogicException
  137. */
  138. public function setAutocompleterValues(?iterable $values)
  139. {
  140. if (\is_array($values)) {
  141. $values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values);
  142. $callback = static function () use ($values) {
  143. return $values;
  144. };
  145. } elseif ($values instanceof \Traversable) {
  146. $valueCache = null;
  147. $callback = static function () use ($values, &$valueCache) {
  148. return $valueCache ?? $valueCache = iterator_to_array($values, false);
  149. };
  150. } else {
  151. $callback = null;
  152. }
  153. return $this->setAutocompleterCallback($callback);
  154. }
  155. /**
  156. * Gets the callback function used for the autocompleter.
  157. */
  158. public function getAutocompleterCallback(): ?callable
  159. {
  160. return $this->autocompleterCallback;
  161. }
  162. /**
  163. * Sets the callback function used for the autocompleter.
  164. *
  165. * The callback is passed the user input as argument and should return an iterable of corresponding suggestions.
  166. *
  167. * @return $this
  168. */
  169. public function setAutocompleterCallback(callable $callback = null): self
  170. {
  171. if ($this->hidden && null !== $callback) {
  172. throw new LogicException('A hidden question cannot use the autocompleter.');
  173. }
  174. $this->autocompleterCallback = $callback;
  175. return $this;
  176. }
  177. /**
  178. * Sets a validator for the question.
  179. *
  180. * @return $this
  181. */
  182. public function setValidator(callable $validator = null)
  183. {
  184. $this->validator = $validator;
  185. return $this;
  186. }
  187. /**
  188. * Gets the validator for the question.
  189. *
  190. * @return callable|null
  191. */
  192. public function getValidator()
  193. {
  194. return $this->validator;
  195. }
  196. /**
  197. * Sets the maximum number of attempts.
  198. *
  199. * Null means an unlimited number of attempts.
  200. *
  201. * @return $this
  202. *
  203. * @throws InvalidArgumentException in case the number of attempts is invalid
  204. */
  205. public function setMaxAttempts(?int $attempts)
  206. {
  207. if (null !== $attempts) {
  208. $attempts = (int) $attempts;
  209. if ($attempts < 1) {
  210. throw new InvalidArgumentException('Maximum number of attempts must be a positive value.');
  211. }
  212. }
  213. $this->attempts = $attempts;
  214. return $this;
  215. }
  216. /**
  217. * Gets the maximum number of attempts.
  218. *
  219. * Null means an unlimited number of attempts.
  220. *
  221. * @return int|null
  222. */
  223. public function getMaxAttempts()
  224. {
  225. return $this->attempts;
  226. }
  227. /**
  228. * Sets a normalizer for the response.
  229. *
  230. * The normalizer can be a callable (a string), a closure or a class implementing __invoke.
  231. *
  232. * @return $this
  233. */
  234. public function setNormalizer(callable $normalizer)
  235. {
  236. $this->normalizer = $normalizer;
  237. return $this;
  238. }
  239. /**
  240. * Gets the normalizer for the response.
  241. *
  242. * The normalizer can ba a callable (a string), a closure or a class implementing __invoke.
  243. *
  244. * @return callable|null
  245. */
  246. public function getNormalizer()
  247. {
  248. return $this->normalizer;
  249. }
  250. protected function isAssoc(array $array)
  251. {
  252. return (bool) \count(array_filter(array_keys($array), 'is_string'));
  253. }
  254. public function isTrimmable(): bool
  255. {
  256. return $this->trimmable;
  257. }
  258. /**
  259. * @return $this
  260. */
  261. public function setTrimmable(bool $trimmable): self
  262. {
  263. $this->trimmable = $trimmable;
  264. return $this;
  265. }
  266. }