Skip to content

Commit 02e2560

Browse files
authored
feat: move the CLI prompts into a Prompt class (#10655)
1 parent 8b0a895 commit 02e2560

13 files changed

Lines changed: 646 additions & 152 deletions

File tree

‎system/CLI/AbstractCommand.php‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,14 @@ final protected function getOutput(): Output
494494
return CLI::getOutput();
495495
}
496496

497+
/**
498+
* Returns a prompt reading through the output of the current run.
499+
*/
500+
protected function getPrompt(): Prompt
501+
{
502+
return new Prompt($this->getOutput());
503+
}
504+
497505
/**
498506
* Configures the command's arguments and options definitions.
499507
*
@@ -811,7 +819,7 @@ private function promptForMissingArguments(array &$arguments, array &$options):
811819
$labels = $this->getArgumentPromptLabels();
812820

813821
foreach ($missing as $name) {
814-
$arguments[] = CLI::prompt($labels[$name] ?? lang('CLI.argumentPrompt', [$name]), null, 'required');
822+
$arguments[] = $this->getPrompt()->ask($labels[$name] ?? lang('CLI.argumentPrompt', [$name]), null, 'required');
815823
CLI::newLine();
816824
}
817825

‎system/CLI/AbstractGeneratorCommand.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ private function confirmCodeIgniterNamespaceOnce(): bool
389389

390390
CLI::write(lang('CLI.generator.usingCINamespace'), 'yellow');
391391

392-
if ($this->isInteractive() && CLI::prompt(lang('CLI.generator.confirmContinue'), ['y', 'n'], 'required') === 'n') {
392+
if ($this->isInteractive() && $this->getPrompt()->ask(lang('CLI.generator.confirmContinue'), ['y', 'n'], 'required') === 'n') {
393393
CLI::write(lang('CLI.generator.cancelOperation'), 'yellow');
394394

395395
return $this->confirmedCodeIgniterNamespace = false;

‎system/CLI/CLI.php‎

Lines changed: 19 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
namespace CodeIgniter\CLI;
1515

16-
use CodeIgniter\Exceptions\InvalidArgumentException;
1716
use WeakMap;
1817

1918
/**
@@ -202,6 +201,14 @@ public static function setOutput(Output $output): void
202201
self::$output = $output;
203202
}
204203

204+
/**
205+
* Returns a prompt reading through the current output.
206+
*/
207+
public static function getPrompt(): Prompt
208+
{
209+
return new Prompt(static::getOutput());
210+
}
211+
205212
/**
206213
* Get input from the shell, using readline or the standard STDIN
207214
*
@@ -240,53 +247,7 @@ public static function input(?string $prefix = null): string
240247
*/
241248
public static function prompt(string $field, $options = null, $validation = null): string
242249
{
243-
$extraOutput = '';
244-
$default = '';
245-
246-
if (isset($validation) && ! is_array($validation) && ! is_string($validation)) {
247-
throw new InvalidArgumentException('$rules can only be of type string|array');
248-
}
249-
250-
if (! is_array($validation)) {
251-
$validation = ($validation !== null) ? explode('|', $validation) : [];
252-
}
253-
254-
if (is_string($options)) {
255-
$extraOutput = ' [' . static::color($options, 'green') . ']';
256-
$default = $options;
257-
}
258-
259-
if (is_array($options) && $options !== []) {
260-
$opts = $options;
261-
$extraOutputDefault = static::color((string) $opts[0], 'green');
262-
263-
unset($opts[0]);
264-
265-
if ($opts === []) {
266-
$extraOutput = $extraOutputDefault;
267-
} else {
268-
$extraOutput = '[' . $extraOutputDefault . ', ' . implode(', ', $opts) . ']';
269-
$validation[] = 'in_list[' . implode(', ', $options) . ']';
270-
}
271-
272-
$default = $options[0];
273-
}
274-
275-
$output = static::getOutput();
276-
$output->setCursorAtLineStart(true);
277-
278-
// The reader renders the prompt itself, so readline redraws repaint it instead of erasing it.
279-
$prompt = sprintf('%s%s%s: ', $field, trim($field) !== '' ? ' ' : '', $extraOutput);
280-
$input = trim($output->getInputOutput()->input($prompt));
281-
$input = $input === '' ? (string) $default : $input;
282-
283-
if ($validation !== []) {
284-
while (! static::validate('"' . trim($field) . '"', $input, $validation)) {
285-
$input = static::prompt($field, $options, $validation);
286-
}
287-
}
288-
289-
return $input;
250+
return static::getPrompt()->ask($field, $options, $validation);
290251
}
291252

