Reputation: 30761
I have a command-line application, which so far uses the Symfony dependency injection component. I now find that I want to add command-line options and improve the formatting of the output, and the Symfony console component seems like a good choice for that.
However, I can't fathom how to get my Symfony console command classes to receive the container object.
The documentation I have found uses the ContainerAwareCommand class, but that is from the FrameworkBundle -- which seems a huge amount of overhead to add to a pure CLI app, as it requires further bundles such as routing, http, config, cache, etc, none of which are of any relevance to me whatsoever here.
(Existing SO question How can i inject dependencies to Symfony Console commands? also assumes the FrameworkBundle, BTW.)
I've made a test repository here with a basic command that illustrates the problem: https://github.com/joachim-n/console-with-di
Upvotes: 11
Views: 3368
Reputation: 552
Four years after, but intended for somebody looking for a similar solution. I had a look alike scenario and implemented my own boilerplate with Dependency Injection ready to go for a standalone symfony console application, supporting auto-wiring and auto-configure for commands and services.
composer create-project coral-media/crune crune
A sample command receiving the container it's in place. Source code available at Github
Happy Coding!
Upvotes: 0
Reputation: 24280
Since 2018 and Symfony 3.4+ DI features, you can make use of commands as services.
You can find working demo here, thanks to @TravisCarden
In short:
<?php
# app/Kernel.php
namespace App;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\DependencyInjection\ContainerBuilder;
final class AppKernel extends Kernel
{
public function registerBundles(): array
{
return [];
}
public function registerContainerConfiguration(LoaderInterface $loader): void
{
$loader->load(__DIR__.'/../config/services.yml');
}
protected function build(ContainerBuilder $containerBuilder): void
{
$containerBuilder->addCompilerPass($this->createCollectingCompilerPass());
}
private function createCollectingCompilerPass(): CompilerPassInterface
{
return new class implements CompilerPassInterface
{
public function process(ContainerBuilder $containerBuilder)
{
$applicationDefinition = $containerBuilder->findDefinition(Application::class);
foreach ($containerBuilder->getDefinitions() as $definition) {
if (! is_a($definition->getClass(), Command::class, true)) {
continue;
}
$applicationDefinition->addMethodCall('add', [new Reference($definition->getClass())]);
}
}
};
}
}
# config/services.yml
services:
_defaults:
autowire: true
App\:
resource: '../app'
Symfony\Component\Console\Application:
public: true
# index.php
require_once __DIR__ . '/vendor/autoload.php';
use Symfony\Component\Console\Application;
$kernel = new AppKernel;
$kernel->boot();
$container = $kernel->getContainer();
$application = $container->get(Application::class)
$application->run();
php index.php
If you're interested in a more detailed explanation, I wrote a post Why You Should Combine Symfony Console and Dependency Injection.
Upvotes: 7
Reputation: 1055
Yes, the whole framework isn't required. In your case, first you need to create a kind of entry script. Something like that:
<?php
require 'just/set/your/own/path/to/vendor/autoload.php';
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\ContainerBuilder;
$container = new ContainerBuilder();
$container
->register('your_console_command', 'Acme\Command\YourConsoleCommand')
->addMethodCall('setContainer', [new Reference('service_container')]);
$container->compile();
$application = new Application();
$application->add($container->get('your_console_command'));
$application->run();
In this example, we create the container, then register the command as a service, add to the command a dependency (the whole container in our case -- but obviously you can create another dependency and inject it) and compile the container. Then we just create app, add the command instance to the app and run it.
Sure, you can keep all configurations for container in yaml
or xml
or even using PHP format.
Upvotes: 3