如何使用命令在独立应用程序中使用 symfony 的 DependencyInjection?

Rau*_*osa 5 php dependency-injection symfony symfony-console

我一直在使用 symfony/console 来创建命令并像这样注册它们,一切正常:

垃圾箱/控制台:

#!/usr/bin/env php
<?php
require_once __DIR__ . '/../vendor/autoload.php';

use App\Commands\LocalitiesCommand;
use Symfony\Component\Console\Application;

$app = new Application();
$app->add(new LocalitiesCommand(new LocalitiesGenerator()));
$app->run();
Run Code Online (Sandbox Code Playgroud)

src/Commands/LocalitiesCommand.php:

<?php

declare(strict_types=1);

namespace App\Commands;

use App\LocalitiesGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;


final class LocalitiesCommand extends Command
{
    protected static $defaultName = 'app:generate-localities';

    public function __construct(private LocalitiesGenerator $localitiesGenerator)
    {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this
            ->setDescription('Generate localities.json file')
            ->setHelp('No arguments needed.');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->localitiesGenerator->generateJsonLocalities();
        $output->writeln("File localities.json generated!");
        return Command::SUCCESS;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我想使用 symfony/dependency-injection 自动注入服务,我正在阅读文档并做了一些更改:

垃圾箱/控制台:

#!/usr/bin/env php
<?php
require_once __DIR__ . '/../vendor/autoload.php';

use App\Commands\LocalitiesCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\Config\FileLocator;

$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/src/config'));
$loader->load('services.yaml');
$container->compile();


$app = new Application();
$app->add(new LocalitiesCommand());
$app->run();
Run Code Online (Sandbox Code Playgroud)

配置/services.yaml:

services:
  _defaults:
    autowire: true
    autoconfigure: true
    public: false
Run Code Online (Sandbox Code Playgroud)

但当我实例化我的命令时,仍然要求我在构造函数中添加我的服务。为什么它不起作用?

yiv*_*ivi 10

首先,我们要澄清一个误解:

\n
\n

但当我实例化我的命令时,仍然要求我在构造函数中添加我的服务。为什么它不起作用?

\n
\n

如果您致电new Foo(),那么您将不再获得自动连线 DI 权益。如果你想使用自动装配和自动依赖注入,你需要让 Symfony 为你工作。你打电话时new,您正在手动实例化对象,并且您需要自己处理 DI。

\n

既然如此,你将如何做到这一点呢?

\n
\n

第一的,composer.json具有基本的依赖项和自动加载器声明:

\n

完整的目录结构最终将如下所示:

\n
<project_dir>\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 composer.json \n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 app \n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src/\n\xe2\x94\x82    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ConsoleCommand/\n\xe2\x94\x82    \xe2\x94\x82       \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 FooCommand.php\n\xe2\x94\x82    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 Text/\n\xe2\x94\x82          \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 Reverser.php\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 config/\n\xe2\x94\x82    \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 services.yaml\n
Run Code Online (Sandbox Code Playgroud)\n

现在,每个部分:

\n

composer.json包含所有依赖项和自动加载器的文件

\n
{\n    "require": {\n        "symfony/dependency-injection": "^5.3",\n        "symfony/console": "^5.3",\n        "symfony/config": "^5.3",\n        "symfony/yaml": "^5.3"\n    },\n    "autoload": {\n        "psr-4": {\n            "App\\\\": "src"\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

前端控制器脚本,运行应用程序的文件(app在我的例子中):

\n
#!/usr/bin/env php\n<?php declare(strict_types=1);\n\nuse Symfony\\Component;\n\nrequire __DIR__ . \'/vendor/autoload.php\';\n\nclass App extends Component\\Console\\Application\n{\n\n    public function __construct(iterable $commands)\n    {\n        $commands = $commands instanceof Traversable ? iterator_to_array($commands) : $commands;\n\n        foreach ($commands as $command) {\n            $this->add($command);\n        }\n\n        parent::__construct();\n    }\n}\n\n$container = new Component\\DependencyInjection\\ContainerBuilder();\n$loader    = new Component\\DependencyInjection\\Loader\\YamlFileLoader($container, new Component\\Config\\FileLocator(__DIR__ . \'/config\'));\n\n$loader->load(\'services.yaml\');\n$container->compile();\n\n$app = $container->get(App::class);\n$app->run();\n
Run Code Online (Sandbox Code Playgroud)\n

项目的服务容器配置:

\n
# config/services.yaml\nservices:\n  _defaults:\n    autowire: true\n\n  _instanceof:\n    Symfony\\Component\\Console\\Command\\Command:\n      tags: [ \'app.command\' ]\n\n  App\\:\n    resource: \'../src/*\'\n\n  App:\n    class: \\App\n    public: true\n    arguments:\n      - !tagged_iterator app.command\n
Run Code Online (Sandbox Code Playgroud)\n

FooCommand类:

\n
<?php declare(strict_types=1);\n\n// src/ConsoleCommand/FooCommand.php\n\nnamespace App\\ConsoleCommand;\n\nuse App\\Text\\Reverser;\nuse Symfony\\Component\\Console;\n\nclass FooCommand extends Console\\Command\\Command\n{\n\n    protected static $defaultName = \'foo\';\n\n    public function __construct(private Reverser $reverser)\n    {\n        parent::__construct(self::$defaultName);\n    }\n\n    protected function execute(Console\\Input\\InputInterface $input, Console\\Output\\OutputInterface $output): int\n    {\n        $output->writeln(\'Foo was invoked\');\n        $output->writeln($this->reverser->exec(\'the lazy fox\'));\n\n        return self::SUCCESS;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

以上取决于App\\Text\\Reverser服务,DI组件会自动为我们注入:

\n
<?php declare(strict_types=1);\n\nnamespace App\\Text;\n\nclass Reverser\n{\n\n    public function exec(string $in): string\n    {\n        return \\strrev($in);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

安装并转储自动加载器后,通过执行php app(1) 我发现该foo命令可用 (2):\n在此输入图像描述

\n

我可以执行php app foo,并且使用其注入的依赖项正确执行该命令:

\n

在此输入图像描述

\n

一个独立的 Symfony 控制台应用程序,具有最小的依赖项和自动依赖项注入。

\n

(这里是一个非常相似的示例的所有代码)。

\n