-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[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
Conversation
@ro0NL while should be another RFC, this is probably not a good thing to do for mocking reasons |
let's do that :) |
Alright. Next is It may benefit from #22200 so it gets /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 |
@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 :) |
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. |
tests still need some love ;) |
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.
thanks for working on this
{ | ||
parent::__construct(); | ||
|
||
if (!$filesystem instanceof Filesystem) { |
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.
I suggest making $filesystem
optional (last, nullable, creating it if not passed)
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.
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?
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.
We can make $filesystem optional as of 4.0 perhaps =/
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.
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.
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.
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()
=/
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.
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(); | ||
} |
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.
not needed as the class is final
|
||
/** | ||
* {@inheritdoc} | ||
*/ |
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.
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"> |
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.
should we prefix them all by console
?
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.
dunno.. it seems to be <extension>.command.name
, except for framework :) there's no framework.
prefix. Hence namespaced under root.
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.
I'm 👍 for adding the console
prefix.
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.
(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.
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.
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 :)
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.
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" /> |
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.
should be null if invalid as the command was calling has()
(same below)
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.
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() |
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.
@deprecated since 3.4 and will be removed in 4.0.
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.
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); |
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.
- '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.'
?
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.
Appended with Construct the command with its required arguments instead.
Also applied to getTwigEnvironment usage in twig bundle.
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*/) |
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.
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.
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.
I would remove it, make $filesystem
the first arg and check for its type instead ($basedir
being the only one with no typehint)
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.
oops, we made $filesystem
optional... then I would just remove the commented arg and don't document it at all
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.
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.
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.
right. then we would have to deprecate it and remove it... should we forgot about the optional filesystem instead?
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.
Are you sure? From what I see, it must always be a dir.
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.
See #23624 (comment)
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.
Not having an exception until 4.0 would be fine for me but I understand your point :)
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.
@GuilhemN Application::getKernel()
returns KernelInterface
which does not provide getProjectDir()
. I'd keep injecting it just to avoid a call to getContainer()->getParameter(...)
.
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.
Ok, that's a good point :)
} | ||
|
||
$this->cacheClearer = $cacheClearer; | ||
$this->cacheDir = $cacheDir; |
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.
Same, imo calling Kernel::getCacheDir()
is enough.
} | ||
|
||
$this->poolClearer = $poolClearer; | ||
$this->cacheDir = $cacheDir; |
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.
same
*/ | ||
private $filesystem; | ||
public function __construct($baseDir = null, Filesystem $filesystem = null /**, $newApi = false*/) |
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.
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); |
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.
parent::getContainer()
?
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.
it won't be containeraware in 4.0, but this will still be supported
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.
Ok, it was just to be consistent with the code above but that's fine for me.
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.
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*/) |
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.
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 |
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.
The getContainer()
is missing here, right?
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.
getContainer()
is protected so the deprecation would be triggered only if called from a subclass, but this is final :)
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.
Indeed... I should have reviewed more carefully 😊
*/ | ||
private $filesystem; | ||
public function __construct($baseDir = null, Filesystem $filesystem = null /**, $newApi = false*/) |
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.
Ok, that's a good point :)
} | ||
|
||
$warmer->warmUp($this->getContainer()->getParameter('kernel.cache_dir')); | ||
$this->cacheWarmer->warmUp(null === $this->cacheDir ? $kernel->getCacheDir() : $this->cacheDir); |
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.
This case is already supported above, isn't it?
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.
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'); |
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.
should use $this
instead of parent
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 That would fix the argument hassle in assets:install :) while remaining consistency with other commands. |
No objection for |
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) { |
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.
Argh... null
implies new api?
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.
null
has never been an allowed name so LGTM as is
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.
actually it is.. https://github.com/symfony/symfony/blob/v3.3.6/src/Symfony/Component/Console/Command/Command.php#L51
leaning to make filesystem a required dep..
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.
fine for me
<argument type="service" id="twig" /> | ||
<tag name="console.command" command="lint:twig" /> | ||
</service> | ||
|
||
<!-- BC to be removed in 4.0 --> |
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.
removed this bc layer in favor of making TwigBundle::registerCommands a noop
return; | ||
} | ||
|
||
$this->filesystem = $filesystem ?: new Filesystem(); |
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.
the ?: ...
is useless, isn't it?
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.
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'); |
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.
https://travis-ci.org/symfony/symfony/jobs/261303308#L2572 should be updated to FQCN?
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.
Yes :)
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); |
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.
%s()
?
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.
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); |
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.
parent::__construct($filesystem);
? calling setName()
now is a behavior change, it's called in configure
anyway.
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.
Hm you're right.. configure()
always wins.. ill have a look at it.
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.
thanks
@ro0NL merged up to master, PR welcomed for the 4.0 cleanup :) |
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
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
* 3.4: Continuation of #23624
…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
…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
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 :)