Я использую 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 обновляет Q
Соответствующими методами должны быть getContainerClass (не только getContainerBaseClass) и initializeContainer в базовом классе Kernel. Строки 452-501 в Kernel.php для справки.
Вообще не понимаю, почему они актуальны. getContainerClass - это класс, который работает с кешем. Рассмотрим $class = $this->getContainerClass(); $cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug);. В initializeContainer я могу только в основном украшать контейнер, но это ограничено.
Ядро вызывает initializeContainer() в boot(), который, в свою очередь, получает имя класса контейнера от getContainerClass(), а не от getContainerBaseClass(). После этого $this->container = new $class();
@ccKep А как же !$cache->isFresh(). Означает ли это, что если кеш чист? Но у меня получилось, что initializeContainer надо переписать. Для этого мне нужно скопировать весь этот внутренний материал и добавить условие в !$cache->isFresh(), чтобы использовать мой класс.
Эта часть просто выгружает контейнер в каталог кеша, но не останавливает часть $this->container = new $class();. Вам не нужно переписывать метод initializeContainer, просто верните свое собственное пространство имен классов контейнера + имя в getContainerClass (и, возможно, getContainerBaseClass?), Чтобы ваш экземпляр был создан?
Позвольте нам продолжить обсуждение в чате.




Итак, чтобы понять, как работает ответ и подходит ли он вам - несколько комментариев.
К контейнеру относятся два класса. Во-первых, сам контейнер: 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:.
`` ''
пространство имен AppBundle \ DependencyInjection;
class TestContainer extends \ appTestDebugProjectContainer {
public $fakeParams = [];
public function getParameter($name) {
$fake = @$this->fakeParams[$name];
if ($fake) return $fake;
return parent::getParameter($name);
}
}
`` ''
#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.
Из любопытства: зачем переписывать контейнер?