Skip to content

[FrameworkBundle] Commands as a service #23624

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

Closed
wants to merge 9 commits into from
Closed

[FrameworkBundle] Commands as a service #23624

wants to merge 9 commits into from

Conversation

ro0NL
Copy link
Contributor

@ro0NL ro0NL commented Jul 22, 2017

Q A
Branch? 3.4
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? yes
Tests pass? yes/no
Fixed tickets #...
License MIT
Doc PR symfony/symfony-docs#...

Next step towards #23488

It's a work in progress if we want to do all commands at once (im fine :)). But i think we should review assets:install first.

Also im assuming framework commands can rely on getApplication()->getKernel() from the framework application (we already do that in some commands). That saves a dep on @kernel.

And filesystem as a service; perhaps drop that as well :)

@linaori
Copy link
Contributor

linaori commented Jul 22, 2017

And filesystem as a service; perhaps drop that as well :)

@ro0NL while should be another RFC, this is probably not a good thing to do for mocking reasons

@chalasr
Copy link
Member

chalasr commented Jul 22, 2017

all commands at once

let's do that :)

@ro0NL
Copy link
Contributor Author

ro0NL commented Jul 22, 2017

Alright. Next is cache:pool:clear.. this one requires all pools by id from the container.

It may benefit from #22200 so it gets cache.pool tagged services. But we need them as a map, indexed by service id 😅 which we can provide as default implem. for !tagged-services.

/cc @nicolas-grekas

edit: that would be the global clearer, which it already has. Basically this command allows for any cleaer-ish like service, so truly needs to be container aware. Bummer :)

or inject @service_container

@nicolas-grekas
Copy link
Member

@ro0NL support for by-id maps in tagged-services wouldn't help us remove the CachePoolClearerPass, which has additional logic that can't be replaced solely by the tag. So, remark is off topic :)

@ro0NL
Copy link
Contributor Author

ro0NL commented Jul 22, 2017

Right! I'd already move forward with injecting @service_container, some other need that as well. Imo. thats good enough to bridge to whatever it's refactored to, some day.

Ready for review.

Btw @iltar need any help with symfony/acl-bundle#2? Regarding the commands we need to decide what to do.

@nicolas-grekas
Copy link
Member

tests still need some love ;)

Copy link
Member

@chalasr chalasr left a comment

Choose a reason for hiding this comment

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

thanks for working on this

{
parent::__construct();

if (!$filesystem instanceof Filesystem) {
Copy link
Member

Choose a reason for hiding this comment

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

I suggest making $filesystem optional (last, nullable, creating it if not passed)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For assets:installs that makes $baseDir the 1st argument, which is as $name also string. Ie we cant detect if user passes name (old api) or path (new api).

I like $filesystem being last and optional though. Suggestions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can make $filesystem optional as of 4.0 perhaps =/

Copy link
Contributor

Choose a reason for hiding this comment

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

What about using is_dir($baseDir) to differentiate a command name from the base dir, if we make it the first constructor arg?
The baseDir is already supposed to exist. The command will throw an exception otherwise.

Copy link
Contributor Author

@ro0NL ro0NL Jul 22, 2017

Choose a reason for hiding this comment

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

I was going for that, but you still get different behavior :) before: exception, after: a path-ish command name.

A way could be to get the path from getApplication()->getKernel()->getContainer()->getParameter() =/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Lets make everything optional then 🤣 should work out.

@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and "%s" won\'t extend "%s" nor implement "%s" anymore in 4.0.', __METHOD__, __CLASS__, ContainerAwareCommand::class, ContainerAwareInterface::class), E_USER_DEPRECATED);

return parent::getContainer();
}
Copy link
Member

Choose a reason for hiding this comment

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

not needed as the class is final


/**
* {@inheritdoc}
*/
Copy link
Member

Choose a reason for hiding this comment

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

missing @deprecated (same for all others)

@@ -12,5 +12,63 @@
<tag name="kernel.event_subscriber" />
<tag name="monolog.logger" channel="console" />
</service>

<service id="command.about" class="Symfony\Bundle\FrameworkBundle\Command\AboutCommand">
Copy link
Member

Choose a reason for hiding this comment

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

should we prefix them all by console?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

dunno.. it seems to be <extension>.command.name, except for framework :) there's no framework. prefix. Hence namespaced under root.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm 👍 for adding the console prefix.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(I have no opinion really :))

