Как Symfony4 автоматически связывает аргументы с конструктором UserPasswordEncoderCommand? - PullRequest
0 голосов
/ 07 февраля 2019

Это вопрос о Symfony 4 autowiring , использующей только «массив» в качестве аргумента типа аргумента конструктора.Я привожу конкретный случай, но это может быть полезно для других, поскольку такая ситуация возникает в нескольких пакетах Symfony.

Эта команда Symfony запрашивает пароль и кодирует его:
php bin/console security:encode-password

Код этой команды находится в vendor / symfony / security-bundle / Command / UserPasswordEncoderCommand.php
В Symfony 4.2.3 это конструктор для UserPasswordEncoderCommand:

class UserPasswordEncoderCommand extends Command
{
    protected static $defaultName = 'security:encode-password';

    private $encoderFactory;
    private $userClasses;

    public function __construct(EncoderFactoryInterface $encoderFactory, array $userClasses = [])
    {
        $this->encoderFactory = $encoderFactory;
        $this->userClasses = $userClasses;

        parent::__construct();
    }

Symfony использует внедрение зависимостейвызывать конструктор, передавая автоматически определенные аргументы.Вышеупомянутый первый аргумент конструктора $ encoderFactory автоматически подключается с использованием подсказки типа EncoderFactoryInterface.Мой вопрос: Как автоматически подключается второй аргумент $ userClasses? Как Symfony знает, что должен содержать массив?Отладочные операторы печати показывают, что массив содержит одно значение «App \ Entity \ User».

Вот мой config / packages / security.yaml

security:
    encoders:
        App\Entity\User:
            algorithm: argon2i

    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator

            # activate different ways to authenticate

            # http_basic: true
            # https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate

            # form_login: true
            # https://symfony.com/doc/current/security/form_login_setup.html

            logout:
                path:   app_logout
                # Where to redirect after logout
                target: app_login

Я думаю, что следующие три файлаважно ответить на этот вопрос.
vendor / symfony / security-bundle / Resources / config / console.xml содержит:

<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    <services>
        <defaults public="false" />

        <service id="security.command.user_password_encoder" class="Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand">
            <argument type="service" id="security.encoder_factory"/>
            <argument type="collection" /> <!-- encoders' user classes -->
            <tag name="console.command" command="security:encode-password" />
        </service>
    </services>
</container>

Часть vendor / symfony / security-bundle / Resources / config / security.xml содержит:

    <service id="security.encoder_factory" alias="security.encoder_factory.generic" />
    <service id="Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface" alias="security.encoder_factory" />

    <service id="security.user_password_encoder.generic" class="Symfony\Component\Security\Core\Encoder\UserPasswordEncoder">
        <argument type="service" id="security.encoder_factory"></argument>
    </service>

    <service id="security.password_encoder" alias="security.user_password_encoder.generic" public="true" />
    <service id="Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface" alias="security.password_encoder" />

Часть поставщика / symfony / security-bundle / DependencyInjection / SecurityExtension.php содержит:

function load(array $configs, ContainerBuilder $container)
...
    if (class_exists(Application::class)) {
        $loader->load('console.xml');
        $container->getDefinition('security.command.user_password_encoder')->replaceArgument(1, array_keys($config['encoders']));
    }

Я задаю этот вопрос, потому что мне хотелось быиспользовать UserPasswordEncoderCommand.php в качестве отправной точки для команды для создания пользователя с правами администратора в моей базе данных.Это даст первому администратору разрешение на вход в систему с помощью веб-браузера для создания других пользователей.

Я попытался скопировать vendor / symfony / security-bundle / Command / UserPasswordEncoderCommand.php в src / Command / AddUserCommand.php иизменив эти строки:

< namespace Symfony\Bundle\SecurityBundle\Command;
---
> namespace App\Command;

< class UserPasswordEncoderCommand extends Command
---
> class AddUserCommand extends Command

<     protected static $defaultName = 'security:encode-password';
---
>     protected static $defaultName = 'app:add-user';

Когда я запустил эту команду: php bin/console app:add-user появилась эта ошибка:

There are no configured encoders for the "security" extension.

Это произошло потому, что конструктор __construct (EncoderFactoryInterface $ encoderFactory, массив $ userClasses =[]) был вызван только с одним аргументом, поэтому вторым аргументом по умолчанию является пустой массив.

Кажется, эти файлы также необходимо скопировать из vendor / symfony / security-bundle куда-нибудь в каталог src /

DependencyInjection/SecurityExtension.php
Resources/config/console.xml

и быть как-то изменено. Какие другие файлы необходимо скопировать в src /? Документация Symfony под https://symfony.com превосходна, но я не могу найти, где описана эта ситуация.

Обратите внимание, что этокоманда работает:

php bin/console app:add-user mypassword "App\Entity\User"

но я не хочу, чтобы пользователь вводил это.И это может измениться, если меняется security.yaml.

1 Ответ

0 голосов
/ 07 февраля 2019

В этом конкретном случае данные собираются с помощью SecurityBundle как часть процесса конфигурации.Когда ядро ​​загружается, все пакеты собираются и их соответствующие расширения, обычно внутри подпапки DependencyInjection, будут зарегистрированы.Когда контейнер создается, он проходит через все эти расширения, чтобы они могли изменять сервисы или добавлять свои собственные.Вот как Symfony собирает конфиги изнутри пакета, когда основное приложение само не знает, где они хранятся, а пакет знает.

Получение пользовательских классов выполняется с помощью Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension при обработке encoders -раздела security.yml.Если вы посмотрите на раздел кодировщиков вашего security.yaml, вы заметите, что каждому кодировщику присваивается имя класса.Затем метод будет использовать этот массив, чтобы создать соответствующий кодировщик для значения и сохранить имя класса в качестве ключа.Этот массив передается фабрике кодировщика в createEncoders.После этого он возьмет тот же массив из конфигурации, получит имена каждого пользовательского класса, используя array_keys(), и заменит аргумент смещением 1, то есть $userClasses, для команды.

Эта концепция называетсяСемантическая конфигурация и внутри вашего собственного приложения это, вероятно, было бы излишним, так как вы должны определить конфигурацию и затем написать логику вручную.Вместо этого вы, вероятно, просто задали бы массив с именами классов в разделе параметров вашего services.yaml.

Если вы хотите передать другие службы, которые могут поступать из других пакетов, о которых ваш пакет не знаетЕсть еще один подход под названием CompilerPasses.В CompilerPass вы можете собрать все классы, например, с определенным тегом, а затем передать их ссылки на метод или конструктор вашего сервиса.Это, например, используется внутри Symfony для регистрации EventListeners или для обеспечения доступности команд в консольном приложении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...