drush-ops / drush

Drush is a command-line shell and scripting interface for Drupal, a veritable Swiss Army knife designed to make life easier for those who spend their working hours hacking away at the command prompt.
https://www.drush.org
2.33k stars 1.08k forks source link

Stop using Drupal container for contrib command discovery and instantiation #4685

Open weitzman opened 3 years ago

weitzman commented 3 years ago

Use Factory method ...

greg-1-anderson commented 3 years ago

The general idea here is that we find command classes by discovery, like we used to do (and still do for site-wide commands). We could also consider discovering command classes via the autoloader, as Robo plugins do.

Once we have a list of command classes, we instantiate them via a static create factory method (that must exist? or only if it exists?). The create method is passed a DI container, and it can use that to fetch and inject its dependencies into the commandfile class.

League/container supports chained DI containers. Symfony DI did not; I'm not sure if it does now, but I suspect we might be able to chain from a League DI container to a Symfony (Drupal) DI container. If not, we might need to pass two DI containers to the create method.

weitzman commented 3 years ago

Looks like neither the Symfony container nor the Drupal container (a close fork of Symfony) support delegate() so we'll need to pass two containers I think.

greg-1-anderson commented 3 years ago

Symfony containers implement ContainerInterface, which is the requirement for deletage() in league / container, so I think that maybe we could delegate to the Drupal container from the Drush container.

We should only do this if we change our bootstrap process to do the delegation; if we don't do that, then we should pass two containers.

Also, just because we can pass just one container doesn't mean we should. I'm undecided on this point.

weitzman commented 3 years ago

Could we do this in a backward compat way? Seems like we could mimic service instantiation so they continue to work?

greg-1-anderson commented 3 years ago

Hm, yeah, that might work.

weitzman commented 3 years ago

A bit more discussion about motivation for this (or lack thereof) https://drupal.slack.com/archives/C62H9CWQM/p1633443431221800?thread_ts=1633433650.221500&cid=C62H9CWQM

joachim-n commented 2 years ago

It would be really nice if Drush looked for commands in all installed packages, not just modules.

There's a comment in DrupalKernelTrait::discoverServiceProviders() about this.

I think it's doable by instantiating Composer and getting a list of packages from it (see https://github.com/composer/composer/issues/4445), but is the command list cached at all? Getting a list of packages out of Composer every single time is going to be a bit slow.

Seldaek commented 2 years ago

If you need a list of packages, please use https://getcomposer.org/doc/07-runtime.md#installed-versions - there's also getInstalledPackagesByType which could be useful for this perhaps if the packages have defined types.

claudiu-cristea commented 2 years ago

@joachim-n right now commands are discoverable in packages but they need to follow the these rules https://github.com/drush-ops/drush/blob/11.x/docs/commands.md#auto-discovered-commands

weitzman commented 2 years ago

@Seldaek - thats a helpful new feature and we will use it soon, now that most people are on Composer 2 (which is great!).

Seldaek commented 2 years ago

Nice, don't forget to require the appropriate composer-runtime-api package to avoid confusing people still running old composer versions (which tends to happen a lot accidentally it seems)

weitzman commented 2 years ago

Oh, I forgot that we only need to use the Composer API for loading global packages, and we can use an existing discovery method for commands that ship in modules.

One thing we havent discussed here is how we will avoid loading commands that belong to uninstalled modules. Do we load the command if ($outside_docroot || $path_is_under_an_enabled_module)?

joachim-n commented 2 years ago

@claudiu-cristea That looks great, thank you!

weitzman commented 1 year ago

Seems like we could experiment with command lazy loading at same time https://symfony.com/doc/current/console/lazy_commands.html

weitzman commented 1 year ago

I got curious about how Laravel auto-discovers Service Providers (analogous to command providers here). See https://divinglaravel.com/laravels-package-auto-discovery. Seems like that composer-centric discovery approach could work for us.

Sources:

weitzman commented 1 year ago

Cake does similar where composer writes a vendor/cakephp-plugins.php file. See https://book.cakephp.org/4/en/plugins.html

weitzman commented 1 year ago

This was largely done in #5548. We could explore the service providers approach and Lazy Commands in future issues.

greg-1-anderson commented 1 year ago

Enhancing the psr4 discovery mechanism to use the Composer API instead is a good idea. This reduces the utility a little (in the existing mechanism, folks can add a second autoload location to put their commands somewhere else in the source tree), but I think it also simplifies things a bit.

weitzman commented 1 year ago

OK, reopening so we dont lose it.