Security bundle introduced security.console.user_password_encoder_command while webserver bundle did web_server.command.server_run; i took the latter as template.

So im in the middle here :) however i always thought console and command are sibling namespaces. Where console namespace is used for library stuff (console.command_loader, console.error_listener), and command for commands solely.

Copy link
Contributor

Choose a reason for hiding this comment

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

For the record and what it's worth, the pass use console.command tag & prefix.

web_server.command.server_run looks ok to me, as we already have a root prefix. So there is few chances to be used in userland.
security.console.user_password_encoder_command was not conform to this last convention, but the web server bundle didn't exist yet nor any convention for command as service at the time it was introduced. So I used mine, i.e: (namespace|context).component.snaked_case_class_name :)

Finally, command as root prefix might mean something else in userland. It can be one of your domain bounded context you use as prefix for related services, or simply the prefix you chose for your command handlers when using command buses.

Actually, I don't really mind either; there are few chances there is any conflict with any existing app. Just saying it won't hurt having this prefix :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated 👍

May be worth it to update security.console.user_password_encoder_command to security.command.user_password_encoder

</service>

<service id="command.router_debug" class="Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand">
<argument type="service" id="router" />
Copy link
Member

Choose a reason for hiding this comment

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

should be null if invalid as the command was calling has() (same below)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

RouterDebugCommand doesnt accept null ;) i remove the definition if routing is disabled in the framework extension.

@@ -87,6 +130,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
*/
protected function getEventDispatcher()
Copy link
Contributor

Choose a reason for hiding this comment

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

@deprecated since 3.4 and will be removed in 4.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

if (__CLASS__ !== get_class($this)) {
$r = new \ReflectionMethod($this, 'getEventDispatcher');
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Usage of method "%s" is deprecated since version 3.4 and will no longer be supported in 4.0.', get_class($this).'::getEventDispatcher'), E_USER_DEPRECATED);
Copy link
Contributor

Choose a reason for hiding this comment

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

- 'Usage of method "%s" is deprecated since version 3.4 and will no longer be supported in 4.0.'
+ 'Usage of method "%s" is deprecated since version 3.4 and will no longer be supported in 4.0. Inject an event dispatcher instance using the constructor instead.'

?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Appended with Construct the command with its required arguments instead. Also applied to getTwigEnvironment usage in twig bundle.

ogizanagi added a commit that referenced this pull request Jul 22, 2017
This PR was merged into the 3.2 branch.

Discussion
----------

Remove unused mocks/vars

| Q             | A
| ------------- | ---
| Branch?       | 3.2

Spotted in #23624

Commits
-------

445c56a Remove unused mocks/vars
ogizanagi added a commit that referenced this pull request Jul 22, 2017
This PR was merged into the 3.3 branch.

Discussion
----------

Remove unused prop + added @deprecated

| Q             | A
| ------------- | ---
| Branch?       | 3.3

Spotted in #23624

Commits
-------

07ff4dd Remove unused prop + added @deprecated
*/
private $filesystem;
public function __construct($baseDir = null, Filesystem $filesystem = null /**, $newApi = false*/)
Copy link
Contributor

@ogizanagi ogizanagi Jul 23, 2017

Choose a reason for hiding this comment

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

About $newApi: I think we already had such flags in the past, but we never introduced them commented.
Instead (if this trick is accepted by other reviewers), I'd suggest to uncomment and document this option in the docBlock, mentioning it'll only be necessary until 4.0, version in which it'll finally get removed.

Copy link
Member

Choose a reason for hiding this comment

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

I would remove it, make $filesystem the first arg and check for its type instead ($basedir being the only one with no typehint)

Copy link
Member

Choose a reason for hiding this comment

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

oops, we made $filesystem optional... then I would just remove the commented arg and don't document it at all

Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO it should be clear for anyone willing to switch to the new API that this flag must be set to true until 4.0, time at which it'll be safely removed. So the argument must exist, and should be documented.

Copy link
Member

Choose a reason for hiding this comment

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

right. then we would have to deprecate it and remove it... should we forgot about the optional filesystem instead?

Copy link
Contributor

@GuilhemN GuilhemN Jul 28, 2017

Choose a reason for hiding this comment

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

Are you sure? From what I see, it must always be a dir.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Not having an exception until 4.0 would be fine for me but I understand your point :)

Copy link
Member

Choose a reason for hiding this comment

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

