Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Console] RunInvokableCommand viaexecute() method.#60353

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Conversation

crynobone
Copy link
Contributor

@crynobonecrynobone commentedMay 6, 2025
edited
Loading

QA
Branch?7.3
Bug fix?no
New feature?no
Deprecations?no
Issuesn/a
LicenseMIT

Laravel Framework always allows handling a command either viahandle() method or__invoke() method. The changes made for Symfony 7.3 will create an issue on existing Laravel applications on Laravel 11 and 12 when version 7.3.0 becomes available.

Having the changes in this PR will allow Laravel to overrideconfigureAsInvokableCommand() to keep the current behavior.


Given the following class on a basic Laravel installation:

<?phpnamespaceApp\Console\Commands;useIlluminate\Console\Command;useIlluminate\Contracts\Config\RepositoryasConfigRepository;class HelloWorldCommandextends Command{/**     * The name of the console command.     *     * @var string     */protected$name ='app:hello-world';/**     * The console command description.     *     * @var string     */protected$description ='Command description';/**     * Execute the console command.     */publicfunction__invoke(ConfigRepository$config)    {$this->components->info($config->get('app.name'));returnself::SUCCESS;    }}

Result on Symfony 7.2

CleanShot 2025-05-06 at 15 02 44

Result on Symfony 7.3

CleanShot 2025-05-06 at 15 03 26

@carsonbot
Copy link

It looks like you unchecked the "Allow edits from maintainer" box. That is fine, but please note that if you have multiple commits, you'll need to squash your commits into one before this can be merged. Or, you can check the "Allow edits from maintainers" box and the maintainer can squash for you.

Cheers!

Carsonbot

@crynobonecrynobone changed the title[Console] Set InvokableCommand via method instead of directly within …[Console] Set InvokableCommand via method instead of directly within__construct()May 6, 2025
@OskarStark
Copy link
Contributor

friendly ping@yceruto

@crynobonecrynobone changed the title[Console] Set InvokableCommand via method instead of directly within__construct()[Console] Set InvokableCommand via a method instead of directly within__construct()May 6, 2025
@xabbuh
Copy link
Member

Can you elaborate a bit on why the upcoming changes for 7.3 are causing issues for Laravel and how the proposed change would fix it? Right now, I have some trouble understanding why this is necessary.

@crynobone
Copy link
ContributorAuthor

Given the following class on a basic Laravel installation:

<?phpnamespaceApp\Console\Commands;useIlluminate\Console\Command;useIlluminate\Contracts\Config\RepositoryasConfigRepository;class HelloWorldCommandextends Command{/**     * The name of the console command.     *     * @var string     */protected$name ='app:hello-world';/**     * The console command description.     *     * @var string     */protected$description ='Command description';/**     * Execute the console command.     */publicfunction__invoke(ConfigRepository$config)    {$this->components->info($config->get('app.name'));returnself::SUCCESS;    }}

Result on Symfony 7.2

CleanShot 2025-05-06 at 15 02 44

Result on Symfony 7.3

CleanShot 2025-05-06 at 15 03 26

@crynobone
Copy link
ContributorAuthor

Sinceis_callable($this) check was made directly in__construct() and set the command to be handled viaInvokableCommand, this skips any preparation that Laravel made withinCommand::execute() method, including IoC injection (see example above).

@@ -134,9 +134,7 @@ public function __construct(?string $name = null)
$this->setHelp($attribute?->help ?? '');
}

