Skip to content

Commit

Permalink
[application] Improve mono-command apps support
Browse files Browse the repository at this point in the history
  • Loading branch information
yannoff committed Sep 16, 2023
1 parent a85b986 commit 510d052
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 70 deletions.
10 changes: 4 additions & 6 deletions bin/demo
Original file line number Diff line number Diff line change
Expand Up @@ -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();
24 changes: 24 additions & 0 deletions bin/mono
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env php
<?php
/**
* This file is part of the yannoff/console library
* (c) Yannoff (https://github.com/yannoff)
*
* @project yannoff/console
* @link https://github.com/yannoff/console
* @license https://github.com/yannoff/console/blob/master/LICENSE
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

// Mute PHP deprecation warnings & notices
error_reporting(E_ERROR);

require __DIR__ . '/../vendor/autoload.php';

use Yannoff\Component\Console\Application;
use Yannoff\Component\Console\Tests\Command\HelloCommand;

(new Application('greeting - mono', '0.0.0', new HelloCommand('hello')))
->run();
30 changes: 30 additions & 0 deletions bin/multi
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env php
<?php
/**
* This file is part of the yannoff/console library
* (c) Yannoff (https://github.com/yannoff)
*
* @project yannoff/console
* @link https://github.com/yannoff/console
* @license https://github.com/yannoff/console/blob/master/LICENSE
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

// Mute PHP deprecation warnings & notices
error_reporting(E_ERROR);

require __DIR__ . '/../vendor/autoload.php';

use Yannoff\Component\Console\Application;
use Yannoff\Component\Console\Tests\Command\HelloCommand;

$application = new Application('greeting - multi', '0.0.0');

$application->addCommands([
new HelloCommand('ciao'),
new HelloCommand('coucou'),
]);

$application->run();
151 changes: 92 additions & 59 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -283,28 +270,90 @@ public function getUsage($tab = Formatter::TAB, $width = Formatter::PAD)
$lines[] = sprintf("${tab}%s <command> [<options>] -- [<arguments>]", $this->script);
$lines[] = "<strong>Commands</strong>";

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();
}

/**
Expand All @@ -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();
}
}
25 changes: 22 additions & 3 deletions src/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/Command/HelpCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
*/
class HelpCommand extends Command
{
/**
* {@inheritdoc}
*/
public function isSystem()
{
return true;
}

/**
* {@inheritdoc}
*/
Expand Down
8 changes: 8 additions & 0 deletions src/Command/VersionCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
*/
class VersionCommand extends Command
{
/**
* {@inheritdoc}
*/
public function isSystem()
{
return true;
}

/**
* {@inheritdoc}
*/
Expand Down
3 changes: 1 addition & 2 deletions src/Tests/Command/HelloCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit 510d052

Please sign in to comment.