Skip to content

Commit 1fa0be5

Browse files
committed
Загрузка конфигурации битриксового сервис-локатора.
1 parent fb1a65e commit 1fa0be5

15 files changed

+665
-18
lines changed

readme.MD

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,27 @@ var_dump(container($micro)->getParameter('example'));
238238

239239
```php
240240
$kernel = container()->get('kernel');
241+
```
242+
243+
## Импорт в контейнер сервисов битриксового сервис-локатора
244+
245+
Автоматом подтягиваются в контейнер сервисы из битриксового сервис-локатора. [Формат](https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=43&LESSON_ID=14032),
246+
секция `services` из `/bitrix/.settings.php` и `/bitrix/.settings_extra.php`. Также загрузчик пробегает
247+
по списку установленных модулей и подцепляет их тоже.
248+
249+
Для отдельных сервис-контейнеров (отнаследованных от `AbstractStandaloneServiceProvider`) такая загрузка
250+
не производится.
251+
252+
Если эта фича не нужна, то нужно отнаследоваться от `ServiceProvider` и заглушить метод `loadBitrixServiceLocatorConfigs`.
253+
254+
```php
255+
class MyServiceProvider extends ServiceProvider
256+
{
257+
/**
258+
* {@inheritDoc}
259+
*/
260+
protected function loadBitrixServiceLocatorConfigs(DelegatingLoader $loader) : void
261+
{
262+
}
263+
}
241264
```

src/Bundles/BundlesLoader.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,14 @@ public static function getBundlesMap() : array
222222
{
223223
return static::$bundlesMap[static::class] ?? [];
224224
}
225+
226+
/**
227+
* Очистить инстанцы бандлов.
228+
*
229+
* @return void
230+
*/
231+
public static function clearBundlesMap() : void
232+
{
233+
static::$bundlesMap[static::class] = [];
234+
}
225235
}

src/Micro/AbstractStandaloneServiceProvider.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Exception;
66
use Prokl\ServiceProvider\Framework\SymfonyCompilerPassBagLight;
77
use Prokl\ServiceProvider\ServiceProvider;
8+
use Symfony\Component\Config\Loader\DelegatingLoader;
89
use Symfony\Component\DependencyInjection\ContainerBuilder;
910

1011
/**
@@ -37,4 +38,12 @@ public function __construct(
3738

3839
parent::__construct($filename, $pathBundlesConfig);
3940
}
41+
42+
/**
43+
* {@inheritDoc}
44+
* @internal Для отдельных контейнеров не нужно грузить сервисы из битриксового сервис-локатора.
45+
*/
46+
protected function loadBitrixServiceLocatorConfigs(DelegatingLoader $loader) : void
47+
{
48+
}
4049
}

src/ServiceProvider.php

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Prokl\ServiceProvider\Framework\SymfonyCompilerPassBag;
1212
use Prokl\ServiceProvider\Services\AppKernel;
1313
use Prokl\ServiceProvider\Utils\ErrorScreen;
14+
use Prokl\ServiceProvider\Utils\Loaders\PhpLoaderSettingsBitrix;
1415
use Psr\Container\ContainerInterface;
1516
use Psr\Container\ContainerInterface as PsrContainerInterface;
1617
use RuntimeException;
@@ -116,10 +117,20 @@ class ServiceProvider
116117
*/
117118
protected $cacheDir = '/bitrix/cache/';
118119

120+
/**
121+
* @var string $projectRoot DOCUMENT_ROOT.
122+
*/
123+
protected $projectRoot = '';
124+
125+
/**
126+
* @var array Конфигурация бандлов.
127+
*/
128+
protected $bundles = [];
129+
119130
/**
120131
* @var ErrorScreen $errorHandler Обработчик ошибок.
121132
*/
122-
private $errorHandler;
133+
protected $errorHandler;
123134

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

139-
/**
140-
* @var string $projectRoot DOCUMENT_ROOT.
141-
*/
142-
private $projectRoot = '';
143-
144-
/**
145-
* @var array Конфигурация бандлов.
146-
*/
147-
private $bundles = [];
148-
149150
/**
150151
* @var array $compilerPassesBag Набор Compiler Pass.
151152
*/
@@ -293,6 +294,9 @@ public function shutdown() : void
293294
$bundle->setContainer(null);
294295
}
295296

297+
$this->bundles = [];
298+
BundlesLoader::clearBundlesMap();
299+
296300
static::$containerBuilder = null;
297301
}
298302

