11

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?

2
  • i think your solution is just fine, why you think it´s brittle ? Commented Feb 4, 2016 at 14:44
  • I don't like that it works outside of the existing input abstractions. It's also not the most readable code. If you see something like if ($this->hasNoArguments()) { $this->addOption("help", true) }, it's more readable. Commented Feb 4, 2016 at 15:26

3 Answers 3

11

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);
}
Sign up to request clarification or add additional context in comments.

Comments

3

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 }

2 Comments

Thanks. I think your answer, while more verbose and requiring an additional class, is likely more of the "right way" vs my own one below because it abstracts things a bit better. When I get the chance, I'll test it and if it's right, accept your answer. I'm away this week so it may be a while before I get to it :)
You're welcome. My primary objective is that if people has the same kind of need, he can find an answer here. And now I know how use the built-in HelpCommand. So thank's for your good question
0

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');

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.