From 525397e64d1ddcf65cd8ae58486931f74b96214c Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Wed, 24 Jan 2024 11:36:32 +0200 Subject: [PATCH] config override --- .gitignore | 2 + application/Espo/Core/Utils/Config.php | 78 ++++++++++--- .../Espo/Core/Utils/Config/ConfigWriter.php | 4 +- tests/unit/Espo/Core/Utils/ConfigTest.php | 106 ++++++++++++++++-- 4 files changed, 161 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 95bb9e14b2..88a4b1b811 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ /data/.backup/* /data/config.php /data/config-internal.php +/data/config-override.php +/data/config-internal-override.php /data/tmp/* /build /node_modules diff --git a/application/Espo/Core/Utils/Config.php b/application/Espo/Core/Utils/Config.php index bc6f4ea464..43b842a923 100644 --- a/application/Espo/Core/Utils/Config.php +++ b/application/Espo/Core/Utils/Config.php @@ -41,9 +41,11 @@ use const E_USER_DEPRECATED; */ class Config { + private string $systemConfigPath = 'application/Espo/Resources/defaults/systemConfig.php'; private string $configPath = 'data/config.php'; private string $internalConfigPath = 'data/config-internal.php'; - private string $systemConfigPath = 'application/Espo/Resources/defaults/systemConfig.php'; + private string $overrideConfigPath = 'data/config-override.php'; + private string $internalOverrideConfigPath = 'data/config-internal-override.php'; private string $cacheTimestamp = 'cacheTimestamp'; /** @var string[] */ protected $associativeArrayAttributeList = [ @@ -52,6 +54,7 @@ class Config 'logger', 'defaultPermissions', ]; + /** @var ?array */ private $data = null; /** @var array */ @@ -227,6 +230,7 @@ class Config $values = $this->changedData; if (!isset($values[$this->cacheTimestamp])) { + /** @noinspection PhpDeprecationInspection */ $values = array_merge($this->updateCacheTimestamp(true) ?? [], $values); } @@ -235,7 +239,7 @@ class Config $configPath = $this->getConfigPath(); if (!$this->fileManager->isFile($configPath)) { - throw new RuntimeException("Config file '{$configPath}' is not found."); + throw new RuntimeException("Config file '$configPath' is not found."); } $data = include($configPath); @@ -274,7 +278,7 @@ class Config private function isLoaded(): bool { - return isset($this->data) && !empty($this->data); + return !empty($this->data); } /** @@ -294,26 +298,65 @@ class Config private function load(): void { $systemData = $this->fileManager->getPhpContents($this->systemConfigPath); + $data = $this->readFile($this->configPath); + $internalData = $this->readFile($this->internalConfigPath); + $overrideData = $this->readFile($this->overrideConfigPath); + $internalOverrideData = $this->readFile($this->internalOverrideConfigPath); - $data = $this->fileManager->isFile($this->configPath) ? - $this->fileManager->getPhpContents($this->configPath) : []; - - $internalData = $this->fileManager->isFile($this->internalConfigPath) ? - $this->fileManager->getPhpContents($this->internalConfigPath) : []; - - /** @var array $mergedData */ - $mergedData = Util::merge( - Util::merge($systemData, $data), - $internalData + $this->data = $this->mergeData( + $systemData, + $data, + $internalData, + $overrideData, + $internalOverrideData ); - $this->data = $mergedData; - - $this->internalParamList = array_keys($internalData); + $this->internalParamList = array_values(array_merge( + array_keys($internalData), + array_keys($internalOverrideData) + )); $this->fileManager->setConfig($this); } + /** + * @param array $systemData + * @param array $data + * @param array $internalData + * @param array $overrideData + * @param array $internalOverrideData + * @return array + */ + private function mergeData( + array $systemData, + array $data, + array $internalData, + array $overrideData, + array $internalOverrideData + ): array { + + /** @var array $mergedData */ + $mergedData = Util::merge($systemData, $data); + + /** @var array $mergedData */ + $mergedData = Util::merge($mergedData, $internalData); + + /** @var array $mergedData */ + $mergedData = Util::merge($mergedData, $overrideData); + + /** @var array */ + return Util::merge($mergedData, $internalOverrideData); + } + + /** + * @return array + */ + private function readFile(string $path): array + { + return $this->fileManager->isFile($path) ? + $this->fileManager->getPhpContents($path) : []; + } + /** * Get all parameters excluding those that are set in the internal config. */ @@ -348,9 +391,11 @@ class Config public function setData($data) { if (is_object($data)) { + /** @noinspection PhpParamsInspection */ $data = get_object_vars($data); } + /** @noinspection PhpDeprecationInspection */ $this->set($data); } @@ -368,6 +413,7 @@ class Config return $timestamp; } + /** @noinspection PhpDeprecationInspection */ $this->set($timestamp); return null; diff --git a/application/Espo/Core/Utils/Config/ConfigWriter.php b/application/Espo/Core/Utils/Config/ConfigWriter.php index d9cde49b44..2d0f750f2b 100644 --- a/application/Espo/Core/Utils/Config/ConfigWriter.php +++ b/application/Espo/Core/Utils/Config/ConfigWriter.php @@ -174,7 +174,7 @@ class ConfigWriter try { $this->fileManager->putPhpContents($path, $data); } - catch (Exception $e) { + catch (Exception) { throw new RuntimeException("Could not save config."); } @@ -190,7 +190,7 @@ class ConfigWriter try { $this->fileManager->putPhpContentsNoRenaming($path, $data); } - catch (Exception $e) { + catch (Exception) { throw new RuntimeException("Could not save config."); } } diff --git a/tests/unit/Espo/Core/Utils/ConfigTest.php b/tests/unit/Espo/Core/Utils/ConfigTest.php index 0ca33674f9..6f28df30ca 100644 --- a/tests/unit/Espo/Core/Utils/ConfigTest.php +++ b/tests/unit/Espo/Core/Utils/ConfigTest.php @@ -29,21 +29,19 @@ namespace tests\unit\Espo\Core\Utils; +use PHPUnit\Framework\TestCase; use tests\unit\ReflectionHelper; use Espo\Core\Utils\Config; use Espo\Core\Utils\Config\ConfigFileManager; -class ConfigTest extends \PHPUnit\Framework\TestCase +class ConfigTest extends TestCase { private ?Config $config = null; private $defaultTestConfig = 'tests/unit/testData/Utils/Config/config.php'; - private $configPath = 'tests/unit/testData/cache/config.php'; - private $systemConfigPath = 'tests/unit/testData/Utils/Config/systemConfig.php'; - private $internalConfigPath = 'tests/unit/testData/cache/config-internal.php'; protected function setUp(): void @@ -64,7 +62,7 @@ class ConfigTest extends \PHPUnit\Framework\TestCase $this->reflection->setProperty('internalConfigPath', $this->internalConfigPath); } - protected function tearDown() : void + protected function tearDown(): void { $this->config = NULL; } @@ -116,7 +114,15 @@ class ConfigTest extends \PHPUnit\Framework\TestCase $fileManager ->method('isFile') - ->willReturn(true); + ->will( + $this->returnValueMap([ + ['data/config.php', true], + ['data/config-internal.php', true], + ['application/Espo/Resources/defaults/systemConfig.php', true], + ['data/config-override.php', false], + ['data/config-internal-override.php', false], + ]) + ); $data = [ 'test1' => '1', @@ -147,9 +153,9 @@ class ConfigTest extends \PHPUnit\Framework\TestCase $this->assertEquals('2', $config->get('test2')); $this->assertEquals('3', $config->get('test3')); - $this->assertEquals(false, $config->isInternal('test1')); - $this->assertEquals(true, $config->isInternal('test2')); - $this->assertEquals(false, $config->isInternal('test3')); + $this->assertFalse($config->isInternal('test1')); + $this->assertTrue($config->isInternal('test2')); + $this->assertFalse($config->isInternal('test3')); $this->assertEquals( (object) [ @@ -166,7 +172,15 @@ class ConfigTest extends \PHPUnit\Framework\TestCase $fileManager ->method('isFile') - ->willReturn(true); + ->will( + $this->returnValueMap([ + ['data/config.php', true], + ['data/config-internal.php', true], + ['application/Espo/Resources/defaults/systemConfig.php', true], + ['data/config-override.php', false], + ['data/config-internal-override.php', false], + ]) + ); $data = [ 'a' => [ @@ -195,7 +209,6 @@ class ConfigTest extends \PHPUnit\Framework\TestCase ]) ); - $config = new Config($fileManager); $this->assertEquals(['1' => 'a1'], $config->get('a')); @@ -210,4 +223,75 @@ class ConfigTest extends \PHPUnit\Framework\TestCase $this->assertTrue($config->has('a')); $this->assertFalse($config->has('0')); } + + public function testGetWithOverride(): void + { + $fileManager = $this->createMock(ConfigFileManager::class); + + $fileManager + ->method('isFile') + ->will( + $this->returnValueMap([ + ['application/Espo/Resources/defaults/systemConfig.php', true], + ['data/config.php', true], + ['data/config-internal.php', true], + ['data/config-override.php', true], + ['data/config-internal-override.php', true], + ]) + ); + + $dataSystem = []; + + $data = [ + 'a' => 'a', + 'c' => 'c', + 'b' => 'b0', + ]; + + $dataInternal = [ + 'b' => 'b0', + 'e' => 'e0', + ]; + + $dataOverride = [ + 'c' => 'c1', + ]; + + $dataInternalOverride = [ + 'e' => 'e1', + 'h' => 'h1', + ]; + + $fileManager + ->method('getPhpContents') + ->will( + $this->returnValueMap([ + ['application/Espo/Resources/defaults/systemConfig.php', $dataSystem], + ['data/config.php', $data], + ['data/config-internal.php', $dataInternal], + ['data/config-override.php', $dataOverride], + ['data/config-internal-override.php', $dataInternalOverride], + ]) + ); + + $config = new Config($fileManager); + + $this->assertEquals('a', $config->get('a')); + + $this->assertEquals('c1', $config->get('c')); + $this->assertEquals('e1', $config->get('e')); + $this->assertEquals('h1', $config->get('h')); + + $this->assertFalse($config->isInternal('c')); + $this->assertTrue($config->isInternal('e')); + $this->assertTrue($config->isInternal('h')); + $this->assertTrue($config->isInternal('b')); + + $nonInternalData = $config->getAllNonInternalData(); + + $this->assertTrue(isset($nonInternalData->a)); + $this->assertTrue(isset($nonInternalData->c)); + $this->assertFalse(isset($nonInternalData->e)); + $this->assertFalse(isset($nonInternalData->h)); + } }