我有一个命令行应用程序,到目前为止使用Symfony依赖注入组件。我现在发现我想添加命令行选项并改进输出的格式,Symfony控制台组件似乎是一个不错的选择。
但是,我无法理解如何让我的Symfony控制台命令类接收容器对象。
我找到的文档使用的是ContainerAwareCommand类,但是来自FrameworkBundle - 这似乎是一个巨大的开销,需要添加到纯CLI应用程序,因为它需要进一步的捆绑,如路由,http,配置,缓存等等,这些都与我没有任何关系。
(现有的SO问题How can i inject dependencies to Symfony Console commands?也假定为FrameworkBundle,BTW。)
我在这里创建了一个测试存储库,其中包含一个说明问题的基本命令:https://github.com/joachim-n/console-with-di
答案 0 :(得分:1)
自2018年和Symfony 3.4+ DI features以来,您可以将命令用作服务。
您可以find working demo here ,感谢@TravisCarden
简而言之:
<?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
如果您对更详细的解释感兴趣,我写了一篇帖子Why You Should Combine Symfony Console and Dependency Injection。
答案 1 :(得分:0)
是的,整个框架不是必需的。在您的情况下,首先需要创建一种入口脚本。这样的事情:
<?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();
在这个例子中,我们创建容器,然后将命令注册为服务,在命令中添加一个依赖项(在我们的例子中是整个容器 - 但显然你可以创建另一个依赖项并注入它)并编译容器。然后我们只需创建应用程序,将命令实例添加到应用程序并运行它。
当然,您可以在yaml
或xml
中保留容器的所有配置,甚至使用PHP格式。