-
Notifications
You must be signed in to change notification settings - Fork 11.1k
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
Overriding console command constructor is bad practice #42454
Conversation
Protected commands do not allow code to be pasted into the controller and the use of the "handle" function. Only __invoke method/
Create template for ProtectedCommand
Add descriptions
This confirm will help you choose the type of console command
Define static class property $defaultName and command would not be instantiated before exact call |
I ran a test.class TestCommand extends Command
{
protected $signature = 'test_command';
protected $description = 'Error example';
// this not work
protected static $defaultName = 'test_command';
public function __construct()
{
parent::__construct();
dd("This code is always executed");
}
public function handle(): void
{
// TODO Logic
}
} In console > php artisan
< "This code is always executed" Also the field "$defaultName" is deprecated! |
This seems unnecessary to me, and contrary to Laravel's usual approach. Better just to document and educate. |
There is an element of learning in the current change. /**
* Use "__invoke()" method
* Inject dependencies "__invoke(Application $application)"
*
* This console command is protected.
* To make the current command unprotected change extend class to "Command"
*
* Example: class {{ class }} extends Command {}
* @see Command
*/ There are 2 examples in the comment. The goal of a framework is to solve problems, not create vulnerabilities. I will give an example from the java language (Spring). There are also console commands. But they don't have a constructor. And there is no way to replace it. And the constructor is not fired on any other console call. Only on demand. |
@driesvints Removing this is a breaking change as overriding classes could rely on this.
use Symfony\Component\Console\Input\InputArgument; | ||
use Symfony\Component\Console\Input\InputOption; | ||
|
||
#[AsCommand(name: 'make:command')] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also revert this.
@driesvints Please also revert this.
Unfortunately I forgot about "use class"
I agree with @GrahamCampbell, the fact that there is no empty constructor in the stub anymore (suggesting the put code in there) should be enough. A hint in the docs should be enough if even necessary. There are a lot of possibilities where writing code can cause unwanted behavior, however laravel doesn't intervenes there as well. I once created a |
I agree too. But it's one thing to write in the constructor in the ServiceProvider and you yourself understand that it will work all the time. The first thought when you write a console command is that it will be executed on demand. But not always, only when necessary. I am not comparing all Laravel objects, but I am saying that it can be confusing at the current location. I can give an example of Laravel entities. When you write code in the model constructor, it only executes when requested. "A hint in the docs should be enough if even necessary." - There is no such thing in the documentation. Nowhere is it written that console commands work on the principle of ServiceContainer. Until you look in the repository, you think that it works differently. "I didn't know that this method already existed in eloquent and I got integrity errors all over the place with those models." - In the current situation (Example 2/ Example3) you won't get an error. She will just be. |
Worst of all, the console command has the right to modify the application. According to Laravel's logic, this is done through another "ServiceProvider" mechanism. I'm not saying it's bad. On the contrary, I propose a safe mechanism. Which does not break the Laravel system in any way. Someone who has studied console commands in detail. Easily abandon "ProtectedCommand" in the direction of "Command" This version of the console command is optional. You have to answer yes. By default, no. |
My solution will protect developers from implicit code invocation!
To initialize the console command class, it would be more correct to initialize it in the "handle" method. Overriding a constructor can be a big hassle. This can be seen on large applications.
Causes:
Examples:
Example 1
If you run any command
In console output
"This code is always executed"
"Worker log" or "Horizon log" or "Supervisor log"
"This code is always executed"
If you run cron, this task will run many times (/var/log/syslog)
"This code is always executed"
In this example, we see that the code in the constructor is executed many times and everywhere. It would be nice if this was done upon request. When it is necessary. This example shows that it is very easy to get confused. On the one hand, the initialization of the class is described in the constructor. On the other hand, we get a time bomb.
Example 2
TestCommand.php
EventServiceProvider.php
As you can see, we have created a console command and registered an event listener in the EventServiceProvider.
In the EventServiceProvider::boot method, we registered a listener just so we can monitor how the application is running. We are only interested in the name of the event.
If you run any command
In console output
"Worker log" or "Horizon log" or "Supervisor log"
If you run cron, this task will run many times (/var/log/syslog)
As you can see, we have created a load that we did not expect. In all practices, variables are initialized in the class constructor. But this place is so unique.
I understand why this is happening. The Illuminate\Foundation\Console\Kernel::load() function is called in the App\Console\Kernel::commands() method.
After running the $this->laravel->make($command) function, the constructor will be called
This is how all commands are initialized
I consider this a good reason to fix
Example 3
TestCommand.php
User.php
In this task, we record that who made changes to a particular model.
As you can see, we have now received an implicit error. The search for which can take a very long time.
I agree that this behavior is normal for Laravel Framework. But this is very difficult to explain to an inexperienced developer. In my opinion, this could be a security and failover hole. I think this fix will help avoid a lot of bugs.
How to use?
Do you want to create a protected command? (yes/no) [no]:
New protected console command