diff --git a/src/Application.php b/src/Application.php index 80ddd6b..b8c2685 100644 --- a/src/Application.php +++ b/src/Application.php @@ -29,6 +29,7 @@ use function array_unshift; use function class_exists; use function implode; +use function is_dir; use function is_object; use function is_string; use function str_replace; @@ -405,6 +406,10 @@ public function commands(array $commands): void */ public function registerCommands(string $namespace, string $basePath): static { + if (!is_dir($basePath)) { + return $this; + } + $this->debugf('register commands from the namespace: %s', $namespace); $length = strlen($basePath) + 1; @@ -430,6 +435,9 @@ public function registerCommands(string $namespace, string $basePath): static */ public function registerGroups(string $namespace, string $basePath): self { + if (!is_dir($basePath)) { + return $this; + } $this->debugf('register groups from the namespace: %s', $namespace); $length = strlen($basePath) + 1; diff --git a/src/Command.php b/src/Command.php index 192677a..f13bcf8 100644 --- a/src/Command.php +++ b/src/Command.php @@ -40,6 +40,14 @@ abstract class Command extends AbstractHandler implements CommandInterface protected ?Controller $group = null; /** + * command argument rules + * + * eg: + * + * [ + * 'arg1' => 'type;desc', + * ] + * * @return array */ protected function getArguments(): array @@ -63,7 +71,7 @@ protected function beforeInitFlagsParser(FlagsParser $fs): void */ protected function afterInitFlagsParser(FlagsParser $fs): void { - $this->debugf('cmd: %s - load command flags configure', $this->getRealCName()); + $this->debugf('cmd: %s - load command flags configure, class: %s', $this->getRealCName(), static::class); $this->configure(); $this->configFlags($fs); diff --git a/src/Decorate/AttachApplicationTrait.php b/src/Decorate/AttachApplicationTrait.php index 83b7636..a28befc 100644 --- a/src/Decorate/AttachApplicationTrait.php +++ b/src/Decorate/AttachApplicationTrait.php @@ -45,14 +45,16 @@ public function getApp(): Application } /** - * @param Application $app + * @param Application|null $app */ - public function setApp(Application $app): void + public function setApp(Application|null $app): void { - $this->app = $app; + if ($app !== null) { + $this->app = $app; - // auto setting $attached - $this->attached = true; + // auto setting $attached + $this->attached = true; + } } /** @@ -168,29 +170,4 @@ public function log(int $level, string $message, array $extra = []): void Console::log($level, $message, $extra); } - - /************************************************************************** - * wrap trigger events - **************************************************************************/ - - /** - * @param string $event - * @param mixed ...$args - * - * @return bool - */ - public function fire(string $event, ...$args): bool - { - $this->debugf("fire event: $event"); - - // if has application instance - if ($this->attached) { - $stop = $this->app->fire($event, ...$args); - if ($stop === false) { - return false; - } - } - - return $this->parentFire($event, ...$args); - } } diff --git a/src/Decorate/SubCommandsWareTrait.php b/src/Decorate/SubCommandsWareTrait.php index ad961a3..e2ce58e 100644 --- a/src/Decorate/SubCommandsWareTrait.php +++ b/src/Decorate/SubCommandsWareTrait.php @@ -117,11 +117,12 @@ protected function subCommands(): array protected function dispatchSub(string $name, array $args): mixed { $subInfo = $this->commands[$name]; - $this->debugf('cmd: %s - dispatch the attached subcommand: %s', $this->getRealName(), $name); + $this->debugf('cmd: %s - dispatch the subcommand: %s', $this->getRealName(), $name); // create and init sub-command $subCmd = $this->createSubCommand($subInfo); $subCmd->setParent($this); + $subCmd->setApp($this->getApp()); $subCmd->setPath($this->path); $subCmd->setInputOutput($this->input, $this->output); @@ -167,7 +168,7 @@ public function addSub(string $name, string|CommandInterface $handler = null, ar $name = $handler::getName(); } - Assert::isFalse(!$name || !$handler, "Command 'name' and 'handler' cannot be empty! name: $name"); + Assert::isFalse(!$name || !$handler, "Command 'name' and 'handler' cannot be empty! name: $name, handler: $handler"); Assert::isFalse(isset($this->commands[$name]), "Command '$name' have been registered!"); $this->validateName($name); diff --git a/src/Handler/AbstractHandler.php b/src/Handler/AbstractHandler.php index 1e7d3d2..af865c0 100644 --- a/src/Handler/AbstractHandler.php +++ b/src/Handler/AbstractHandler.php @@ -130,11 +130,12 @@ public static function aliases(): array */ public function __construct(Input $input = null, Output $output = null) { - $this->input = $input; - $this->output = $output; + // init io stream + $input && $this->setInput($input); + $output && $this->setOutput($output); // init an flags object - $this->flags = new SFlags(); + $this->setFlags(new SFlags()); $this->init(); } @@ -318,6 +319,8 @@ public function run(array $args): mixed return $this->doRun($args); } catch (Throwable $e) { + $this->log(Console::VERB_DEBUG, "cmd: $name - run error: " . $e->getMessage(), ['args' => $args]); + if ($this->isDetached()) { ErrorHandler::new()->handle($e); } else { @@ -348,7 +351,7 @@ protected function doRun(array $args): mixed } // only fire for alone command run. - if ($this->isAlone()) { + if ($this->isAloneCmd()) { $this->fire(ConsoleEvent::COMMAND_RUN_BEFORE, $this); } @@ -375,6 +378,8 @@ protected function doExecute(): mixed */ public function coExecute(): int { + /** @noinspection PhpFullyQualifiedNameUsageInspection */ + /** @noinspection PhpUndefinedNamespaceInspection */ $cid = \Swoole\Coroutine\run(function (): void { $this->execute($this->input, $this->output); }); @@ -432,6 +437,7 @@ protected function prepare(): bool if (function_exists('cli_set_process_title')) { cli_set_process_title($this->processTitle); } elseif (function_exists('setproctitle')) { + /** @noinspection PhpUndefinedFunctionInspection */ setproctitle($this->processTitle); } @@ -444,6 +450,32 @@ protected function prepare(): bool return true; } + /************************************************************************** + * wrap trigger events + **************************************************************************/ + + /** + * @param string $event + * @param mixed ...$args + * + * @return bool + */ + public function fire(string $event, ...$args): bool + { + $this->debugf("fire event: $event"); + + // if has application instance + if ($this->attached) { + $stop = $this->app->fire($event, ...$args); + if ($stop === false) { + return false; + } + } + // TODO pop fire event to parent and app + + return $this->parentFire($event, ...$args); + } + /************************************************************************** * helper methods **************************************************************************/ @@ -496,7 +528,7 @@ public function loadRulesByDocblock(string $method, FlagsParser $fs): void $rftMth = PhpHelper::reflectMethod($this, $method); // parse doc for get flag rules - $dr = DocblockRules::newByDocblock($rftMth->getDocComment()); + $dr = DocblockRules::newByDocblock((string)$rftMth->getDocComment()); $dr->parse(); $fs->addArgsByRules($dr->getArgRules()); @@ -582,11 +614,11 @@ public function getRealDesc(): string /** * @param bool $useReal * - * @return string + * @return string top:sub */ public function getCommandId(bool $useReal = true): string { - return $useReal ? self::getName() : $this->commandName; + return $this->getPath(':'); } /**