Skip to content

Commit f7df2c6

Browse files
committed
feat: split the CLI output into Output, Terminal, ProgressBar, Table and Color
1 parent a3f2d71 commit f7df2c6

35 files changed

Lines changed: 2597 additions & 584 deletions

‎rector.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
// Exclude test file because `is_cli()` is mocked and Rector might remove needed parameters.
102102
RemoveExtraParametersRector::class => [
103103
__DIR__ . '/tests/system/CLI/CLITest.php',
104+
__DIR__ . '/tests/system/CLI/OutputTest.php',
104105
__DIR__ . '/tests/system/Debug/ToolbarTest.php',
105106
],
106107

‎system/CLI/AbstractCommand.php‎

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -438,43 +438,60 @@ public function setInteractive(bool $interactive): static
438438
*
439439
* @param list<string> $arguments Parsed arguments from command line.
440440
* @param array<string, list<string|null>|string|null> $options Parsed options from command line.
441+
* @param Output|null $output The output to run against, or `null` for the current one.
441442
*
442443
* @throws ArgumentCountMismatchException
443444
* @throws CommandNotAvailableException
444445
* @throws LogicException
445446
* @throws OptionValueMismatchException
446447
* @throws UnknownOptionException
447448
*/
448-
final public function run(array $arguments, array $options): int
449+
final public function run(array $arguments, array $options, ?Output $output = null): int
449450
{
450-
if (! $this->isAvailable()) {
451-
throw new CommandNotAvailableException(lang('Commands.notAvailable', [$this->name]));
452-
}
451+
$previous = CLI::getOutput();
452+
453+
CLI::setOutput($output ?? $previous);
454+
455+
try {
456+
if (! $this->isAvailable()) {
457+
throw new CommandNotAvailableException(lang('Commands.notAvailable', [$this->name]));
458+
}
459+
460+
// Reset per-run interactive state from the current options.
461+
$this->runtimeInteractive = $this->hasUnboundOption('no-interaction', $options) ? false : null;
453462

454-
// Reset per-run interactive state from the current options.
455-
$this->runtimeInteractive = $this->hasUnboundOption('no-interaction', $options) ? false : null;
463+
$this->initialize($arguments, $options);
456464

457-
$this->initialize($arguments, $options);
465+
if ($this->isInteractive()) {
466+
if ($this instanceof PromptsForMissingInputInterface) {
467+
$this->promptForMissingArguments($arguments, $options);
468+
}
458469

459-
if ($this->isInteractive()) {
460-
if ($this instanceof PromptsForMissingInputInterface) {
461-
$this->promptForMissingArguments($arguments, $options);
470+
$this->interact($arguments, $options);
462471
}
463472

464-
$this->interact($arguments, $options);
465-
}
473+
$this->unboundArguments = $arguments;
474+
$this->unboundOptions = $options;
466475

467-
$this->unboundArguments = $arguments;
468-
$this->unboundOptions = $options;
476+
[$boundArguments, $boundOptions] = $this->bind($arguments, $options);
469477

470-
[$boundArguments, $boundOptions] = $this->bind($arguments, $options);
478+
$this->validate($boundArguments, $boundOptions);
471479

472-
$this->validate($boundArguments, $boundOptions);
480+
$this->validatedArguments = $boundArguments;
481+
$this->validatedOptions = $boundOptions;
473482

474-
$this->validatedArguments = $boundArguments;
475-
$this->validatedOptions = $boundOptions;
483+
return $this->execute($boundArguments, $boundOptions);
484+
} finally {
485+
CLI::setOutput($previous);
486+
}
487+
}
476488

477-
return $this->execute($boundArguments, $boundOptions);
489+
/**
490+
* Returns the output of the current run.
491+
*/
492+
final protected function getOutput(): Output
493+
{
494+
return CLI::getOutput();
478495
}
479496

480497
/**
@@ -573,7 +590,7 @@ abstract protected function execute(array $arguments, array $options): int;
573590
*/
574591
protected function call(string $command, array $arguments = [], array $options = [], ?bool $noInteractionOverride = null): int
575592
{
576-
return $this->commands->runCommand($command, $arguments, $this->resolveChildInteractiveState($options, $noInteractionOverride));
593+
return $this->runChild($command, $arguments, $options, $noInteractionOverride, $this->getOutput());
577594
}
578595

579596
/**
@@ -585,19 +602,13 @@ protected function call(string $command, array $arguments = [], array $options =
585602
*/
586603
protected function callSilently(string $command, array $arguments = [], array $options = [], ?bool $noInteractionOverride = true): int
587604
{
588-
$priorInputOutput = CLI::getInputOutput();
589-
$priorLastWrite = CLI::getLastWrite();
590-
591-
CLI::setInputOutput(new NullInputOutput());
592-
593-
try {
594-
return $this->call($command, $arguments, $options, $noInteractionOverride);
595-
} finally {
596-
$priorInputOutput instanceof InputOutput
597-
? CLI::setInputOutput($priorInputOutput)
598-
: CLI::resetInputOutput();
599-
CLI::setLastWrite($priorLastWrite);
600-
}
605+
return $this->runChild(
606+
$command,
607+
$arguments,
608+
$options,
609+
$noInteractionOverride,
610+
new Output(new NullInputOutput(), $this->getOutput()->getTerminal()),
611+
);
601612
}
602613

603614
/**
@@ -782,7 +793,7 @@ protected function provideDefaultOptions(): void
782793
private function promptForMissingArguments(array &$arguments, array &$options): void
783794
{
784795
// A null reader cannot satisfy a required prompt, so leave the missing arguments to validation.
785-
if (CLI::getInputOutput() instanceof NullInputOutput) {
796+
if ($this->getOutput()->getInputOutput() instanceof NullInputOutput) {
786797
return;
787798
}
788799

@@ -1182,4 +1193,18 @@ private function createDefaultUsage(): void
11821193

11831194
array_unshift($this->usages, implode(' ', $usage));
11841195
}
1196+
1197+
/**
1198+
* @param list<string> $arguments
1199+
* @param array<string, list<string>|string|null> $options
1200+
*/
1201+
private function runChild(string $command, array $arguments, array $options, ?bool $noInteractionOverride, Output $output): int
1202+
{
1203+
return $this->commands->runCommand(
1204+
$command,
1205+
$arguments,
1206+
$this->resolveChildInteractiveState($options, $noInteractionOverride),
1207+
$output,
1208+
);
1209+
}
11851210
}

0 commit comments

Comments
 (0)