refactor!: modernize console commands with attributes#6527
Merged
Conversation
Console commands are now registered via Symfony's native #[AsCommand] attribute and discovered through ClassDiscovery (core + addons), instead of the hardcoded loader map and the "console_commands" package.yml property. - Remove the "console_commands" package.yml property (and its JSON schema) - Add #[AsCommand] (name/description/help) to all core commands - Add AvailableInSetupInterface to gate which commands show during setup - CommandLoader returns LazyCommand, so "console list" no longer instantiates every command - only the executed command is instantiated - AbstractCommand::$addon is now a lazily resolved, non-nullable property (replaces getAddon()/setAddon()); reading it on a core command throws - Update rector upgrade rules accordingly
Replace configure()/execute() with Symfony 8.1 invokable commands: arguments
and options are declared via #[Argument]/#[Option] attributes on the __invoke()
parameters, with SymfonyStyle/OutputInterface injected directly. The generated
input definitions (names, modes, defaults, shortcuts, descriptions) are
unchanged.
Completion suggestions are inline `static function` closures right on the
attributes (PHP 8.5 allows static closures in constant expressions) - the same
inline style the commands used in configure() before, so no separate public
suggest* helper methods are needed.
AssetsSyncCommand keeps configure() for its runtime-computed help text. The
now-unused AbstractCommand::getStyle() helper is removed.
Also fixes a pre-existing missing space in UserListCommand's completion query
("SELECT login FROM" . table -> "FROM " . table) that produced invalid SQL.
Running "cronjob:run --job" without a value is documented to let you pick a job interactively, but since 6.x it errored out instead: the option value was cast with (int), turning the value-less null into 0, so executeSingleJob() never received null and the interactive ChoiceQuestion branch was unreachable. Use the typed bool|string option value directly - a value-less --job resolves to true, which now maps to null (interactive selection); a given id is cast to int as before. Restores the 5.x behavior.
The concrete command classes are @internal leaf classes that are instantiated via the command loader, never extended. Marking them final makes that explicit, finishes what UserListCommand/UserDeleteCommand already started, and clears the ClassMustBeFinal hints. AbstractCommand stays abstract.
The config:get/set argument was renamed from "config-key" to "key" (a breaking change; positional invocation is unaffected). Update the command tests to pass the argument under its new name.
fb887ff to
9bd53dc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Modernizes the console command layer to use Symfony 8.1 attributes.
#[AsCommand]attribute and discovered throughClassDiscovery(core + active addons). Theconsole_commandsproperty inpackage.ymlis gone — an addon now registers a command simply by adding#[AsCommand]to a class extendingAbstractCommand, the same way#[AsExtension]already works.configure()/execute()are replaced by__invoke()with#[Argument]/#[Option]parameters and injectedSymfonyStyle/OutputInterface. Completion suggestions are inlinestatic functionclosures / first-class callables right on the attributes (PHP 8.5 allows static closures and first-class callables in constant expressions).CommandLoaderreturnsLazyCommands, so listing commands (console list, shell completion) no longer instantiates every command class — only the command that is actually executed is built. This matters most for addon-provided commands.AbstractCommand::$addonis now a lazily-resolved, non-nullable property (derived from the command class location), replacinggetAddon()/setAddon(). Reading it on a core command throws.AvailableInSetupInterfacemarker, replacing the hardcoded list of setup-time commands in the loader.final.Also fixes a pre-existing 6.x regression in
cronjob:run --job: running it without a value is documented to let you pick a job interactively, but an(int)cast turned the value-lessnullinto0, so the interactive picker was unreachable.5.xbehaves correctly; this restores that behavior.Breaking changes
console_commandsproperty inpackage.ymlis removed — use#[AsCommand]on the command class instead.AbstractCommand::getAddon()/setAddon()are removed in favor of the$addonproperty. The Rector upgrade rules are updated accordingly (getPackage()/getAddon()→->addon).