@GuilhemN Application::getKernel() returns KernelInterface which does not provide getProjectDir(). I'd keep injecting it just to avoid a call to getContainer()->getParameter(...).

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, that's a good point :)

}

$this->cacheClearer = $cacheClearer;
$this->cacheDir = $cacheDir;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same, imo calling Kernel::getCacheDir() is enough.

}

$this->poolClearer = $poolClearer;
$this->cacheDir = $cacheDir;
Copy link
Contributor

Choose a reason for hiding this comment

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

same

*/
private $filesystem;
public function __construct($baseDir = null, Filesystem $filesystem = null /**, $newApi = false*/)
Copy link
Contributor

@GuilhemN GuilhemN Jul 28, 2017

Choose a reason for hiding this comment

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

Are you sure? From what I see, it must always be a dir.

$pools[$id] = $id;
} else {
$pool = $container->get($id);
$pool = $kernel->getContainer()->get($id);
Copy link
Contributor

Choose a reason for hiding this comment

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

parent::getContainer()?

Copy link
Member

Choose a reason for hiding this comment

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

it won't be containeraware in 4.0, but this will still be supported

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, it was just to be consistent with the code above but that's fine for me.

Copy link
Contributor Author

@ro0NL ro0NL Jul 29, 2017

Choose a reason for hiding this comment

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

We basically favor kernel aware over container aware. See also #23632

We might favor kernel injection over kernel aware. That would be the most flexible, but not sure if needed.

*/
private $filesystem;
public function __construct($baseDir = null, Filesystem $filesystem = null /**, $newApi = false*/)
Copy link
Contributor

Choose a reason for hiding this comment

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

Not having an exception until 4.0 would be fine for me but I understand your point :)

@@ -25,6 +25,29 @@
*/
final class CachePoolClearCommand extends ContainerAwareCommand
Copy link
Contributor

Choose a reason for hiding this comment

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

The getContainer() is missing here, right?

Copy link
Member

Choose a reason for hiding this comment

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

getContainer() is protected so the deprecation would be triggered only if called from a subclass, but this is final :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Indeed... I should have reviewed more carefully 😊

*/
private $filesystem;
public function __construct($baseDir = null, Filesystem $filesystem = null /**, $newApi = false*/)
Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, that's a good point :)

}

$warmer->warmUp($this->getContainer()->getParameter('kernel.cache_dir'));
$this->cacheWarmer->warmUp(null === $this->cacheDir ? $kernel->getCacheDir() : $this->cacheDir);
Copy link
Contributor

Choose a reason for hiding this comment

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

This case is already supported above, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope. Above is for BC, as long as the command extends ContainerAwareCommand we need to get the cachedir from the container, if not provided already.

This is the new behavior if cachedir is not provided in 4.0.

However i still have my concerns about getCacheDir() vs getParameter('cachedir'). I think we should actually prefer getParameter(), also in 4.0.

$this->poolClearer = $this->getContainer()->get('cache.global_clearer');
}
if (null === $this->cacheDir) {
$this->cacheDir = parent::getContainer()->getParameter('kernel.cache_dir');
Copy link
Member

Choose a reason for hiding this comment

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

should use $this instead of parent

@ro0NL
Copy link
Contributor Author

ro0NL commented Jul 29, 2017

Im having my thoughts about cache_dir/project_dir as an optional constructor arg. Do we really, really need it? It's not like the framework commands should be very abstract and generic, and we fallback to the parameter anyway.

My vote goes to simply getKernel()->getContainer()->getParameter(). Which works for project_dir and cache_dir (im favoring getParameter() over getCacheDir() as well).

That would fix the argument hassle in assets:install :) while remaining consistency with other commands.

@chalasr
Copy link
Member

chalasr commented Jul 31, 2017

No objection for getKernel()->getContainer()->getParameter('kernel.*') on my side.

@ro0NL
Copy link
Contributor Author

ro0NL commented Aug 1, 2017

Done.

Last thing; i noticed cache:pool:prune is added in between and is registered in cache.xml, the definition is, AFAIK, not removed if Console\Application is unavailable. I tend to register all commands in console.xml which is only loaded under that condition, and then additionally remove specific command definitions if its component is unavailable. This also applies to #23694

Is that the right approach, or should we inverse it? And does it need a class_exists(Application::class) check?

