在没有 Symfony 框架包的情况下,如何使用依赖注入的 Symfony 控制台?
how do I use Symfony console with dependency injection, without the Symfony framework bundle?
我有一个命令行应用程序,目前它使用 Symfony 依赖注入组件。我现在发现我想添加命令行选项并改进输出格式,而 Symfony 控制台组件似乎是一个不错的选择。
但是,我无法理解如何让我的 Symfony 控制台命令 classes 接收容器对象。
我找到的文档使用了 ContainerAwareCommand class,但那是来自 FrameworkBundle——添加到纯 CLI 应用程序似乎需要大量开销,因为它需要进一步的捆绑,例如路由, http, config, cache 等,none 其中与我有任何相关性。
(现有的 SO 问题 How can i inject dependencies to Symfony Console commands? 也假定 FrameworkBundle,顺便说一句。)
我在这里创建了一个测试存储库,其中包含一个说明问题的基本命令:https://github.com/joachim-n/console-with-di
是的,不需要整个框架。在您的情况下,首先您需要创建一种入口脚本。类似的东西:
<?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 格式。
Symfony 3/4/5 方式
自 2018 年和 Symfony 3.4+ DI features 以来,您可以将 命令用作服务。
你可以find working demo here,感谢@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
如果你对更详细的解释感兴趣,我写了一篇postWhy You Should Combine Symfony Console and Dependency Injection。
四年后,但适用于寻找类似解决方案的人。我有一个看起来相似的场景,并使用依赖注入实现了我自己的样板,准备好用于独立的 symfony 控制台应用程序,支持 auto-wiring 和 auto-configure 命令和服务。
composer create-project coral-media/crune crune
接收容器的示例命令。
源代码位于 Github
编码愉快!
我有一个命令行应用程序,目前它使用 Symfony 依赖注入组件。我现在发现我想添加命令行选项并改进输出格式,而 Symfony 控制台组件似乎是一个不错的选择。
但是,我无法理解如何让我的 Symfony 控制台命令 classes 接收容器对象。
我找到的文档使用了 ContainerAwareCommand class,但那是来自 FrameworkBundle——添加到纯 CLI 应用程序似乎需要大量开销,因为它需要进一步的捆绑,例如路由, http, config, cache 等,none 其中与我有任何相关性。
(现有的 SO 问题 How can i inject dependencies to Symfony Console commands? 也假定 FrameworkBundle,顺便说一句。)
我在这里创建了一个测试存储库,其中包含一个说明问题的基本命令:https://github.com/joachim-n/console-with-di
是的,不需要整个框架。在您的情况下,首先您需要创建一种入口脚本。类似的东西:
<?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 格式。
Symfony 3/4/5 方式
自 2018 年和 Symfony 3.4+ DI features 以来,您可以将 命令用作服务。
你可以find working demo here,感谢@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
如果你对更详细的解释感兴趣,我写了一篇postWhy You Should Combine Symfony Console and Dependency Injection。
四年后,但适用于寻找类似解决方案的人。我有一个看起来相似的场景,并使用依赖注入实现了我自己的样板,准备好用于独立的 symfony 控制台应用程序,支持 auto-wiring 和 auto-configure 命令和服务。
composer create-project coral-media/crune crune
接收容器的示例命令。 源代码位于 Github
编码愉快!