Skip to content

Commit

Permalink
Fix config merger (#163)
Browse files Browse the repository at this point in the history
Co-authored-by: Alexander Makarov <sam@rmcreative.ru>
  • Loading branch information
vjik and samdark authored Dec 25, 2023
1 parent 8ef9884 commit f6c3d85
Show file tree
Hide file tree
Showing 25 changed files with 342 additions and 43 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

- New #155: Add ability to specify recursion depth for recursive modifier (@vjik)
- Enh #157: Remove unnecessary code in `PackagesListBuilder` (@vjik)
- Bug #153: Do not throw "Duplicate key…" exception when using nested groups (@vjik)
- Bug #163: References to another configs use reverse and recursive modifiers of root group now (@vjik)

## 1.4.0 November 17, 2023

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ If you want to recursively merge arrays to a certain depth, use the `RecursiveMe
RecursiveMerge::groups(['widgets-themes', 'my-custom-group'], 1)
```

> Note: References to another configs use recursive modifier of root group.
### Reverse merge of arrays

Result of reverse merge is being ordered descending by data source. It is useful for merging module config with
Expand All @@ -494,6 +496,8 @@ $config = new Config(
$events = $config->get('events-console'); // merged reversed
```

> Note: References to another configs use reverse modifier of root group.
### Remove elements from vendor package configuration

Sometimes it is necessary to remove some elements of vendor packages configuration. To do this,
Expand Down
43 changes: 15 additions & 28 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ public function get(string $group): array
}

$this->runBuildParams();
$this->runBuildGroup($group);

$this->merger->reset();
$this->build[$group] = $this->buildGroup($group);

return $this->build[$group];
}
Expand All @@ -85,51 +87,36 @@ public function has(string $group): bool
*/
private function runBuildParams(): void
{
if ($this->paramsGroup !== null) {
if ($this->paramsGroup !== null && !isset($this->build[$this->paramsGroup])) {
$this->isBuildingParams = true;
$this->runBuildGroup($this->paramsGroup);
$this->build[$this->paramsGroup] = $this->buildGroup($this->paramsGroup);
$this->isBuildingParams = false;
}
}

/**
* @throws ErrorException If an error occurred during the build.
*/
private function runBuildGroup(string $group): void
{
$this->merger->reset();
$this->buildGroup($group);
}

/**
* Builds the configuration of the group.
*
* @param string $group The group name.
*
* @throws ErrorException If an error occurred during the build.
*/
private function buildGroup(string $group): void
private function buildGroup(string $group, array $result = [], ?string $originalGroup = null): array
{
if (isset($this->build[$group])) {
return;
}

$this->build[$group] = [];

foreach ($this->filesExtractor->extract($group) as $file => $context) {
if (Options::isVariable($file)) {
if ($context->isVariable()) {
$variable = $this->prepareVariable($file, $group);
$array = $this->get($variable);
$result = $this->buildGroup($variable, $result, $originalGroup ?? $group);
} else {
$array = $this->buildFile($file);
$result = $this->merger->merge(
$context->setOriginalGroup($originalGroup ?? $group),
$result,
$this->buildFile($file),
);
}

$this->build[$group] = $this->merger->merge(
$context,
$this->build[$group],
$array,
);
}

return $result;
}

/**
Expand Down
13 changes: 13 additions & 0 deletions src/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ final class Context
public const APPLICATION = 3;
public const ENVIRONMENT = 4;

private string $originalGroup = '';

public function __construct(
private string $group,
private string $package,
Expand All @@ -23,6 +25,17 @@ public function __construct(
) {
}

public function setOriginalGroup(string $group): self
{
$this->originalGroup = $group;
return $this;
}

public function originalGroup(): string
{
return $this->originalGroup;
}

public function group(): string
{
return $this->group;
Expand Down
13 changes: 4 additions & 9 deletions src/Merger.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ public function reset(): void
*/
public function merge(Context $context, array $arrayA, array $arrayB): array
{
$recursionDepth = $this->dataModifiers->getRecursionDepth($context->group());
$isReverseMerge = $this->dataModifiers->isReverseMergeGroup($context->group());
$recursionDepth = $this->dataModifiers->getRecursionDepth($context->originalGroup());
$isReverseMerge = $this->dataModifiers->isReverseMergeGroup($context->originalGroup());

if ($isReverseMerge) {
$arrayB = $this->prepareArrayForReverse($context, [], $arrayB, $recursionDepth !== false);
Expand Down Expand Up @@ -143,7 +143,7 @@ private function performMerge(
);

if ($file !== null) {
$this->throwDuplicateKeyErrorException($context->group(), $fullKeyPath, [$file, $context->file()]);
$this->throwDuplicateKeyErrorException($context->originalGroup(), $fullKeyPath, [$file, $context->file()]);
}
}

Expand Down Expand Up @@ -197,11 +197,6 @@ private function prepareArrayForReverse(
continue;
}

if ($context->isVariable()) {
$result[$key] = $value;
continue;
}

$recursiveKeyPath[] = $key;

/** @var string|null $file */
Expand All @@ -211,7 +206,7 @@ private function prepareArrayForReverse(
);

if ($file !== null) {
$this->throwDuplicateKeyErrorException($context->group(), $recursiveKeyPath, [$file, $context->file()]);
$this->throwDuplicateKeyErrorException($context->originalGroup(), $recursiveKeyPath, [$file, $context->file()]);
}

$result[$key] = $value;
Expand Down
12 changes: 6 additions & 6 deletions tests/Integration/Dummy/DummyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -369,16 +369,16 @@ public function testConfigWithReverseMerge(): void
'b-web-environment-override-key' => 'c-web-override-value',
'c-web-key' => 'c-web-value',
'c-web-environment-override-key' => 'c-web-override-value',
'root-common-nested-key-2' => 'root-common-nested-value-2',
'root-common-nested-key-1' => 'root-common-nested-value-1',
'a-common-key' => 'a-common-value',
'a-common-root-override-key' => 'common-root-override-value',
'b-common-key' => 'b-common-value',
'b-common-root-override-key' => 'common-root-override-value',
'c-common-key' => 'c-common-value',
'c-common-root-override-key' => 'common-root-override-value',
'root-common-key-2' => 'root-common-value-2',
'root-common-key-1' => 'root-common-value-1',
'c-common-key' => 'c-common-value',
'b-common-key' => 'b-common-value',
'a-common-key' => 'a-common-value',
'root-common-key-2' => 'root-common-value-2',
'root-common-nested-key-1' => 'root-common-nested-value-1',
'root-common-nested-key-2' => 'root-common-nested-value-2',
'root-web-key' => 'root-web-value',
],
$config->get('web')
Expand Down
29 changes: 29 additions & 0 deletions tests/Integration/NestedGroupBug153/NestedGroupBug153Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Config\Tests\Integration\NestedGroupBug153;

use Yiisoft\Config\Tests\Integration\IntegrationTestCase;

final class NestedGroupBug153Test extends IntegrationTestCase
{
public function testBase(): void
{
$config = $this->runComposerUpdateAndCreateConfig(
rootPath: __DIR__,
packages: [
'test/a' => __DIR__ . '/packages/a',
],
extra: [
'config-plugin' => [
'params' => [],
'di' => 'di.php',
'di-web' => '$di',
],
],
);

$this->assertSame(['key' => 'app-di'], $config->get('di-web'));
}
}
7 changes: 7 additions & 0 deletions tests/Integration/NestedGroupBug153/di.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

declare(strict_types=1);

return [
'key' => 'app-di',
];
9 changes: 9 additions & 0 deletions tests/Integration/NestedGroupBug153/packages/a/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "test/a",
"version": "1.0.0",
"extra": {
"config-plugin": {
"di-web": "di-web.php"
}
}
}
7 changes: 7 additions & 0 deletions tests/Integration/NestedGroupBug153/packages/a/di-web.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

declare(strict_types=1);

return [
'key' => 'package-di-web',
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Config\Tests\Integration\NestedGroupDuplicateKey;

use ErrorException;
use Yiisoft\Config\Tests\Integration\IntegrationTestCase;

final class NestedGroupDuplicateKeyTest extends IntegrationTestCase
{
public function testBase(): void
{
$config = $this->runComposerUpdateAndCreateConfig(
rootPath: __DIR__,
packages: [
'test/a' => __DIR__ . '/packages/a',
],
extra: [
'config-plugin' => [
'params' => [],
'di' => 'di.php',
'di-web' => [
'$di',
'di-web.php',
],
],
],
);

$this->expectException(ErrorException::class);
$this->expectExceptionMessage(
'Duplicate key "key" in the following configs while building "di-web" group:' . "\n" .
' - di-web.php' . "\n" .
' - di.php'
);
$config->get('di-web');
}
}
7 changes: 7 additions & 0 deletions tests/Integration/NestedGroupDuplicateKey/di-web.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

declare(strict_types=1);

return [
'key' => 'app-di-web',
];
7 changes: 7 additions & 0 deletions tests/Integration/NestedGroupDuplicateKey/di.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

declare(strict_types=1);

return [
'key' => 'app-di',
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "test/a",
"version": "1.0.0",
"extra": {
"config-plugin": {
"di": "di.php"
}
}
}
7 changes: 7 additions & 0 deletions tests/Integration/NestedGroupDuplicateKey/packages/a/di.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

declare(strict_types=1);

return [
'key' => 'package-di',
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Integration\RemoveFromVendorExtended;

use Yiisoft\Config\Modifier\RecursiveMerge;
use Yiisoft\Config\Modifier\RemoveFromVendor;
use Yiisoft\Config\Tests\Integration\IntegrationTestCase;

final class RemoveFromVendorExtendedTest extends IntegrationTestCase
{
public function testBase(): void
{
$config = $this->runComposerUpdateAndCreateConfig(
rootPath: __DIR__,
packages: [
'yiisoft/auth-jwt' => __DIR__ . '/packages/yiisoft-auth-jwt',
],
extra: [
'config-plugin' => [
'params' => 'params.php',
'params-web' => [
'$params',
'params-web.php',
],
],
'config-plugin-environments' => [
'dev' => [
'params' => 'params-dev.php',
],
],
],
environment: 'dev',
paramsGroup: 'params-web',
modifiers: [
RecursiveMerge::groups('params', 'params-web'),
RemoveFromVendor::keys(['yiisoft/auth-jwt', 'algorithms'])->package('yiisoft/auth-jwt', 'params'),
],
);

$this->assertSame(
[
'yiisoft/auth-jwt' => [
'algorithms' => [
'RS384',
'HS512',
],
],
'debug' => true,
'test' => 7,
],
$config->get('params-web'),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "yiisoft/auth-jwt",
"version": "1.0.0",
"extra": {
"config-plugin": {
"params": "params.php"
}
}
}
Loading

0 comments on commit f6c3d85

Please sign in to comment.