Skip to content

Commit

Permalink
Загрузка конфигурации битриксового сервис-локатора.
Browse files Browse the repository at this point in the history
  • Loading branch information
ProklUng committed Jul 13, 2021
1 parent fb1a65e commit 1fa0be5
Show file tree
Hide file tree
Showing 15 changed files with 665 additions and 18 deletions.
23 changes: 23 additions & 0 deletions readme.MD
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,27 @@ var_dump(container($micro)->getParameter('example'));

```php
$kernel = container()->get('kernel');
```

## Импорт в контейнер сервисов битриксового сервис-локатора

Автоматом подтягиваются в контейнер сервисы из битриксового сервис-локатора. [Формат](https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=43&LESSON_ID=14032),
секция `services` из `/bitrix/.settings.php` и `/bitrix/.settings_extra.php`. Также загрузчик пробегает
по списку установленных модулей и подцепляет их тоже.

Для отдельных сервис-контейнеров (отнаследованных от `AbstractStandaloneServiceProvider`) такая загрузка
не производится.

Если эта фича не нужна, то нужно отнаследоваться от `ServiceProvider` и заглушить метод `loadBitrixServiceLocatorConfigs`.

```php
class MyServiceProvider extends ServiceProvider
{
/**
* {@inheritDoc}
*/
protected function loadBitrixServiceLocatorConfigs(DelegatingLoader $loader) : void
{
}
}
```
10 changes: 10 additions & 0 deletions src/Bundles/BundlesLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,14 @@ public static function getBundlesMap() : array
{
return static::$bundlesMap[static::class] ?? [];
}

/**
* Очистить инстанцы бандлов.
*
* @return void
*/
public static function clearBundlesMap() : void
{
static::$bundlesMap[static::class] = [];
}
}
9 changes: 9 additions & 0 deletions src/Micro/AbstractStandaloneServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Exception;
use Prokl\ServiceProvider\Framework\SymfonyCompilerPassBagLight;
use Prokl\ServiceProvider\ServiceProvider;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
Expand Down Expand Up @@ -37,4 +38,12 @@ public function __construct(

parent::__construct($filename, $pathBundlesConfig);
}

/**
* {@inheritDoc}
* @internal Для отдельных контейнеров не нужно грузить сервисы из битриксового сервис-локатора.
*/
protected function loadBitrixServiceLocatorConfigs(DelegatingLoader $loader) : void
{
}
}
55 changes: 41 additions & 14 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Prokl\ServiceProvider\Framework\SymfonyCompilerPassBag;
use Prokl\ServiceProvider\Services\AppKernel;
use Prokl\ServiceProvider\Utils\ErrorScreen;
use Prokl\ServiceProvider\Utils\Loaders\PhpLoaderSettingsBitrix;
use Psr\Container\ContainerInterface;
use Psr\Container\ContainerInterface as PsrContainerInterface;
use RuntimeException;
Expand Down Expand Up @@ -116,10 +117,20 @@ class ServiceProvider
*/
protected $cacheDir = '/bitrix/cache/';

/**
* @var string $projectRoot DOCUMENT_ROOT.
*/
protected $projectRoot = '';

/**
* @var array Конфигурация бандлов.
*/
protected $bundles = [];

/**
* @var ErrorScreen $errorHandler Обработчик ошибок.
*/
private $errorHandler;
protected $errorHandler;

/**
* @var Filesystem $filesystem Файловая система.
Expand All @@ -136,16 +147,6 @@ class ServiceProvider
*/
private $filename;

/**
* @var string $projectRoot DOCUMENT_ROOT.
*/
private $projectRoot = '';

/**
* @var array Конфигурация бандлов.
*/
private $bundles = [];

/**
* @var array $compilerPassesBag Набор Compiler Pass.
*/
Expand Down Expand Up @@ -293,6 +294,9 @@ public function shutdown() : void
$bundle->setContainer(null);
}

