@@ -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