繁体   English   中英

在没有 Symfony 框架包的情况下,如何使用依赖注入的 Symfony 控制台?

[英]how do I use Symfony console with dependency injection, without the Symfony framework bundle?

我有一个命令行应用程序,到目前为止它使用 Symfony 依赖注入组件。 我现在发现我想添加命令行选项并改进 output 的格式,而 Symfony 控制台组件似乎是一个不错的选择。

但是,我无法理解如何让我的 Symfony 控制台命令类接收容器 object。

我找到的文档使用了 ContainerAwareCommand class,但那是来自 FrameworkBundle——添加到纯 CLI 应用程序似乎需要大量开销,因为它需要进一步的捆绑,例如路由、http、配置、缓存等,这些都与我无关。

(现有的 SO 问题How can i inject dependencies to Symfony Console commands?也假定 FrameworkBundle,BTW。)

我在这里创建了一个测试存储库,其中包含一个说明问题的基本命令: https://github.com/joachim-n/console-with-di

Symfony 3/4/5 路

自 2018 年和Symfony 3.4+ DI 功能以来,您可以将命令用作服务

感谢@TravisCarden,您可以在这里找到工作演示

简而言之:

1. 应用内核

<?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())]);
                }
            }
        };
    }
}

2. 服务

# config/services.yml

services:
    _defaults:
        autowire: true

    App\:
        resource: '../app'

    Symfony\Component\Console\Application:
        public: true

3.bin文件

# 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

如果你对更详细的解释感兴趣,我写了一篇文章为什么你应该结合 Symfony 控制台和依赖注入

是的,不需要整个框架。 在您的情况下,首先您需要创建一种入口脚本。 类似的东西:

<?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();

在这个例子中,我们创建容器,然后将命令注册为服务,向命令添加一个依赖项(在我们的例子中是整个容器——但显然你可以创建另一个依赖项并注入它)并编译容器。 然后我们只需创建应用程序,将命令实例添加到应用程序并运行它。

当然,您可以将容器的所有配置保留在yamlxml ,甚至可以使用 PHP 格式。

四年后,但适用于寻找类似解决方案的人。 我有一个看起来相似的场景,并通过依赖注入实现了我自己的样板,准备好 go 用于独立的 symfony 控制台应用程序,支持命令和服务的自动连接和自动配置。

composer create-project coral-media/crune crune

接收容器的示例命令。 源代码可在Github 获取

快乐编码!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM