Skip to content

[spiral/config] Refactored config files loading system. Now, it uses a new way to manage config directories and file loaders. #1218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

name: build

on:
Expand Down Expand Up @@ -47,7 +46,7 @@ jobs:
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Restore Composer Cache
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }}
Expand Down Expand Up @@ -99,7 +98,7 @@ jobs:
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Restore Composer Cache
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }}
Expand Down Expand Up @@ -165,7 +164,7 @@ jobs:
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Restore Composer Cache
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }}
Expand Down
87 changes: 53 additions & 34 deletions src/Boot/src/Bootloader/ConfigurationBootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,82 @@

use Psr\Container\ContainerInterface;
use Spiral\Boot\DirectoriesInterface;
use Spiral\Boot\EnvironmentInterface;
use Spiral\Config\ConfigManager;
use Spiral\Config\ConfiguratorInterface;
use Spiral\Config\Loader\DirectoryLoader;
use Spiral\Config\Loader\ConfigsMergerInterface;
use Spiral\Config\Loader\DirectoriesRepository;
use Spiral\Config\Loader\DirectoriesRepositoryInterface;
use Spiral\Config\Loader\FileLoaderInterface;
use Spiral\Config\Loader\FileLoaderRegistry;
use Spiral\Config\Loader\JsonLoader;
use Spiral\Config\Loader\MergeFileStrategyLoader;
use Spiral\Config\Loader\PhpLoader;
use Spiral\Core\BinderInterface;
use Spiral\Config\Loader\RecursiveConfigMerger;
use Spiral\Config\Loader\SingleFileStrategyLoader;
use Spiral\Config\LoaderInterface;
use Spiral\Core\ConfigsInterface;