292253
/**
@@ -301,21 +262,7 @@ public static function prompt(string $field, $options = null, $validation = null
301262
*/
302263
public static function promptByKey($text, array $options, $validation = null): string
303264
{
304-
if (is_string($text)) {
305-
$text = [$text];
306-
} elseif (! is_array($text)) {
307-
throw new InvalidArgumentException('$text can only be of type string|array');
308-
}
309-
310-
self::isZeroOptions($options);
311-
312-
if (($line = array_shift($text)) !== null) {
313-
static::write($line);
314-
}
315-
316-
self::printKeysAndValues($options);
317-
318-
return static::prompt(PHP_EOL . array_shift($text), array_keys($options), $validation);
265+
return static::getPrompt()->askByKey($text, $options, $validation);
319266
}
320267

321268
/**
@@ -329,102 +276,26 @@ public static function promptByKey($text, array $options, $validation = null): s
329276
*/
330277
public static function promptByMultipleKeys(string $text, array $options): array
331278
{
332-
self::isZeroOptions($options);
333-
334-
$extraOutputDefault = static::color('0', 'green');
335-
$opts = $options;
336-
unset($opts[0]);
337-
338-
if ($opts === []) {
339-
$extraOutput = $extraOutputDefault;
340-
} else {
341-
$optsKey = array_keys($opts);
342-
$extraOutput = '[' . $extraOutputDefault . ', ' . implode(', ', $optsKey) . ']';
343-
$extraOutput = 'You can specify multiple values separated by commas.' . PHP_EOL . $extraOutput;
344-
}
345-
346-
static::write($text);
347-
self::printKeysAndValues($options);
348-
static::newLine();
349-
350-
$input = static::prompt($extraOutput);
351-
$input = ($input === '') ? '0' : $input; // 0 is default
352-
353-
// validation
354-
while (true) {
355-
$pattern = preg_match_all('/^\d+(,\d+)*$/', trim($input));
356-
357-
// separate input by comma and convert all to an int[]
358-
$inputToArray = array_map(static fn ($value): int => (int) $value, explode(',', $input));
359-
// find max from key of $options
360-
$maxOptions = array_key_last($options);
361-
// find max from input
362-
$maxInput = max($inputToArray);
363-
364-
// return the prompt again if $input contain(s) non-numeric character, except a comma.
365-
// And if max from $options less than max from input,
366-
// it means user tried to access null value in $options
367-
if ($pattern < 1 || $maxOptions < $maxInput) {
368-
static::error('Please select correctly.');
369-
static::newLine();
370-
371-
$input = static::prompt($extraOutput);
372-
$input = ($input === '') ? '0' : $input;
373-
} else {
374-
break;
375-
}
376-
}
377-
378-
$input = [];
379-
380-
foreach ($options as $key => $description) {
381-
foreach ($inputToArray as $inputKey) {
382-
if ($key === $inputKey) {
383-
$input[$key] = $description;
384-
}
385-
}
386-
}
387-
388-
return $input;
389-
}
390-
391-
/**
392-
* Validation for $options in promptByKey() and promptByMultipleKeys(). Return an error if $options is an empty array.
393-
*
394-
* @param array<array-key, string> $options
395-
*/
396-
private static function isZeroOptions(array $options): void
397-
{
398-
if ($options === []) {
399-
throw new InvalidArgumentException('No options to select from were provided');
400-
}
401-
}
402-
403-
/**
404-
* Print each key and value one by one
405-
*
406-
* @param array<array-key, string> $options
407-
*/
408-
private static function printKeysAndValues(array $options): void
409-
{
410-
// +2 for the square brackets around the key
411-
$keyMaxLength = max(array_map(mb_strwidth(...), array_keys($options))) + 2;
412-
413-
foreach ($options as $key => $description) {
414-
$name = str_pad(' [' . $key . '] ', $keyMaxLength + 4, ' ');
415-
static::write(static::color($name, 'green') . static::wrap($description, 125, $keyMaxLength + 4));
416-
}
279+
return static::getPrompt()->askByMultipleKeys($text, $options);
417280
}
418281

419282
/**
420283
* Validate one prompt "field" at a time
421284
*
285+
* @deprecated 4.8.0 Override `Prompt::validate()` instead.
286+
*
422287
* @param string $field Prompt "field" output
423288
* @param string $value Input value
424289
* @param list<string>|string $rules Validation rules
425290
*/
426291
protected static function validate(string $field, string $value, $rules): bool
427292
{
293+
@trigger_error(sprintf(
294+
'Since v4.8.0, "%s()" is deprecated. Override "%s::validate()" instead.',
295+
__METHOD__,
296+
Prompt::class,
297+
), E_USER_DEPRECATED);
298+
428299
$label = $field;
429300
$field = 'temp';
430301
$validation = service('validation', null, false);

0 commit comments

Comments
 (0)