Zum Hauptinhalt springen
••••••••••••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

Auto-Discovering Console Commands in Power Modules

A couple of days ago, I was working on a PHP project that uses the Power Modules framework and Symfony Console, and I realized I was repeating the same command…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!

A couple of days ago, I was working on a PHP project that uses the Power Modules framework and Symfony Console, and I realized I was repeating the same command registration pattern across multiple projects. Time to build something better!



For those new to the series: Power Modules is a modular PHP framework where each module encapsulates its own logic and dependencies, communicating through well-defined interfaces while maintaining strict boundaries.



I already had a working setup/boilerplate for registering console commands exported from a Power Module (using the ExportsComponents interface) that I copy-pasted across projects. It follows the Power Modules framework principles: keep modules encapsulated and their dependencies private, and delegate the instantiation and dependency resolution to their DI container:




$modules = [
new OrdersModule(),
new UsersModule(),
// ... other modules
];
$app = new ModularAppBuilder(__DIR__ . '/../')
->withModules(...$modules)
->build()
;

$console = new \Symfony\Component\Console\Application('My Console Application', '0.1.0');

foreach ($modules as $module) {
foreach ($module::exports() as $exportedComponent) {
// $exportedComponent is just a class-string that references to a power module DI container
if (is_a($exportedComponent, Command::class, true) === true) {
$console->add($app->get($exportedComponent));
}
}
}









The Problem



This works, but it felt clunky repeating this boilerplate across projects. I knew that Symfony DI can manage console commands, and I wanted to see if I could build something that automatically discovers and registers console commands from different modules. So I thought: Why not apply the PowerModuleSetup concept for this? (you can read more about it here: The Night I Discovered I'd Built Something Revolutionary (And Didn't Know It)






Building the Solution



The implementation was quite straightforward - create a PowerModuleSetup that does the same thing as the above code, but in a more modular and reusable way.






First Iteration: Direct Registration



This PowerModuleSetup bridges Symfony Console with the Power Modules framework's modular architecture. Modules export console commands while maintaining encapsulation principles, and commands are auto-discovered and registered into a central Console\Application.




final class ConsoleCommandsSetup implements PowerModuleSetup
{
private \Symfony\Component\Console\Application $console;

public function __construct()
{
$this->console = new \Symfony\Component\Console\Application();
}

public function setup(PowerModuleSetupDto $dto): void
{
if (!$dto->powerModule instanceof ExportsComponents) {
return;
}

if ($dto->setupPhase !== SetupPhase::Post) {
return;
}

if ($dto->rootContainer->has(Application::class) === false) {
$dto->rootContainer->set(Application::class, $this->console);
}

foreach ($dto->powerModule::exports() as $exportedComponent) {
if (is_a($exportedComponent, Command::class, true) === true) {
$this->console->add($dto->rootContainer->get($exportedComponent));
}
}
}
}









Adding Lazy-Loading with ContainerCommandLoader



I wanted to take it further with deferred command instantiation. Symfony's ContainerCommandLoader loads commands from a DI container on-demand. Since ExportsComponentsSetup already registers exported components in the root container, I could leverage that. This meant updating the setup to use a two-phase approach: collect commands in the Pre phase and register them in the Post phase.




final class ConsoleCommandsSetup implements PowerModuleSetup
{
private Application $console;
private ?CommandLoaderInterface $commandLoader = null;

/**
* @var array<string,class-string<Command>> $commandMap
*/

private array $commandMap = [];

public function __construct()
{
$this->console = new Application();
}

public function setup(PowerModuleSetupDto $powerModuleSetupDto): void
{
if (!$powerModuleSetupDto->powerModule instanceof ExportsComponents) {
return;
}

if ($powerModuleSetupDto->setupPhase === SetupPhase::Pre) {
// PRE phase: collect all commands to be registered later
foreach ($powerModuleSetupDto->powerModule::exports() as $component) {
if (is_subclass_of($component, Command::class)) {
if ($attribute = (new ReflectionClass($component))->getAttributes(AsCommand::class)) {
$this->commandMap[$attribute[0]->newInstance()->name] = $component;
}
}
}

return;
}

if ($this->commandLoader !== null) {
return;
}

$this->commandLoader = new ContainerCommandLoader(
$powerModuleSetupDto->rootContainer,
$this->commandMap,
);

$console = $this->console;

if ($powerModuleSetupDto->rootContainer->has(Application::class) === true) {
$console = $powerModuleSetupDto->rootContainer->get(Application::class);
} else {
$powerModuleSetupDto->rootContainer->set(Application::class, $this->console);
}

$console->setCommandLoader($this->commandLoader);
}
}









Usage



Now I have a reusable PowerModuleSetup that can be added to any Power Modules application to automatically discover and register console commands from modules:




$app = new ModularAppBuilder(__DIR__ . '/../')
->withModules(
new OrdersModule(),
new UsersModule(),
)
->withPowerSetup(new ConsoleCommandsSetup())
->build();

// Console application is now available with all module commands registered
$console = $app->get(\Symfony\Component\Console\Application::class);
$console->run();









Wrapping Up



The complete implementation is available in the repository: power-modules/console



Install via Composer:




composer require power-modules/console






This pattern eliminated the boilerplate I was copy-pasting and made command registration automatic. If you're building modular applications with Power Modules, this setup might save you some time too.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Auto-Discovering Console Commands in Power Modules

Thematisch verwandte Begriffe: AutoDiscovering, Console, Commands, Power · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
Nächster Beitrag