composer.json:
"repositories": [
{
"type": "git",
"url": "https://github.com/proklung/bitrix.core.symfony"
}
]composer require proklung/bitrix-core-symfonyВ init.php:
use Prokl\ServiceProvider\ServiceProvider;
$serviceProvider = new ServiceProvider('local/configs/services.yaml');Для обеспечения "преемственности" (похожести) с оригиналом можно задать путь к файлу конфигурации (скажем, bundles.php)
бандлов вторым (необязательным) параметром конструктора.
Предполагается, что переменные среды к моменту инициализации контейнера уже загружены тем или иным способом.
Значимые переменные среды:
-
DEBUG(булево значение - режим отладки),APP_DEBUG- алиасDEBUG, но с большим приоритетом (если одновременно присустствуютDEBUGиAPP_DEBUG, то в дело пойдет значениеAPP_DEBUG). -
APP_ENV- код окружения. Если код не задан, то будет проинтерпретировано значениеDEBUGв смысле - если в режиме отладки, то окружениеdev, иначеprod.
Если переменные среды не заданы, то с помощью класса Prokl\ServiceProvider\LoadEnvironment их можно загрузить.
Скажем, в init.php, перед инициализацией контейнера:
// Параметр конструктора - путь, где лежат файлы .env
$loader = new \Prokl\ServiceProvider\LoadEnvironment($_SERVER['DOCUMENT_ROOT'] . '/../..');
$loader->load(); // Загрузка $_ENV
$loader->process(); // Обработка переменных- Опция
compile.containerв подтягиваемом конфиге - компилировать ли контейнер в файл. Если не задана, то "нет, не компилировать". Имеет смысл для окружения, не равного "dev". Т.е. опция управляет дампированием контейнера на проде.
Место, где хранятся дампы контейнеров: <значение переменной контейнера kernel.cache_dir>/<SITE_ID>/containers
Определяются классом AppKernel. По умолчанию:
- путь к кэшу (
kernel.cache_dir) -/bitrix/cache - путь к логам (
kernel.logs_dir) -'/../../logs'(два уровня выше DOCUMENT_ROOT - особенности используемой сборки Битрикс)
Чтобы это изменить нужно отнаследоваться от класса AppKernel и переопределить несколько переменных:
use Prokl\ServiceProvider\Services\AppKernel;
class MyKernel extends AppKernel
{
protected $cacheDir = '/bitrix/cache/mycache';
protected $logDir = '/logs-saver';
}(второй вариант - отнаследоваться от AppKernel и переопределить методы getCacheDir и getLogDir).
Изменить через наследование класс ядра:
class MyServiceProvider extends ServiceProvider
{
protected $kernelServiceClass = MyKernel::class;
protected $cacheDir = '/bitrix/cache/mycache';
}Второй вариант - отнаследоваться от ServiceProvider и заменить метод getPathCacheDirectory своей логикой.
Файл конфигурации - /config/standalone_bundles.php. Этот путь можно изменить через конструктор.
Папка, где лежат конфигурации - /local/configs. Конфигурации бандлов - /local/configs/packages.
Согласно концепции Symfony все сервисы (в идеале) должны быть приватными и инжектиться. Но в кастомном случае
часто нужно получать их через хелпер-сервис-локатор. Для превращения нужных сервисов в публичные предлагается
такое решение. В общем разделе параметров контейнера появилась опция publicable_services:
parameters:
publicable_services:
- 'snc_redis.default'После компиляции контейнера приватный сервис snc_redis.default станет публичным.
Отдельные контейнеры - со своим конфигом, полностью изолированные (для модулей и т.п.).
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Prokl\ServiceProvider\Micro\AbstractStandaloneServiceProvider;
use Prokl\ServiceProvider\Micro\ExampleAppKernel;
class ExampleMicroServiceProvider extends AbstractStandaloneServiceProvider
{
/**
* @var ContainerBuilder $containerBuilder Контейнер.
*/
protected static $containerBuilder;
/**
* @var string $pathBundlesConfig Путь к конфигурации бандлов.
*/
protected $pathBundlesConfig = '/src/Micro/example.config/standalone_bundles.php';
/**
* @var string $configDir Папка, где лежат конфиги.
*/
protected $configDir = '/src/Micro/example.config/example.config/example.yaml';
/**
* @var string $kernelServiceClass Класс, реализующий сервис kernel.
* Нужен для того, чтобы экземпляры контейнеров в kernel сервисе не перемешивались.
*/
protected $kernelServiceClass = ExampleAppKernel::class;
}Пример класса ExampleAppKernel:
use Prokl\ServiceProvider\Micro\AbstractKernel;
class ExampleAppKernel extends AbstractKernel
{
protected static $kernelContainer;
}Где надо - инициализация:
$micro = new ExampleMicroServiceProvider('src/SymfonyDI/Micro/example.config/example.yaml');Хэлпер container заточен под работу с микро-сервис-провайдерами:
var_dump(container($micro)->getParameter('example'));Чтобы сервис запустился автоматически после инициализации контейнера, он должен быть помечен тэгом service.bootstrap.
app.options:
class: Prokl\Services\AppOptions
arguments: ['%kernel.environment%', '@parameter_bag']
tags: ['service.bootstrap']Поддерживается приоритет запуска. Тогда надо так:
app.options:
class: Local\Services\AppOptions
arguments: ['%kernel.environment%', '@parameter_bag']
tags:
- { name: 'service.bootstrap', priority: 100 }Сервис с приоритетом 100 запустится раньше сервиса с приоритетом 200.
Тэг: bitrix.events.init.
event- название события.method- метод-обработчик в сервисеmodule- модуль событияsort- сортировка
admin_entity_edit.event_init:
class: Local\Bitrix\PsModuleInitializer
tags:
- { name: bitrix.events.init, module: ps.d7, event: onGetEntityList, method: registerEntities, sort: 0 }Тэг twig.extension.
service.twig.parameter:
class: Prokl\Bundles\ParameterBundle\Twig\ParameterExtension
public: true
arguments:
- '@service.parameter'
tags:
- { name: twig.extension }Автоматом регистрируются сервисы:
service_container(и alias) - сервис-контейнер целикомapp.request- конвертор глобалов в Request- синонимы сервиса
kernel delegated_container_manipulator- манипулятор делегированными контейнерами.bitrix.request.instance- Экземпляр битриксового Requestbitrix.response.instance- Экземпляр битриксового Responsebitrix.request- Symfony Request, полученный из битриксовогоbitrix.request.psr7- Битриксовый Request, приведенный к PSR-7bitrix.response- Symfony Response, полученный из битриксовогоbitrix.response.psr7- Битриксовый Response, приведенный к PSR-7psr17.http_factory- HttpFactory стандарта PSR-17psr18.http_client- Http client стандарта PSR-18
container()- отдает экземпляр контейнера (выступает в роли сервис-локатора):
$kernel = container()->get('kernel');delegatedContainer()- отдает экземпляр манипулятора (реализующего интерфейсSymfony\Component\DependencyInjection\ContainerInterface) делегированными контейнерами.
$moduleService = delegatedContainer()->get('my_module_id.service');В контейнере делегированный контейнер помечается тэгом delegated.container (их может быть сколь угодно много):
module_notifier_container:
class: Symfony\Component\DependencyInjection\ContainerInterface
factory: ['Proklung\Notifier\DI\Services', 'getInstance']
tags:
- { name: 'delegated.container' }Делегированный контейнер - автономный контейнер, сформированные в модуле, плагине и тому подобных местах.
Автоматом подтягиваются в контейнер сервисы из битриксового сервис-локатора. Формат,
секция services из /bitrix/.settings.php и /bitrix/.settings_extra.php. Также загрузчик пробегает
по списку установленных модулей и подцепляет их тоже.
Для отдельных сервис-контейнеров (отнаследованных от AbstractStandaloneServiceProvider) такая загрузка
не производится.
Если эта фича не нужна, то нужно отнаследоваться от ServiceProvider и заглушить метод loadBitrixServiceLocatorConfigs.
class MyServiceProvider extends ServiceProvider
{
/**
* {@inheritDoc}
*/
protected function loadBitrixServiceLocatorConfigs(DelegatingLoader $loader) : void
{
}
}Адаптер-импортер настроек битриксового сервис-локатора (.settings.php) в симфонический контейнер.
importParameters(ContainerInterface $container, array $settings, ?string $section = null)- импорт параметров.section- если задано, то параметры лягут в именованную секцию параметров контейнера.importServices(ContainerInterface $container, array $services)- импорт сервисов. Формат
Считываются конфиги в Битриксе как-то так:
use Bitrix\Main\Config\Configuration;
$this->config = Configuration::getInstance()->get('my_config') ?? [];
// Из модуля
$this->parameters = Configuration::getInstance('my.module')->get('parameters') ?? [];
$this->services = Configuration::getInstance('my.module')->get('services') ?? [];С версии 21.400.0 (от 16.07.2021) главного модуля в Битриксе появился сносный роутер.
Чтобы использовать в этом контексте контейнер нужно:
- В файле описания маршрутов (например,
/local/routes/web.php) в самом верху подключить ядро.
Это важно, т.к. без этого сервис-провайдер завалится на стадии подключения файла с роутами; они подключаются раньше инициализации ядра.
И, если эту проблему еще можно решить, отключив проверку классов сервисов на существование, то запускающиеся автоматом сервисы по тэгу
service.bootstrap обломятся стопроцентно.
use Local\ExampleBitrixActionController;
use Prokl\ServiceProvider\ServiceProvider;
use Bitrix\Main\Routing\Controllers\PublicPageController;
use Bitrix\Main\Routing\RoutingConfigurator;
require_once($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
$container = ServiceProvider::instance();
return function (RoutingConfigurator $routes) use ($container) {
$routes->get('/countries3/{country}/', [$container->get(ExampleBitrixActionController::class), 'cacheAction'])
->default('country', 'Russia')
->name('first_bitrix_route')
;
$routes->get('/', new PublicPageController('/index.php')); // Старый роут на статике.
};Класс битриксового контроллера (ExampleBitrixActionController) с заточкой под DI:
namespace Local;
use Bitrix\Main\Engine\Contract\RoutableAction;
use Bitrix\Main\Engine\Controller;
use Symfony\Component\HttpKernel\KernelInterface;
class ExampleBitrixActionController extends Controller implements RoutableAction
{
/**
* @var KernelInterface $kernel
*/
private $kernel;
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
parent::__construct();
}
/**
* @return string|Controller
*/
public static function getControllerClass() {
return ExampleBitrixActionController::class;
}
/**
* @return string
*/
public static function getDefaultName() {
return 'testingAction';
}
public function cacheAction(string $country)
{
return ['cacheDir' => $this->kernel->getCacheDir(), 'country' => $country];
}
public function configureActions()
{
return [
'cache' => [
'prefilters' => [], 'postfilters' => [],
],
];
}
}Описывается сервисом так:
Local\ExampleBitrixActionController:
arguments: ['@kernel']Ничего революционного, но так можно получить нормально-сконфигурированный класс контроллера, со всякими зависимостями и т.п.