diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 71b6041..e22b48c 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -55,6 +55,7 @@ require_once $rootDir.'/tests/webfiori/tests/cli/TestCommand.php'; require_once $rootDir.'/tests/webfiori/tests/cli/testCommands/Command00.php'; require_once $rootDir.'/tests/webfiori/tests/cli/testCommands/Command01.php'; +require_once $rootDir.'/tests/webfiori/tests/cli/testCommands/Command03.php'; require_once $rootDir.'/tests/webfiori/tests/cli/testCommands/WithExceptionCommand.php'; require_once $rootDir.'/tests/webfiori/tests/TestStudent.php'; require_once $rootDir.'/tests/webfiori/tests/TestStudent2.php'; diff --git a/tests/webfiori/tests/cli/RunnerTest.php b/tests/webfiori/tests/cli/RunnerTest.php index 43139dd..27e0a75 100644 --- a/tests/webfiori/tests/cli/RunnerTest.php +++ b/tests/webfiori/tests/cli/RunnerTest.php @@ -542,6 +542,30 @@ public function testRunner21() { "Command Exit Status: -1\n" ], $output); } + public function testRunner22() { + $runner = new Runner(); + $runner->register(new testCommands\Command03()); + $runner->setArgsVector([ + 'entry.php', + 'run-another', + 'arg-1' => 'Nice', + 'arg-2' => 'Cool' + ]); + $runner->setInputStream(new ArrayInputStream([ + + ])); + $runner->setOutputStream(new ArrayOutputStream()); + $exitCode = $runner->start(); + $output = $runner->getOutput(); + $this->assertEquals([ + "Running Sub Command\n", + "System version: 1.0.0\n", + "Nice\n", + "Cool\n", + "Ur\n", + "Done\n", + ], $output); + } /** * @test */ diff --git a/tests/webfiori/tests/cli/testCommands/Command03.php b/tests/webfiori/tests/cli/testCommands/Command03.php new file mode 100644 index 0000000..86a9448 --- /dev/null +++ b/tests/webfiori/tests/cli/testCommands/Command03.php @@ -0,0 +1,19 @@ +println('Running Sub Command'); + $this->getOwner()->register(new Command01()); + $this->execSubCommand('show-v', ['arg-3' => 'Ur']); + $this->println('Done'); + return 0; + } + +} diff --git a/webfiori/cli/CLICommand.php b/webfiori/cli/CLICommand.php index 9dc6563..34ab7a2 100644 --- a/webfiori/cli/CLICommand.php +++ b/webfiori/cli/CLICommand.php @@ -306,7 +306,6 @@ public function confirm(string $confirmTxt, bool $default = null) : bool { public function error(string $message) { $this->printMsg($message, 'Error', 'light-red'); } - /** * Execute the command. * @@ -320,17 +319,23 @@ public function error(string $message) { public function excCommand() : int { $retVal = -1; - foreach ($this->getOwner()->getArgs() as $arg) { - $this->addArgument($arg); + $owner = $this->getOwner(); + + if ($owner !== null) { + foreach ($owner->getArgs() as $arg) { + $this->addArgument($arg); + } } if ($this->parseArgsHelper() && $this->checkIsArgsSetHelper()) { $retVal = $this->exec(); } - foreach ($this->getOwner()->getArgs() as $arg) { - $this->removeArgument($arg->getName()); - $arg->resetValue(); + if ($owner !== null) { + foreach ($owner->getArgs() as $arg) { + $this->removeArgument($arg->getName()); + $arg->resetValue(); + } } return $retVal; @@ -347,6 +352,33 @@ public function excCommand() : int { * */ public abstract function exec() : int; + /** + * Execute a registered command using a sub-runner. + * + * This method can be used to execute a registered command within the runner + * using another + * runner instance which shares argsv, input and output streams with the + * main runner. It can be used to invoke another command from within a + * running command. + * + * @param string $name The name of the command. It must be a part of + * registered commands. + * + * @param array $additionalArgs An associative array that represents additional arguments + * to be passed to the command. + * + * @return int The method will return an integer that represent exit status + * code of the command after execution. + */ + public function execSubCommand(string $name, $additionalArgs = []) : int { + $owner = $this->getOwner(); + + if ($owner === null) { + return -1; + } + + return $owner->runCommandAsSub($name, $additionalArgs); + } /** * Returns an object that holds argument info if the command. * @@ -406,11 +438,13 @@ public function getArgValue(string $optionName) { $arg = $this->getArg($trimmedOptName); if ($arg !== null) { - if ($arg->getValue() !== null && !($this->getOwner() !== null && $this->getOwner()->isInteractive())) { + $owner = $this->getOwner(); + + if ($arg->getValue() !== null && !($owner !== null && $owner->isInteractive())) { return $arg->getValue(); } - return Argument::extractValue($trimmedOptName, $this->getOwner()); + return Argument::extractValue($trimmedOptName, $owner); } return null; diff --git a/webfiori/cli/Runner.php b/webfiori/cli/Runner.php index 719b8f1..941f61d 100644 --- a/webfiori/cli/Runner.php +++ b/webfiori/cli/Runner.php @@ -423,6 +423,45 @@ public function runCommand(CLICommand $c = null, array $args = [], bool $ansi = return $this->commandExitVal; } + /** + * Execute a registered command using a sub-runner. + * + * This method can be used to execute a registered command using another + * runner instance which shares argsv, input and output streams with the + * main runner. It can be used to invoke another command from within a + * running command. + * + * @param string $commandName The name of the command. It must be a part of + * registered commands. + * + * @param array $additionalArgs An associative array that represents additional arguments + * to be passed to the command. + * + * @return int The method will return an integer that represent exit status + * code of the command after execution. + */ + public function runCommandAsSub(string $commandName, array $additionalArgs = []) : int { + $c = $this->getCommandByName($commandName); + + if ($c === null) { + return -1; + } + $subRunner = new Runner(); + $subRunner->setInputStream($this->getInputStream()); + $subRunner->setOutputStream($this->getOutputStream()); + $subRunner->register($c); + $args = $this->getArgsVector(); + $args[0] = $commandName; + $code = $subRunner->runCommand(null, array_merge($args, $additionalArgs), $this->isAnsi); + + if ($code != 0) { + if ($this->getActiveCommand() !== null) { + $this->getActiveCommand()->warning('Command "'.$commandName.'" exited with code '.$code.'.'); + } + } + + return $code; + } /** * Sets the command which is currently in execution stage.