{
parent::__construct();

if (!$filesystem instanceof Filesystem) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Argh... null implies new api?

Copy link
Member

Choose a reason for hiding this comment

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

null has never been an allowed name so LGTM as is

Copy link
Contributor Author

@ro0NL ro0NL Aug 6, 2017

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

fine for me

<argument type="service" id="twig" />
<tag name="console.command" command="lint:twig" />
</service>

<!-- BC to be removed in 4.0 -->
Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed this bc layer in favor of making TwigBundle::registerCommands a noop

return;
}

$this->filesystem = $filesystem ?: new Filesystem();
Copy link
Member

Choose a reason for hiding this comment

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

the ?: ... is useless, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

kinda yes.. in 4.0 it would be ?? ... and in 3.4 the else may not happen (depends on discussion above :))

@@ -695,6 +695,13 @@ private function registerDebugConfiguration(array $config, ContainerBuilder $con
*/
private function registerRouterConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
{
if (!$this->isConfigEnabled($container, $config)) {
$container->removeDefinition('console.command.router_debug');
$container->removeDefinition('console.command.router_match');
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes :)

@nicolas-grekas
Copy link
Member

Thank you @ro0NL.

parent::__construct();

if (!$filesystem instanceof Filesystem) {
@trigger_error(sprintf('Passing a command name as the first argument of "%s" is deprecated since version 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED);
Copy link
Member

Choose a reason for hiding this comment

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

%s()?

Copy link
Member

Choose a reason for hiding this comment

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

message is confusing if I was not passing anything as argument

if (!$filesystem instanceof Filesystem) {
@trigger_error(sprintf('Passing a command name as the first argument of "%s" is deprecated since version 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED);

$this->setName(null === $filesystem ? 'assets:install' : $filesystem);
Copy link
Member

Choose a reason for hiding this comment

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

parent::__construct($filesystem);? calling setName() now is a behavior change, it's called in configure anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm you're right.. configure() always wins.. ill have a look at it.

Copy link
Member

Choose a reason for hiding this comment

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

thanks

@nicolas-grekas
Copy link
Member

@ro0NL merged up to master, PR welcomed for the 4.0 cleanup :)

@ro0NL ro0NL deleted the framework/commands branch August 6, 2017 10:44
@ro0NL ro0NL mentioned this pull request Aug 6, 2017
nicolas-grekas pushed a commit that referenced this pull request Aug 6, 2017
nicolas-grekas added a commit that referenced this pull request Aug 6, 2017
This PR was squashed before being merged into the 3.4 branch (closes #23801).

Discussion
----------

Continuation of #23624

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!--highly recommended for new features-->

See #23624 (comment)

cc @chalasr

Also included service injection for init:acl + set:acl and simplification of lint:xliff + lintt:yaml.

Btw, i think init:acl needs to be renamed to acl:init :)

Commits
-------

5f637c1 Continuation of #23624
symfony-splitter pushed a commit to symfony/framework-bundle that referenced this pull request Aug 6, 2017
This PR was squashed before being merged into the 3.4 branch (closes #23801).

Discussion
----------

Continuation of #23624

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!--highly recommended for new features-->

See symfony/symfony#23624 (comment)

cc @chalasr

Also included service injection for init:acl + set:acl and simplification of lint:xliff + lintt:yaml.

Btw, i think init:acl needs to be renamed to acl:init :)

Commits
-------

5f637c1 Continuation of #23624
nicolas-grekas added a commit that referenced this pull request Aug 6, 2017
* 3.4:
  Continuation of #23624
fabpot added a commit that referenced this pull request Sep 3, 2017
…ervices (chalasr)

This PR was merged into the 3.4 branch.

Discussion
----------

Fix deprecations regarding core commands registered as services

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #23624 (comment)
| License       | MIT
| Doc PR        | n/a

Current deprecation messages can be confusing (see fixed ticket), this improves them and adds a bunch of missing CHANGELOG/UPGRADE entries on the same topic

Commits
-------

4659975 Fix deprecations regarding core commands registered as services
This was referenced Oct 18, 2017
nicolas-grekas added a commit that referenced this pull request Oct 28, 2017
…ommand service id (kbond)

This PR was merged into the 3.4 branch.

Discussion
----------

[FrameworkBundle] fix CachePoolPrunerPass to use correct command service id

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #23451 (comment)
| License       | MIT
| Doc PR        | n/a

#23624 broke #23451.

Commits
-------

14c62da fix CachePoolPrunerPass to use correct command service id
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.

10 participants