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

11

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

然而,我不知道如何让我的Symfony控制台命令类接收容器对象。

我找到的文档使用了ContainerAwareCommand类,但这是来自FrameworkBundle -- 在一个纯CLI应用中添加这个所需要的诸如路由、http、配置、缓存等相关组件显然是过度的负担,对我来说完全没有任何意义。

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

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


你的链接中第二个答案怎么样? - COil
3个回答

7

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.二进制文件

# 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 Console和依赖注入

1
这看起来是一个很好的解决方案!它需要为Symfony 4进行更改吗?我尝试了一下,但是出现了一个异常,说找不到Application的服务定义。 - TravisCarden
1
谢谢您,@tomáš-votruba,但我仍然遇到相同的错误。我根据您的答案创建了一个概念验证,并在https://github.com/TravisCarden/stackoverflow-a-50356503上为其创建了一个GitHub存储库。您是否愿意查看它以查看问题所在?也许我们可以验证一个完整的解决方案并相应地更新答案。 - TravisCarden
1
谢谢你,@tomáš-votruba。我已经更新了仓库以反映您的最新编辑。更改是有意义的,但我仍然遇到致命错误。我想知道我们是否在翻译中失去了一些东西。您能否发送一个拉取请求到我的仓库? - TravisCarden
1
检查一下:https://github.com/TravisCarden/stackoverflow-a-50356503/pull/1我还将存储库演示的链接添加到答案中,如果您没意见的话。 - Tomas Votruba
在较新版本的Symfony中,创建内核需要先设置环境和调试选项。 - przemo_li
显示剩余5条评论

3

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

<?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格式中。

缺少 Symfony\Component\DependencyInjection\Reference 的导入。 - Patrick Bergner

0
四年后,但是这篇文章是为那些正在寻找类似解决方案的人而写的。我遇到了一个类似的场景,并使用依赖注入实现了自己的样板文件,以便为独立的Symfony控制台应用程序准备就绪,支持命令和服务的自动装配和自动配置。 composer create-project coral-media/crune crune 一个接收容器的示例命令已经准备好了。 源代码可在Github上获取。
祝编码愉快!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接