首页 文章

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

提问于
浏览
3

我有一个命令行应用程序,到目前为止使用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

2 回答

  • 1

    是的,整个框架不是必需的 . 在您的情况下,首先需要创建一种入口脚本 . 像这样的东西:

    <?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格式 .

  • 0

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

    You can find working demo here ,感谢@TravisCarden

    简而言之:

    1. App内核

    <?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
    

    如果您对更详细的解释感兴趣,我写了一篇文章Why You Should Combine Symfony Console and Dependency Injection .

相关问题