$this->bundles = [];
BundlesLoader::clearBundlesMap();

static::$containerBuilder = null;
}

Expand Down Expand Up @@ -821,6 +825,9 @@ private function loadContainerConfig(string $fileName, ContainerBuilder $contain
try {
$loader->load($this->projectRoot . '/' . $fileName);
$loader->load(__DIR__ . '/../config/base.yaml');

$this->loadBitrixServiceLocatorConfigs($loader);

return true;
} catch (Exception $e) {
$this->errorHandler->die('Сервис-контейнер: ' . $e->getMessage());
Expand All @@ -829,6 +836,27 @@ private function loadContainerConfig(string $fileName, ContainerBuilder $contain
}
}

/**
* Конфиги битриксового сервис-локатора.
*
* @param DelegatingLoader $loader Загрузчик.
*
* @return void
* @throws Exception Когда что-то не так с файлами конфигураций.
*
* @since 13.07.2021
*/
protected function loadBitrixServiceLocatorConfigs(DelegatingLoader $loader) : void
{
// Если не найден '/bitrix/.settings.php', то у нас проблемы с Битриксом (не установлен)
// Выбросит исключение.
$loader->load($this->projectRoot . '/bitrix/.settings.php');

if ($this->filesystem->exists($this->projectRoot . '/bitrix/.settings_extra.php')) {
$loader->load($this->projectRoot . '/bitrix/.settings_extra.php');
}
}

/**
* Загрузка конфигураций в различных форматах из папки configs.
*
Expand All @@ -846,9 +874,7 @@ private function configureContainer(ContainerBuilder $container, LoaderInterface
$confDir = $this->projectRoot . $this->configDir;

if (!@file_exists($confDir)) {
throw new RuntimeException(
'Config directory ' . $confDir . ' not exist.'
);
throw new RuntimeException('Config directory ' . $confDir . ' not exist.');
}

$container->setParameter('container.dumper.inline_class_loader', true);
Expand Down Expand Up @@ -882,6 +908,7 @@ private function getContainerLoader(ContainerBuilder $container): DelegatingLoad
new XmlFileLoader($container, $locator),
new YamlFileLoader($container, $locator),
new IniFileLoader($container, $locator),
new PhpLoaderSettingsBitrix($container, $locator),
new PhpFileLoader($container, $locator),
new GlobFileLoader($container, $locator),
new DirectoryLoader($container, $locator),
Expand Down
84 changes: 84 additions & 0 deletions src/Utils/BitrixSettingsDiAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace Prokl\ServiceProvider\Utils;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;

/**
* Class BitrixSettingsDiAdapter
* @package Prokl\ServiceProvider\Utils
*
* @since 12.07.2021
*/
class BitrixSettingsDiAdapter
{
/**
* Импортировать параметры из .settings.php
*
* @param ContainerInterface $container Контейнер.
* @param array $settings Секция parameters .settings.php.
* @param string|null $section Если задано, то параметры попадут в отдельную секцию контейнера.
*
* @return void
*/
public function importParameters(
ContainerInterface $container,
array $settings,
?string $section = null
) : void {
if ($section !== null) {
$container->setParameter($section, $settings);
return;
}

foreach ($settings as $id => $value) {
$container->setParameter($id, $value);
}
}

/**
* Импортировать сервисы из .settings.php.
*
* @param ContainerInterface $container Контейнер.
* @param array $services Секция services .settings.php.
*
* @return void
*/
public function importServices(ContainerInterface $container, array $services) : void
{
foreach ($services as $id => $service) {
if (array_key_exists('constructor', $service)
&&
is_callable($service['constructor'])
) {
/** @var Definition $definition */
$definition = $container->register($id, FactoryClosure::class);
$definition->setFactory([FactoryClosure::class, 'from']);
$definition->addArgument($service['constructor']);
$definition->setPublic(true);
}

if (array_key_exists('className', $service) && is_string($service['className'])) {
$definition = $container->register($id, $service['className'])->setPublic(true);

if (array_key_exists('constructorParams', $service) && is_callable($service['constructorParams'])) {
$arguments = $service['constructorParams']();
if (is_array($arguments)) {
foreach ($arguments as $argument) {
$definition->addArgument($argument);
}
} else {
$definition->addArgument($service['constructorParams']());
}
}

if (array_key_exists('constructorParams', $service) && is_array($service['constructorParams'])) {
foreach ($service['constructorParams'] as $param) {
$definition->addArgument($param);
}
}
}
}
}
}
24 changes: 24 additions & 0 deletions src/Utils/FactoryClosure.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Prokl\ServiceProvider\Utils;

use Closure;

/**
* Class FactoryClosure
* @package Prokl\FrameworkExtensionBundle\Services\Utils
*
* @since 13.07.2021
*/
class FactoryClosure
{
/**
* @param Closure $closure Closure.
*
* @return mixed
*/
public function from(Closure $closure)
{
return $closure();
}
}
95 changes: 95 additions & 0 deletions src/Utils/Loaders/PhpLoaderSettingsBitrix.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace Prokl\ServiceProvider\Utils\Loaders;

use Bitrix\Main\Config\Configuration;
use Bitrix\Main\ModuleManager;
use Prokl\ServiceProvider\Utils\BitrixSettingsDiAdapter;
use Symfony\Component\DependencyInjection\Loader\FileLoader;
use Symfony\Component\DependencyInjection\Loader\ProtectedPhpFileLoader;

/**
* Class PhpLoaderSettingsBitrix
* Загрузчик битриксовых конфигурационных файлов
* @package Prokl\ServiceProvider\Utils\Loaders
*
* @since 12.07.2021
*/
class PhpLoaderSettingsBitrix extends FileLoader
{
/**
* {@inheritdoc}
*/
public function load($resource, string $type = null)
{
// the container and loader variables are exposed to the included file below
$container = $this->container;

$path = $this->locator->locate($resource);

$this->setCurrentDir(\dirname($path));
$this->container->fileExists($path);

// the closure forbids access to the private scope in the included file
$load = \Closure::bind(function ($path, $env) {
return $this->loadBitrixConfig('services', true);
}, $this, ProtectedPhpFileLoader::class);

try {
$callback = $load($path, $this->env);
if (is_array($callback)) {
$adapter = new BitrixSettingsDiAdapter();
$adapter->importServices($container, $callback);
}
} finally {
$this->instanceof = [];
$this->registerAliasesForSinglyImplementedInterfaces();
}
}

/**
* {@inheritdoc}
*/
public function supports($resource, string $type = null)
{
if (!\is_string($resource)
// ToDo более строгая проверка на нужный файл.
|| stripos($resource, '.settings') === false
) {
return false;
}

if (null === $type && 'php' === pathinfo($resource, \PATHINFO_EXTENSION)) {
return true;
}

return 'php' === $type;
}

/**
* Загрузка битриксовых конфигов.
*
* @param string $key Ключ в параметрах битриксовых файлов.
* @param boolean $loadModulesServices Загружать такую же секцию в установленных модулях.
*
* @return array
*/
public function loadBitrixConfig(string $key, bool $loadModulesServices = true) : array
{
$mainBitrixServices = Configuration::getInstance()->get($key) ?? [];

// Собрать конфиги всех установленных модулей.
$servicesModules = [];

if ($loadModulesServices) {
foreach (ModuleManager::getInstalledModules() as $module) {
$services = Configuration::getInstance($module['ID'])->get($key) ?? [];
if (count($services) > 0) {
$servicesModules[] = $services;
}
}
}

return array_merge($mainBitrixServices, ...$servicesModules);
}
}
Loading

0 comments on commit 1fa0be5

Please sign in to comment.