Skip to content

[Console] Document invokable command #20932

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: 7.3
Choose a base branch
from

Conversation

yceruto
Copy link
Member

@yceruto yceruto commented May 1, 2025

Closes #20553

  • Make invokable commands first-class citizen
  • Move old command definition to a separate section

Pending docs to be updated:

  • components/console/console_arguments.rst (full refactoring required)
  • components/console/helpers/questionhelper.rst (full refactoring required)
  • console/input.rst (full refactoring required)
  • console.rst (full refactoring required)

@yceruto yceruto force-pushed the invokable_command branch from 3d9b930 to a59a80c Compare May 1, 2025 16:40
Copy link
Contributor

@alamirault alamirault left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the harmonization of some command names 👍

Comment on lines +26 to +27
#[AsCommand(name: 'app:create-user')]
class CreateUserCommand
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should make all these changes with the #AsCommand attribute in a separate PR to avoid growing this PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d actually prefer to keep all these related changes in a single PR, since they’re closely tied together. It helps keep the context in one place and makes it easier to review everything as a whole.

In any case, we can go ahead and merge this one, then open a new PR for the remaining updates.

@yceruto
Copy link
Member Author

yceruto commented May 5, 2025

Thanks @alamirault and @OskarStark for your review 🙏

I’m actively looking for someone to help me refactor console.rst, as I’m running out of free-time before the final release.

// ...

public function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(InputInterface $input, OutputInterface $output): int
{
$helper = $this->getHelper('question');
Copy link
Contributor

@94noni 94noni May 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this wont work isnt it ?
or are those console helper injectable in some ways ?

Copy link
Member Author

@yceruto yceruto May 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Yes, there is a way, but I’m not sure if it’s simpler to extend Command or to get the helper via:

public function __invoke(InputInterface $input, OutputInterface $output, Application $application): int
{
    $helper = $application->getHelperSet()->get('question');

    // ...
}

Anyway, this all looks old-fashioned, because SymfonyStyle can do it better. There’s even a note right at the beginning:

.. note::

    As an alternative, consider using the
    :ref:`SymfonyStyle <symfony-style-questions>` to ask questions.

IMO, it’s not just an alternative, but the simplest way to ask questions from now on:

public function __invoke(SymfonyStyle $io): int
{
    $answer = $io->ask(...);
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should do the same here as with invokable commands: start with the simpler approach (SymfonyStyle), then mention alternatives for custom needs. wdyt @symfony/docs ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for documentation I agree yes, basic usage then more advance 👍🏻

a question just: cant we imagine those helper as console service as injectable in the invoke method ? (can open an dedicated issue for later :))

@94noni
Copy link
Contributor

94noni commented May 5, 2025

Pending docs to be updated:

I would also add https://symfony.com/doc/current/console/input.html, wdyt @yceruto ?
I can help on this in a separate unitary PR if its ok

@yceruto
Copy link
Member Author

yceruto commented May 5, 2025

Thanks, Antoine! Sure, go for it.

Here we're discussing input related topics symfony/symfony#59602

Copy link
Member

@javiereguiluz javiereguiluz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yonel, thanks a lot for this massive contribution 🙇

I agree with what you said here:

we can go ahead and merge this one, then open a new PR for the remaining updates

I'll wait a bit to read more opinions, but that's the plan I'd like to follow. Thanks!

Copy link
Member

@wouterj wouterj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with doing the work in steps, but I think the order should be swapped around.

We can't update all examples across the documentation to use features that we didn't document properly. Imho, console/input.rst has to be updated before we can start using #[Option]/#[Argument] in the documentation.
But it would be perfectly fine if all articles in the docs still have outdated (too complex) examples, but the main guides are updated to document both the simple + advanced way.

We must also be careful about updating an example without updating the context it is placed in (I tried to comment on those cases whenever I saw this).

Comment on lines +147 to +148
leverage advanced features like lifecycle hooks: :method:`Symfony\\Component\\Console\\Command\\Command::initialize`,
:method:`Symfony\\Component\\Console\\Command\\Command::interact`, and built-in helpers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
leverage advanced features like lifecycle hooks: :method:`Symfony\\Component\\Console\\Command\\Command::initialize`,
:method:`Symfony\\Component\\Console\\Command\\Command::interact`, and built-in helpers.
leverage advanced features like lifecycle hooks (e.g. :method:`Symfony\\Component\\Console\\Command\\Command::initialize` and
:method:`Symfony\\Component\\Console\\Command\\Command::interact`) and built-in helpers.

Additionally, you can extend the :class:`Symfony\\Component\\Console\\Command\\Command` class to
leverage advanced features like lifecycle hooks: :method:`Symfony\\Component\\Console\\Command\\Command::initialize`,
:method:`Symfony\\Component\\Console\\Command\\Command::interact`, and built-in helpers.

Configuring the Command
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole section needs updating, the text currently no longer matches the updated code example.

I think we can remove it completely, and instead merge it with the previous section. Something like

You can also use ``#[AsCommand]`` to add a description and longer help text for the command::

    // src/Command/CreateUserCommand.php

    #[AsCommand(
        name: 'app:create-user',
        description: 'Creates a new user.', // the command description shown when running "php bin/console list"
        help: 'This command allows you to create a user...', // the command help shown when running the command with the "--help" option
    )]
    class CreateUserCommand
    {
        public function __invoke(): int
        {
            // ...
        }
    }