@@ -821,6 +825,9 @@ private function loadContainerConfig(string $fileName, ContainerBuilder $contain
821825
try {
822826
$loader->load($this->projectRoot . '/' . $fileName);
823827
$loader->load(__DIR__ . '/../config/base.yaml');
828+
829+
$this->loadBitrixServiceLocatorConfigs($loader);
830+
824831
return true;
825832
} catch (Exception $e) {
826833
$this->errorHandler->die('Сервис-контейнер: ' . $e->getMessage());
@@ -829,6 +836,27 @@ private function loadContainerConfig(string $fileName, ContainerBuilder $contain
829836
}
830837
}
831838

839+
/**
840+
* Конфиги битриксового сервис-локатора.
841+
*
842+
* @param DelegatingLoader $loader Загрузчик.
843+
*
844+
* @return void
845+
* @throws Exception Когда что-то не так с файлами конфигураций.
846+
*
847+
* @since 13.07.2021
848+
*/
849+
protected function loadBitrixServiceLocatorConfigs(DelegatingLoader $loader) : void
850+
{
851+
// Если не найден '/bitrix/.settings.php', то у нас проблемы с Битриксом (не установлен)
852+
// Выбросит исключение.
853+
$loader->load($this->projectRoot . '/bitrix/.settings.php');
854+
855+
if ($this->filesystem->exists($this->projectRoot . '/bitrix/.settings_extra.php')) {
856+
$loader->load($this->projectRoot . '/bitrix/.settings_extra.php');
857+
}
858+
}
859+
832860
/**
833861
* Загрузка конфигураций в различных форматах из папки configs.
834862
*
@@ -846,9 +874,7 @@ private function configureContainer(ContainerBuilder $container, LoaderInterface
846874
$confDir = $this->projectRoot . $this->configDir;
847875

848876
if (!@file_exists($confDir)) {
849-
throw new RuntimeException(
850-
'Config directory ' . $confDir . ' not exist.'
851-
);
877+
throw new RuntimeException('Config directory ' . $confDir . ' not exist.');
852878
}
853879

854880
$container->setParameter('container.dumper.inline_class_loader', true);
@@ -882,6 +908,7 @@ private function getContainerLoader(ContainerBuilder $container): DelegatingLoad
882908
new XmlFileLoader($container, $locator),
883909
new YamlFileLoader($container, $locator),
884910
new IniFileLoader($container, $locator),
911+
new PhpLoaderSettingsBitrix($container, $locator),
885912
new PhpFileLoader($container, $locator),
886913
new GlobFileLoader($container, $locator),
887914
new DirectoryLoader($container, $locator),
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
namespace Prokl\ServiceProvider\Utils;
4+
5+
use Symfony\Component\DependencyInjection\ContainerInterface;
6+
use Symfony\Component\DependencyInjection\Definition;
7+
8+
/**
9+
* Class BitrixSettingsDiAdapter
10+
* @package Prokl\ServiceProvider\Utils
11+
*
12+
* @since 12.07.2021
13+
*/
14+
class BitrixSettingsDiAdapter
15+
{
16+
/**
17+
* Импортировать параметры из .settings.php
18+
*
19+
* @param ContainerInterface $container Контейнер.
20+
* @param array $settings Секция parameters .settings.php.
21+
* @param string|null $section Если задано, то параметры попадут в отдельную секцию контейнера.
22+
*
23+
* @return void
24+
*/
25+
public function importParameters(
26+
ContainerInterface $container,
27+
array $settings,
28+
?string $section = null
29+
) : void {
30+
if ($section !== null) {
31+
$container->setParameter($section, $settings);
32+
return;
33+
}
34+
35+
foreach ($settings as $id => $value) {
36+
$container->setParameter($id, $value);
37+
}
38+
}
39+
40+
/**
41+
* Импортировать сервисы из .settings.php.
42+
*
43+
* @param ContainerInterface $container Контейнер.
44+
* @param array $services Секция services .settings.php.
45+
*
46+
* @return void
47+
*/
48+
public function importServices(ContainerInterface $container, array $services) : void
49+
{
50+
foreach ($services as $id => $service) {
51+
if (array_key_exists('constructor', $service)
52+
&&
53+
is_callable($service['constructor'])
54+
) {
55+
/** @var Definition $definition */
56+
$definition = $container->register($id, FactoryClosure::class);
57+
$definition->setFactory([FactoryClosure::class, 'from']);
58+
$definition->addArgument($service['constructor']);
59+
$definition->setPublic(true);
60+
}
61+
62+
if (array_key_exists('className', $service) && is_string($service['className'])) {
63+
$definition = $container->register($id, $service['className'])->setPublic(true);
64+
65+
if (array_key_exists('constructorParams', $service) && is_callable($service['constructorParams'])) {
66+
$arguments = $service['constructorParams']();
67+
if (is_array($arguments)) {
68+
foreach ($arguments as $argument) {
69+
$definition->addArgument($argument);
70+
}
71+
} else {
72+
$definition->addArgument($service['constructorParams']());
73+
}
74+
}
75+
76+
if (array_key_exists('constructorParams', $service) && is_array($service['constructorParams'])) {
77+
foreach ($service['constructorParams'] as $param) {
78+
$definition->addArgument($param);
79+
}
80+
}
81+
}
82+
}
83+
}
84+
}

src/Utils/FactoryClosure.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Prokl\ServiceProvider\Utils;
4+
5+
use Closure;
6+
7+
/**
8+
* Class FactoryClosure
9+
* @package Prokl\FrameworkExtensionBundle\Services\Utils
10+
*
11+
* @since 13.07.2021
12+
*/
13+
class FactoryClosure
14+
{
15+
/**
16+
* @param Closure $closure Closure.
17+
*
18+
* @return mixed
19+
*/
20+
public function from(Closure $closure)
21+
{
22+
return $closure();
23+
}
24+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
namespace Prokl\ServiceProvider\Utils\Loaders;
4+
5+
use Bitrix\Main\Config\Configuration;
6+
use Bitrix\Main\ModuleManager;
7+
use Prokl\ServiceProvider\Utils\BitrixSettingsDiAdapter;
8+
use Symfony\Component\DependencyInjection\Loader\FileLoader;
9+
use Symfony\Component\DependencyInjection\Loader\ProtectedPhpFileLoader;
10+
11+
/**
12+
* Class PhpLoaderSettingsBitrix
13+
* Загрузчик битриксовых конфигурационных файлов
14+
* @package Prokl\ServiceProvider\Utils\Loaders
15+
*
16+
* @since 12.07.2021
17+
*/
18+
class PhpLoaderSettingsBitrix extends FileLoader
19+
{
20+
/**
21+
* {@inheritdoc}
22+
*/
23+
public function load($resource, string $type = null)
24+
{
25+
// the container and loader variables are exposed to the included file below
26+
$container = $this->container;
27+
28+
$path = $this->locator->locate($resource);
29+
30+
$this->setCurrentDir(\dirname($path));
31+
$this->container->fileExists($path);
32+
33+
// the closure forbids access to the private scope in the included file
34+
$load = \Closure::bind(function ($path, $env) {
35+
return $this->loadBitrixConfig('services', true);
36+
}, $this, ProtectedPhpFileLoader::class);
37+
38+
try {
39+
$callback = $load($path, $this->env);
40+
if (is_array($callback)) {
41+
$adapter = new BitrixSettingsDiAdapter();
42+
$adapter->importServices($container, $callback);
43+
}
44+
} finally {
45+
$this->instanceof = [];
46+
$this->registerAliasesForSinglyImplementedInterfaces();
47+
}
48+
}
49+
50+
/**
51+
* {@inheritdoc}
52+
*/
53+
public function supports($resource, string $type = null)
54+
{
55+
if (!\is_string($resource)
56+
// ToDo более строгая проверка на нужный файл.
57+
|| stripos($resource, '.settings') === false
58+
) {
59+
return false;
60+
}
61+
62+
if (null === $type && 'php' === pathinfo($resource, \PATHINFO_EXTENSION)) {
63+
return true;
64+
}
65+
66+
return 'php' === $type;
67+
}
68+
69+
/**
70+
* Загрузка битриксовых конфигов.
71+
*
72+
* @param string $key Ключ в параметрах битриксовых файлов.
73+
* @param boolean $loadModulesServices Загружать такую же секцию в установленных модулях.
74+
*
75+
* @return array
76+
*/
77+
public function loadBitrixConfig(string $key, bool $loadModulesServices = true) : array
78+
{
79+
$mainBitrixServices = Configuration::getInstance()->get($key) ?? [];
80+
81+
// Собрать конфиги всех установленных модулей.
82+
$servicesModules = [];
83+
84+
if ($loadModulesServices) {
85+
foreach (ModuleManager::getInstalledModules() as $module) {
86+
$services = Configuration::getInstance($module['ID'])->get($key) ?? [];
87+
if (count($services) > 0) {
88+
$servicesModules[] = $services;
89+
}
90+
}
91+
}
92+
93+
return array_merge($mainBitrixServices, ...$servicesModules);
94+
}
95+
}

0 commit comments

Comments
 (0)