Как переписать базовый класс контейнера в Symfony 3.2

Я использую symfony 3.2. Хочу переписать app/AppKernel#getContainerBaseClass. На самом деле я хочу переписать Container и создать свой собственный, который расширяется от Symfony\Component\DependencyInjection\Container.

Я обнаружил, что AppKernel имеет #getContainerBaseClass, который, как я понял, возвращает контейнерный класс. Я пробовал следующее:

protected function getContainerBaseClass() {
    if ('test' == $this->environment) {
        return \AppBundle\DependencyInjection\TestContainer::class;
    }

    return parent::getContainerBaseClass();
}

И это не работает. Откуда я знаю, что это не работает. Сначала я проверяю, что контейнер не ведет себя из настроенного контейнера. Во-вторых, я установил точку останова внутри упомянутого метода, и он не был вызван. Но точки останова в AppKernel#getRootDir и AppKernel#getCacheDir вызывались. Искал в Kernel что-нибудь полезное. Но было сложно понять, почему это не сработало. Пытался очистить кеш.

Мне кажется, что я что-то не так делаю. Но в Интернете нет ничего о переписывании контейнера Symfony. Это странно.

Я хочу переписать контейнер, чтобы можно было изменять (имитировать) конфигурацию. Насколько я знаю, в настоящее время в symfony есть набор параметров, который нельзя изменить.

Из любопытства: зачем переписывать контейнер?

ccKep 02.04.2018 20:48

@ccKep обновляет Q

FreeLightman 02.04.2018 20:50

Соответствующими методами должны быть getContainerClass (не только getContainerBaseClass) и initializeContainer в базовом классе Kernel. Строки 452-501 в Kernel.php для справки.

ccKep 02.04.2018 20:51

Вообще не понимаю, почему они актуальны. getContainerClass - это класс, который работает с кешем. Рассмотрим $class = $this->getContainerClass(); $cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug);. В initializeContainer я могу только в основном украшать контейнер, но это ограничено.

FreeLightman 02.04.2018 21:00

Ядро вызывает initializeContainer() в boot(), который, в свою очередь, получает имя класса контейнера от getContainerClass(), а не от getContainerBaseClass(). После этого $this->container = new $class();

ccKep 02.04.2018 21:09

@ccKep А как же !$cache->isFresh(). Означает ли это, что если кеш чист? Но у меня получилось, что initializeContainer надо переписать. Для этого мне нужно скопировать весь этот внутренний материал и добавить условие в !$cache->isFresh(), чтобы использовать мой класс.

FreeLightman 02.04.2018 21:15

Эта часть просто выгружает контейнер в каталог кеша, но не останавливает часть $this->container = new $class();. Вам не нужно переписывать метод initializeContainer, просто верните свое собственное пространство имен классов контейнера + имя в getContainerClass (и, возможно, getContainerBaseClass?), Чтобы ваш экземпляр был создан?

ccKep 02.04.2018 21:17

Позвольте нам продолжить обсуждение в чате.

FreeLightman 02.04.2018 21:27
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
0
8
81
1

Ответы 1

Итак, чтобы понять, как работает ответ и подходит ли он вам - несколько комментариев.

К контейнеру относятся два класса. Во-первых, сам контейнер: Symfony\Component\DependencyInjection\Container. Он не изменяется для каждого env и содержит динамические методы, которые не имеют фактических данных (без параметров, служб, только вызовы для их получения). Во-вторых, комплектный контейнер. И в нем есть все параметры и сервисы. Он анализирует файлы yml и создает из них методы / свойства. Он содержит гораздо больше методов, чем Symfony\Component\DependencyInjection\Container. Например, у него есть #getDefaultParameters, который возвращает все параметры в одном массиве. Он также имеет #getTwigService, который фактически является экземпляром веточки. Таким образом, он превращает файлы yml в реквизиты и методы php. Это объединяет имя класса контейнера и пространство имен зависит от env. Итак, для тестового env это \appTestDebugProjectContainer. И поскольку он скомпилирован и отличается от env, он находится в папке кеша. И последнее: это расширение от Symfony\Component\DependencyInjection\Container.

Кроме того, в скомпилированном контейнере есть несколько динамических методов, которые перезаписывают Symfony\Component\DependencyInjection\Container (например, #getParameter).

Итак, теперь мы можем вернуться к вопросу. AppKernel имеет следующие методы, относящиеся к контейнеру:

  • getContainerBaseClass, который возвращает исходный контейнер. По умолчанию это Symfony\Component\DependencyInjection\Container. Вы можете изменить его, чтобы он возвращал ваш индивидуальный контейнер. Но на самом деле здесь не так много того, что можно настроить, потому что контейнер пакетов переписывает множество методов и добавляет собственную логику.
  • getContainerClass, который возвращает имя класса контейнера пакетов. Вот в чем суть. Это контейнер, который фактически используется в вашем env. И вы можете продлить его. Но сначала вы должны знать, из какого класса расширяться. Для моего тестового окружения это appTestDebugProjectContainer. Вы можете получить класс для своих нужд по следующей логике:

    вернуть $ this-> name.ucfirst ($ this-> environment). ($ this-> debug? 'Debug': ''). 'ProjectContainer';

$this->name обычно представляет собой app. В остальном все довольно просто. - initializeContainer делает все, чтобы получить и загрузить контейнер. Интересующая нас линейка - это $this->container = new $class();. Мы должны переписать переменную $class с нашим

Итак, каково же конечное решение. Создайте где-нибудь свой собственный контейнер и расширьте его из имени связанного контейнера. Перепишите #initializeContainer в AppKernel.

Пример для test env continer:.

  1. Создайте контейнер:

`` ''

пространство имен AppBundle \ DependencyInjection;

class TestContainer extends \ appTestDebugProjectContainer {

public $fakeParams = [];

public function getParameter($name) {
    $fake = @$this->fakeParams[$name];
    if ($fake) return $fake;
    return parent::getParameter($name);
}

}

`` ''

  1. Перепишите #getContainerClass в AppKernel:

`` '' класс AppKernel расширяет ядро { // ... защищенная функция initializeContainer () { $ class = $ this-> getContainerClass (); $ cache = new ConfigCache ($ this-> getCacheDir (). '/'. $ class. '. php', $ this-> debug); $ fresh = true;

    if (!$cache->isFresh()) {
        $container = $this->buildContainer();
        $container->compile();
        $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());

        $fresh = false;
    }

    require_once $cache->getPath();
    if ('test' == $this->environment) {
        $class = \AppBundle\DependencyInjection\TestContainer::class;
    }


    $this->container = new $class();
    $this->container->set('kernel', $this);

    if (!$fresh && $this->container->has('cache_warmer')) {
        $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir'));
    }
}
}

`` ''

Я вставил только следующие строки:

if ('test' == $this->environment) {
            $class = \AppBundle\DependencyInjection\TestContainer::class;
        }

Все остальное просто скопировано с Kernel#initializeContainer.

Это подходит для меня.

Чтобы увидеть, что вы можете расширить, вы можете проверить связанный контейнер, который находится (для тестового env) в var/cache/test/appTestDebugProjectContainer.php.

Другие вопросы по теме