Additionally, you can extend the :class:`Symfony\\Component\\Console\\Command\\Command`
class to leverage advanced features like lifecycle hooks (e.g.
:method:`Symfony\\Component\\Console\\Command\\Command::initialize`,
:method:`Symfony\\Component\\Console\\Command\\Command::interact`) and built-in
helpers.

// the command help shown when running the command with the "--help" option
->setHelp('This command allows you to create a user...')
;
// ...
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The next section ("Registering the Command") also needs rework, the document now only shows the #[AsCommand] examples, so "You can register the command by adding the AsCommand attribute to it:" is out of place now.

However, we need to document the final paragraph ("If you can't use PHP attributes, register the command as a service [...]") somewhere in this article I think.

@@ -18,16 +18,15 @@ the returned code from the command (return value from command ``execute()``
method)::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph needs rework, it talks about the execute() method which no longer exists in the example.

@@ -18,16 +18,15 @@ the returned code from the command (return value from command ``execute()``
method)::

// ...
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
use Symfony\Component\Console\Command;

@@ -70,7 +59,7 @@ To make your command lazily loaded, either define its name using the PHP
// ...

#[AsCommand(name: 'app:sunshine')]
class SunshineCommand extends Command
class SunshineCommand
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We reuse this command in the example below to configure it using the console.command tag. Is this tag compatible with non-Command classes?

class and pass the ``$input`` and ``$output`` variables as its arguments. Then,
you can start using any of its helpers, such as ``title()``, which displays the
title of the command::
In your `__invoke` method, add an argument of type :class:`Symfony\\Component\\Console\\Style\\SymfonyStyle`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In your `__invoke` method, add an argument of type :class:`Symfony\\Component\\Console\\Style\\SymfonyStyle`.
In your ``__invoke()`` method, add an argument of type :class:`Symfony\\Component\\Console\\Style\\SymfonyStyle`.

// ...

public function execute(InputInterface $input, OutputInterface $output): int
public function __invoke(OutputInterface $output, #[Argument] string $username, #[Argument] string $password): int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR doesn't update https://symfony.com/doc/current/console/input.html (not sure if that is intentional). This means that we never documented #[Argument] or #[Option].

I don't think it's a good idea to update other examples with these attributes as long as we don't document the attributes themselves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Console] Add support for invokable commands and input attributes
7 participants