diff --git a/bin/demo b/bin/demo index 1a61017..5f075cf 100755 --- a/bin/demo +++ b/bin/demo @@ -20,10 +20,8 @@ require __DIR__ . '/../vendor/autoload.php'; use Yannoff\Component\Console\Application; use Yannoff\Component\Console\Tests\Command\HelloCommand; -$application = new Application('foobar', '0.0.0'); +$application = new Application('greeting', '0.0.0'); -$application->addCommands([ - new HelloCommand(), -]); - -$application->run(); +$application + ->add(new HelloCommand('hello')) + ->run(); diff --git a/bin/mono b/bin/mono new file mode 100755 index 0000000..67e817f --- /dev/null +++ b/bin/mono @@ -0,0 +1,24 @@ +#!/usr/bin/env php +run(); diff --git a/bin/multi b/bin/multi new file mode 100755 index 0000000..1211813 --- /dev/null +++ b/bin/multi @@ -0,0 +1,30 @@ +#!/usr/bin/env php +addCommands([ + new HelloCommand('ciao'), + new HelloCommand('coucou'), +]); + +$application->run(); diff --git a/src/Application.php b/src/Application.php index 1ad533a..6fe7df4 100644 --- a/src/Application.php +++ b/src/Application.php @@ -77,24 +77,32 @@ class Application extends StreamAware implements FormatterAware protected $commands; /** - * Name of the default command to be used if none was supplied + * Is this application a Mono-Command Application ? * - * @var string + * @var bool */ - protected $default; + protected $mono = false; /** * Application constructor. * - * @param string $name The application name - * @param string $version The application version + * @param string $name The application name + * @param string $version The application version + * @param Command|null $main Main command (only for MCA) */ - public function __construct($name, $version) + public function __construct($name, $version, Command $main = null) { $this->name = $name; $this->version = $version; $this->formatter = FormatterFactory::create(); + // For Mono-Command Applications, the command can be passed as a 3rd constructor arg + if ($main instanceof Command) { + $this + ->add($main) + ->mono = true; + } + $this->init(); } @@ -153,31 +161,10 @@ public function run($args = []) } $this->script = array_shift($args); - $command = array_shift($args); - - // Invoke the appropriated command for special global options like --help, --version, etc - switch ($command): - case '--version': - $command = self::COMMAND_VERS; - break; - - case 'list': - case '--help': - case '-h': - case '--usage': - $command = self::COMMAND_HELP; - break; - - case null: - $command = $this->getDefault(); - break; - - default: - break; - endswitch; try { - return $this->get($command)->run($args); + $info = $this->parse($args); + return $this->get($info['command'])->run($info['args']); } catch (LogicException $e) { $error = sprintf('%s, exiting.', (string) $e); $this->iowrite($error); @@ -283,28 +270,90 @@ public function getUsage($tab = Formatter::TAB, $width = Formatter::PAD) $lines[] = sprintf("${tab}%s [] -- []", $this->script); $lines[] = "Commands"; - foreach ($this->commands as $name => $command) { - // Don't display help or version commands - if (in_array($name, [self::COMMAND_HELP, self::COMMAND_VERS])) { - continue; - } - + foreach ($this->getUserCommands() as $name => $command) { $lines[] = sprintf("${tab}%-{$width}s %s", $name, $command->getHelp()); } return implode(Formatter::LF, $lines); } + /** + * Find command name and arguments from the command-line invocation + * + * @param array $args The command-line parameters list + * + * @return array An associative array of the form ['command' => '...', 'args' => array(...)] + */ + public function parse($args) + { + if (in_array('--version', $args)) { + return [ + 'command' => self::COMMAND_VERS, + 'args' => array_filter($args, function ($a) { return $a !== '--version'; }) + ]; + } + + if ($this->isMono()) { + return ['command' => $this->getDefault(), 'args' => $args]; + } + + $command = array_shift($args); + + switch ($command): + case 'list': + case '--help': + case '-h': + case null: + $command = self::COMMAND_HELP; + break; + + default: + break; + endswitch; + + return ['command' => $command, 'args' => $args]; + } + + /** + * Setter for the mono-command application flag + * + * @param bool $mono + * + * @return self + */ + public function setMono($mono) + { + $this->mono = $mono; + + return $this; + } + + /** + * Getter for the mono-command application flag + * + * @return bool + */ + public function isMono() + { + return $this->mono; + } + + /** + * @return Command[] + */ + public function getUserCommands() + { + return array_filter($this->commands, function (Command $command) { return (!$command->isSystem()); }); + } + /** * Hook for initialization tasks, called at the end of the constructor: * - add common commands (help, version) - * - set default command name */ protected function init() { $this - ->addBaseCommands() - ->setDefault(self::COMMAND_HELP); + ->addBaseCommands(); } /** @@ -326,32 +375,16 @@ public function addBaseCommands() * Getter for the default command name * * @return string + * @throws LogicException If the application has no user-defined command */ public function getDefault() { - return $this->default; - } + $commands = $this->getUserCommands(); - /** - * Setter for the default command name - * Allow easy configuration in user-defined applications - * - * @param string|Command $command Name of the default command - * A command object may also be passed, thanks to force to-string type-casting - * - * @return self - * @throws UnknownCommandException - */ - public function setDefault($command) - { - $command = (string) $command; - - if (!$this->has($command)) { - throw new UnknownCommandException($command); + if (0 === count($commands)) { + throw new LogicException('Mono-command applications need at least 1 command defined'); } - $this->default = $command; - - return $this; + return $commands[0]->getName(); } } diff --git a/src/Command.php b/src/Command.php index cccc06f..ff3cb7d 100644 --- a/src/Command.php +++ b/src/Command.php @@ -331,18 +331,37 @@ public function getUsage($tab = Formatter::TAB, $width = Formatter::PAD) return implode(Formatter::LF, $lines) . Formatter::LF; } + /** + * Discriminate user-defined vs built-in commands + * + * @return bool + */ + public function isSystem() + { + return false; + } + /** * Get the command synopsis * - * @param string $tab The tabulation string (defaults to `\n`) + * @param string $tab The tabulation string (defaults to `\t`) * * @return string */ protected function getSynopsis($tab = Formatter::TAB) { - $format = "{$tab}%s %s [options] [--] %s"; + $help = []; + + $help[] = sprintf('%s%s', $tab, $this->application->getScript()); + + if (! $this->getApplication()->isMono()) { + $help[] = $this->name; + } + + $help[] = '[options] [--]'; + $help[] = $this->definition->getArgSynopsis(); - return sprintf($format, $this->application->getScript(), $this->name, $this->definition->getArgSynopsis()); + return implode(' ', $help); } /** diff --git a/src/Command/HelpCommand.php b/src/Command/HelpCommand.php index 5e2fc3e..d9b4865 100644 --- a/src/Command/HelpCommand.php +++ b/src/Command/HelpCommand.php @@ -27,6 +27,14 @@ */ class HelpCommand extends Command { + /** + * {@inheritdoc} + */ + public function isSystem() + { + return true; + } + /** * {@inheritdoc} */ diff --git a/src/Command/VersionCommand.php b/src/Command/VersionCommand.php index 9c6cc44..716880b 100644 --- a/src/Command/VersionCommand.php +++ b/src/Command/VersionCommand.php @@ -25,6 +25,14 @@ */ class VersionCommand extends Command { + /** + * {@inheritdoc} + */ + public function isSystem() + { + return true; + } + /** * {@inheritdoc} */ diff --git a/src/Tests/Command/HelloCommand.php b/src/Tests/Command/HelloCommand.php index ca65b01..ba93d9f 100644 --- a/src/Tests/Command/HelloCommand.php +++ b/src/Tests/Command/HelloCommand.php @@ -31,7 +31,6 @@ public function configure() { // the content of the configure method is almost identical... $this - ->setName('hello') ->setHelp('Hello world') ->setDescription('Hello world demo application') // ...except for argument type delaration @@ -60,7 +59,7 @@ public function execute() // the same goes for the option getter $upper = $this->getOption('upper'); - $message = 'Hello ' . $name; + $message = ucfirst($this->name) . ' ' . $name; if ($upper) { $message = strtoupper($message); }