gazareth
gazareth

Reputation: 1154

Symfony console - displaying help for command with no arguments

I'm developing a pretty simple Symfony console application. It has just one command with one argument, and a few options.

I followed this guide to create an extension of the Application class.

This is the normal usage for the app, and it works fine:
php application <argument>

This also works fine (argument with options):
php application.php <argument> --some-option

If someone runs php application.php without any arguments or options, I want it to run as though the user had run php application.php --help.

I do have a working solution but it isn't optimal and is perhaps slightly brittle. In my extended Application class, I overrode the run() method as follows:

/**
 * Override parent method so that --help options is used when app is called with no arguments or options
 *
 * @param InputInterface|null $input
 * @param OutputInterface|null $output
 * @return int
 * @throws \Exception
 */
public function run(InputInterface $input = null, OutputInterface $output = null)
{
    if ($input === null) {
        if (count($_SERVER["argv"]) <= 1) {
            $args = array_merge($_SERVER["argv"], ["--help"]);
            $input = new ArgvInput($args);
        }
    }
    return parent::run($input, $output);
}

By default, Application::run() is called with a null InputInterface, so here I figured I could just check the raw value of the arguments and forcefully add a help option to pass to the parent method.

Is there a better way to achieve this?

Upvotes: 11

Views: 2892

Answers (3)

Jeremy Postlethwaite
Jeremy Postlethwaite

Reputation: 1324

Symfony DescriptorHelper provides formatting options

See the Symfony DescriptorHelper on GitHub

use Symfony\Component\Console\Helper\DescriptorHelper;

public function handle(): int
{
    /**
     * @var array<string, mixed> $options
     */
    $options = $this->options();

    $helper = new DescriptorHelper();
    $helper->describe($this->output, $this, [
        // 'format' => $this->input->getOption('format'),
        'format' => 'txt', // txt|xml|json|md|rst
        // 'raw_text' => $this->input->getOption('raw'),
        // 'raw_text' => false,
    ]);
  • This allows you to format the help as txt (default) or xml|json|md|rst

This is how the Symfony HelpCommand renders help for other commands. We just pass the class instance to $helper->describe($this->output, $this, []) instead of another command.

There are around 100 methods available on the command class instance. It appears that public function help() is not defined, so this could be put into a separate method.

use Symfony\Component\Console\Helper\DescriptorHelper;

/**
 * Get the underlying Symfony output implementation.
 *
 * @return \Symfony\Component\Console\Output\OutputInterface
 */
abstract public function getOutput();

/**
 * @param string $format txt|xml|json|md|rst
 */
public function help(string $format = 'txt', bool $raw_text = false): void
{
    $helper = new DescriptorHelper();
    $helper->describe($this->getOutput(), $this, [
        'format' => $format,
        'raw_text' => $raw_text,
    ]);
}

This will allow you to call help in your command as needed.

Display standard CLI help:

$this->help();

Display the help as JSON:

$this->help('json');

Upvotes: 0

chalasr
chalasr

Reputation: 13167

To do a specific action depending on command, you can use an EventListener which is called when the onConsoleCommand is fired.

The listener class should work as follows :

<?php

namespace AppBundle\EventListener;

use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Command\HelpCommand;

class ConsoleEventListener
{
    public function onConsoleCommand(ConsoleCommandEvent $event)
    {
        $application = $event->getCommand()->getApplication();
        $inputDefinition = $application->getDefinition();

        if ($inputDefinition->getArgumentCount() < 2) {
            $help = new HelpCommand();
            $help->setCommand($event->getCommand());

            return $help->run($event->getInput(), $event->getOutput());
        }
    }
}

The service declaration :

services:
     # ...
     app.console_event_listener:
         class: AppBundle\EventListener\ConsoleEventListener
         tags:
             - { name: kernel.event_listener, event: console.command, method: onConsoleCommand }

Upvotes: 3

gazareth
gazareth

Reputation: 1154

I managed to work out a solution which didn't involve touching the Application class at all. To call the help command from within another command:

/**
 * @param InputInterface $input
 * @param OutputInterface $output
 * @return int
 * @throws \Symfony\Component\Console\Exception\ExceptionInterface
 */
protected function outputHelp(InputInterface $input, OutputInterface $output)
{
    $help = new HelpCommand();
    $help->setCommand($this);
    return $help->run($input, $output);
}

Upvotes: 11

Related Questions