if (\is_callable($this)) {
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if the fix instead could be to additionally check here that theexecute() method is not overridden.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

In the example,App\Console\Commands\HelloWorldCommand doesn't have anexecute() method, onlyIlluminate\Console\Command has it.

It would be nice if Symfony introduce an interface to handle invokable command (as this is a new feature for Symfony 7.3) so it doesn't rely onis_callable().

Copy link
Member

Choose a reason for hiding this comment

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

For the check I had in mind it doesn't really matter if it's an intermediate class or the actual command class that implementsexecute(). We would just ensure that the class declaring the method is not Symfony'sCommand class.

Copy link
Member

Choose a reason for hiding this comment

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

It would be nice if Symfony introduce an interface to handle invokable command (as this is a new feature for Symfony 7.3) so it doesn't rely on is_callable().

An interface is not desired on our side as we want commands to be any callable really.

Copy link
Member

@GromNaNGromNaNMay 6, 2025
edited
Loading

Choose a reason for hiding this comment

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

In the example,App\Console\Commands\HelloWorldCommand doesn't have an execute() method, onlyIlluminate\Console\Command has it.

This confirms that theexecute() method is overridden inIlluminate\Console\Command.

I wonder if the fix instead could be to additionally check here that the execute() method is not overridden.

I agree with this solution, here's an implementation.

if (\is_callable($this) && (new \ReflectionMethod($this,'execute'))->getDeclaringClass()->name !==self::class) {$this->code =newInvokableCommand($this,$this(...));}

@crynobone The solution you propose solves the issue you raise, but creates a new one because it introduces a newprotected method tied to an implementation detail and becomes a maintenance constraint.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Nice, happy if anyone take over and make a PR for that as I will only be able to work on it much later today/tomorrow. Thank you

Copy link
Member

Choose a reason for hiding this comment

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

I agree to add only && (new \ReflectionMethod($this, 'execute'))->getDeclaringClass()->name !== self::class to this line, that does solve the original issue, right? please also add a new test around this case, thanks!

Alternatively wouldn't it make more sense to resolve/invoke $this->code from execute() method, otherwise throw LogicException?

I don’t think it’s a good idea: invokable code detection should happen as early as possible due to its configuration in thegetNativeDefinition() method (that’s why we perform this check in the constructor). Also, moving thecode invocation to theexecute() method will break the priority behavior, and thus affect some apps relying on->setCode() having higher priority overexecute() (which is currently the case).

chalasr and GromNaN reacted with thumbs up emoji
Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

My suggestion mainly moving changes fromrun() toexecute(). I don't see how that would make a different.

Copy link
Member

@GromNaNGromNaNMay 6, 2025
edited
Loading

Choose a reason for hiding this comment

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

I opened#60361

The value of$this->code influences how the command definition is generated.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks@GromNaN

@crynobonecrynobone marked this pull request as draftMay 6, 2025 10:33
@crynobonecrynoboneforce-pushed theconfigure-invokable-command branch from089ea87 tof4503b6CompareMay 6, 2025 13:22
@crynobonecrynobone changed the title[Console] Set InvokableCommand via a method instead of directly within__construct()[Console] RunInvokableCommand viaexecute() method.May 6, 2025
@crynobonecrynobone marked this pull request as ready for reviewMay 6, 2025 13:24
@carsonbot
Copy link

It looks like you unchecked the "Allow edits from maintainer" box. That is fine, but please note that if you have multiple commits, you'll need to squash your commits into one before this can be merged. Or, you can check the "Allow edits from maintainers" box and the maintainer can squash for you.

Cheers!

Carsonbot

@yceruto
Copy link
Member

Let's close here in favor of#60361

Thanks@crynobone for reporting it and suggest a solution!

chalasr added a commit that referenced this pull requestMay 6, 2025
…ority over `__invoke()` (GromNaN)This PR was merged into the 7.3 branch.Discussion----------[Console] Ensure overriding `Command::execute()` keeps priority over `__invoke()`| Q             | A| ------------- | ---| Branch?       | 7.3| Bug fix?      | yes| New feature?  | no| Deprecations? | no| Issues        | Replace#60353| License       | MITImplements#60353 (comment)Commits-------f77c403 Ensure overriding Command::execute() keep priority over __invoke
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@GromNaNGromNaNGromNaN left review comments

@xabbuhxabbuhxabbuh left review comments

@ycerutoycerutoyceruto left review comments

@chalasrchalasrAwaiting requested review from chalasrchalasr is a code owner

Assignees
No one assigned
Projects
None yet
Milestone
7.3
Development

Successfully merging this pull request may close these issues.

7 participants
@crynobone@carsonbot@OskarStark@xabbuh@yceruto@GromNaN@chalasr

[8]ページ先頭

©2009-2025 Movatter.jp