/**
* Bootloads core services.
* Boot core service responsible for configuration loading and management.
*/
final class ConfigurationBootloader extends Bootloader
{
protected const SINGLETONS = [
// configuration
ConfigsInterface::class => ConfiguratorInterface::class,
ConfiguratorInterface::class => ConfigManager::class,
ConfigManager::class => [self::class, 'configManager'],
];
public function __construct(
private readonly ContainerInterface $container,
) {}

private readonly ConfiguratorInterface $configurator;
public function defineSingletons(): array
{
return [
ConfigsInterface::class => ConfiguratorInterface::class,
ConfiguratorInterface::class => ConfigManager::class,
FileLoaderRegistry::class => $this->createFileLoader(...),
ConfigManager::class => $this->createConfigManager(...),
ConfigsMergerInterface::class => RecursiveConfigMerger::class,
LoaderInterface::class => $this->createConfigLoader(...),
DirectoriesRepositoryInterface::class => $this->createDirectories(...),
];
}

/** @var FileLoaderInterface[] */
private array $loaders;
private function createConfigLoader(EnvironmentInterface $env): LoaderInterface
{
return match ($env->get('CONFIG_STRATEGY', 'single')) {
'merge' => $this->container->get(MergeFileStrategyLoader::class),
default => $this->container->get(SingleFileStrategyLoader::class),
};
}

public function __construct(
ContainerInterface $container,
private readonly DirectoriesInterface $directories,
private readonly BinderInterface $binder,
) {
$this->loaders = [
'php' => $container->get(PhpLoader::class),
'json' => $container->get(JsonLoader::class),
];
private function createDirectories(DirectoriesInterface $directories): DirectoriesRepositoryInterface
{
return new DirectoriesRepository([
$directories->get('config'),
]);
}

$this->configurator = $this->createConfigManager();
private function createFileLoader(PhpLoader $phpLoader, JsonLoader $jsonLoader): FileLoaderRegistry
{
return new FileLoaderRegistry([
'php' => $phpLoader,
'json' => $jsonLoader,
]);
}

public function addLoader(string $ext, FileLoaderInterface $loader): void
public function setDirectories(array $directories): void
{
if (!isset($this->loaders[$ext]) || $loader::class !== $this->loaders[$ext]::class) {
$this->loaders[$ext] = $loader;
$this->binder->bindSingleton(ConfigManager::class, $this->createConfigManager());
}
$this->container->get(DirectoriesRepositoryInterface::class)->setDirectories($directories);
}

private function createConfigManager(): ConfigManager
public function addLoader(string $ext, FileLoaderInterface $loader): void
{
return new ConfigManager(
new DirectoryLoader($this->directories->get('config'), $this->loaders),
true,
);
$this->container->get(FileLoaderRegistry::class)->register($ext, $loader);
}

private function configManager(): ConfiguratorInterface
private function createConfigManager(LoaderInterface $loader): ConfigManager
{
return $this->configurator;
return new ConfigManager(
loader: $loader,
strict: true,
);
}
}
52 changes: 47 additions & 5 deletions src/Boot/tests/ConfigsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,71 @@

namespace Spiral\Tests\Boot;

use Spiral\Boot\DirectoriesInterface;
use Spiral\Config\ConfiguratorInterface;
use Spiral\Config\Loader\DirectoriesRepositoryInterface;
use Spiral\Tests\Boot\Fixtures\FooConfig;
use Spiral\Tests\Boot\Fixtures\TestConfig;
use Spiral\Tests\Boot\Fixtures\TestCore;
use Traversable;

class ConfigsTest extends TestCase
{
public function testDirectories(): void
{
$core = TestCore::create([
'root' => __DIR__,
'root' => __DIR__,
'config' => __DIR__ . '/config',
])->run();

/** @var TestConfig $config */
$config = $core->getContainer()->get(TestConfig::class);
/** @var TestConfig $testConfig */
$testConfig = $core->getContainer()->get(TestConfig::class);

self::assertSame(['key' => 'value'], $config->toArray());
/** @var FooConfig $fooConfig */
$fooConfig = $core->getContainer()->get(FooConfig::class);

self::assertSame(['key' => 'value1'], $testConfig->toArray());
self::assertSame(['key' => 'value'], $fooConfig->toArray());
}

public function testCustomDirectoriesRepository(): void
{
$core = TestCore::create([
'root' => __DIR__,
'config' => __DIR__ . '/config',
])->run();

$core->getContainer()->bindSingleton(
DirectoriesRepositoryInterface::class,
static function (DirectoriesInterface $dirs): DirectoriesRepositoryInterface {
return new class($dirs->get('config')) implements DirectoriesRepositoryInterface {
public function __construct(
private string $rootDir,
) {}


public function getIterator(): Traversable
{
yield $this->rootDir;
}
};
},
);

/** @var TestConfig $testConfig */
$testConfig = $core->getContainer()->get(TestConfig::class);

/** @var FooConfig $fooConfig */
$fooConfig = $core->getContainer()->get(FooConfig::class);

self::assertSame(['key' => 'value'], $testConfig->toArray());
self::assertSame(['key' => 'value'], $fooConfig->toArray());
}

public function testCustomConfigLoader(): void
{
$core = TestCore::create([
'root' => __DIR__,
'root' => __DIR__,
'config' => __DIR__ . '/config',
])->run();

Expand Down
13 changes: 11 additions & 2 deletions src/Boot/tests/Fixtures/ConfigBootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Boot\Bootloader\ConfigurationBootloader;
use Spiral\Boot\Bootloader\CoreBootloader;
use Spiral\Boot\DirectoriesInterface;
use Spiral\Core\Container;

class ConfigBootloader extends Bootloader
Expand Down Expand Up @@ -41,8 +42,12 @@ public function init(Container $container, AbstractKernel $kernel): void
$container->bind('efg', 'foo');
}

public function boot(ConfigurationBootloader $configuration, AbstractKernel $kernel, Container $container): void
{
public function boot(
ConfigurationBootloader $configuration,
DirectoriesInterface $directories,
AbstractKernel $kernel,
Container $container,
): void {
// won't be executed
$kernel->booting(static function (AbstractKernel $kernel) use ($container): void {
$container->bind('ghi', 'foo');
Expand All @@ -53,5 +58,9 @@ public function boot(ConfigurationBootloader $configuration, AbstractKernel $ker
});

$configuration->addLoader('yaml', $container->get(TestLoader::class));
$configuration->setDirectories([
$directories->get('config') . '/prod',
$directories->get('config'),
]);
}
}
12 changes: 12 additions & 0 deletions src/Boot/tests/Fixtures/FooConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Spiral\Tests\Boot\Fixtures;

use Spiral\Core\InjectableConfig;

class FooConfig extends InjectableConfig
{
public const CONFIG = 'foo';
}
2 changes: 1 addition & 1 deletion src/Boot/tests/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function testSpiral(): void
$c = $core->getContainer();

ContainerScope::runScope($c, static function (): void {
self::assertSame(['key' => 'value'], spiral(TestConfig::class)->toArray());
self::assertSame(['key' => 'value1'], spiral(TestConfig::class)->toArray());
});
}

Expand Down
5 changes: 5 additions & 0 deletions src/Boot/tests/config/foo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

declare(strict_types=1);

return ['key' => 'value'];
5 changes: 5 additions & 0 deletions src/Boot/tests/config/prod/test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

declare(strict_types=1);

return ['key' => 'value1'];
1 change: 1 addition & 0 deletions src/Config/src/ConfigManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* Load config files, provides container injection and modifies config data on
* bootloading.
*
* @internal
* @implements ConfiguratorInterface<object>
*/
#[Singleton]
Expand Down
14 changes: 14 additions & 0 deletions src/Config/src/Loader/ConfigsMergerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Spiral\Config\Loader;

interface ConfigsMergerInterface
{
/**
* @param array ...$config
* @return array
*/
public function merge(array ...$config): array;
}
42 changes: 42 additions & 0 deletions src/Config/src/Loader/DirectoriesRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Spiral\Config\Loader;

use Traversable;

/**
* @internal
*/
final class DirectoriesRepository implements DirectoriesRepositoryInterface
{
/** @var string[] */
private array $directories;

/**
* @param string[] $directories
*/
public function __construct(array $directories)
{
$this->setDirectories($directories);
}

public function setDirectories(array $directories): void
{
$this->directories = \array_map(
static fn(string $dir): string => \rtrim($dir, '/'),
$directories,
);
}

public function addDirectory(string $directory): void
{
$this->directories[] = \rtrim($directory, '/');
}

public function getIterator(): Traversable
{
return new \ArrayIterator($this->directories);
}
}
10 changes: 10 additions & 0 deletions src/Config/src/Loader/DirectoriesRepositoryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Spiral\Config\Loader;

/**
* @extends \IteratorAggregate<array-key, string>
*/
interface DirectoriesRepositoryInterface extends \IteratorAggregate {}
4 changes: 4 additions & 0 deletions src/Config/src/Loader/DirectoryLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
use Spiral\Config\Exception\LoaderException;
use Spiral\Config\LoaderInterface;

/**
* @internal
* @deprecated Use {@see SingleFileStrategyLoader} instead. Will be removed in 4.0.
*/
final class DirectoryLoader implements LoaderInterface
{
private readonly string $directory;
Expand Down
Loading
Loading