state config

This commit is contained in:
Yuri Kuznetsov
2025-09-30 15:28:52 +03:00
parent 3c81bf93e3
commit 20474a9d3e
6 changed files with 285 additions and 28 deletions

1
.gitignore vendored
View File

@@ -7,6 +7,7 @@
/data/config-internal.php
/data/config-override.php
/data/config-internal-override.php
/data/state.php
/data/tmp/*
/build
/node_modules

View File

@@ -46,6 +46,7 @@ class Config
private string $internalConfigPath = 'data/config-internal.php';
private string $overrideConfigPath = 'data/config-override.php';
private string $internalOverrideConfigPath = 'data/config-internal-override.php';
private string $stateConfigPath = 'data/state.php';
private string $cacheTimestamp = 'cacheTimestamp';
/** @var string[] */
protected $associativeArrayAttributeList = [
@@ -81,12 +82,22 @@ class Config
* A path to the internal config file.
*
* @todo Move to ConfigData.
* @internal
*/
public function getInternalConfigPath(): string
{
return $this->internalConfigPath;
}
/**
* @todo Move to ConfigData.
* @internal
*/
public function getStateConfigPath(): string
{
return $this->stateConfigPath;
}
/**
* Get a parameter value.
*
@@ -302,13 +313,15 @@ class Config
$internalData = $this->readFile($this->internalConfigPath);
$overrideData = $this->readFile($this->overrideConfigPath);
$internalOverrideData = $this->readFile($this->internalOverrideConfigPath);
$stateConfigData = $this->readFile($this->stateConfigPath);
$this->data = $this->mergeData(
$systemData,
$data,
$internalData,
$overrideData,
$internalOverrideData
systemData: $systemData,
data: $data,
internalData: $internalData,
overrideData: $overrideData,
internalOverrideData: $internalOverrideData,
stateData: $stateConfigData,
);
$this->internalParamList = array_values(array_merge(
@@ -325,6 +338,7 @@ class Config
* @param array<string, mixed> $internalData
* @param array<string, mixed> $overrideData
* @param array<string, mixed> $internalOverrideData
* @param array<string, mixed> $stateData
* @return array<string, mixed>
*/
private function mergeData(
@@ -332,7 +346,8 @@ class Config
array $data,
array $internalData,
array $overrideData,
array $internalOverrideData
array $internalOverrideData,
array $stateData,
): array {
/** @var array<string, mixed> $mergedData */
@@ -344,8 +359,13 @@ class Config
/** @var array<string, mixed> $mergedData */
$mergedData = Util::merge($mergedData, $overrideData);
/** @var array<string, mixed> */
return Util::merge($mergedData, $internalOverrideData);
/** @var array<string, mixed> $mergedData */
$mergedData = Util::merge($mergedData, $internalOverrideData);
/** @var array<string, mixed> $mergedData */
$mergedData = Util::merge($mergedData, $stateData);
return $mergedData;
}
/**

View File

@@ -107,6 +107,7 @@ class ConfigWriter
$configPath = $this->config->getConfigPath();
$internalConfigPath = $this->config->getInternalConfigPath();
$stateConfigPath = $this->config->getStateConfigPath();
if (!$this->fileManager->isFile($configPath)) {
throw new RuntimeException("Config file '$configPath' not found.");
@@ -117,6 +118,9 @@ class ConfigWriter
$dataInternal = $this->fileManager->isFile($internalConfigPath) ?
$this->fileManager->getPhpContents($internalConfigPath) : [];
$dataState = $this->fileManager->isFile($stateConfigPath) ?
$this->fileManager->getPhpContents($stateConfigPath) : [];
if (!is_array($data)) {
throw new RuntimeException("Could not read config.");
}
@@ -125,12 +129,45 @@ class ConfigWriter
throw new RuntimeException("Could not read config-internal.");
}
if (!is_array($dataState)) {
throw new RuntimeException("Could not read state.");
}
$toSaveInternal = false;
$toSaveState = false;
$toSaveMain = false;
foreach (array_merge(array_keys($changedData), $this->removeParamList) as $key) {
if (array_key_exists($key, $data)) {
$toSaveMain = true;
}
if (array_key_exists($key, $dataInternal)) {
$toSaveInternal = true;
}
if (array_key_exists($key, $dataState)) {
$toSaveState = true;
}
}
foreach ($changedData as $key => $value) {
if ($this->internalConfigHelper->isParamForStateConfig($key)) {
$dataState[$key] = $value;
unset($data[$key]);
unset($dataInternal[$key]);
$toSaveState = true;
continue;
}
if ($this->internalConfigHelper->isParamForInternalConfig($key)) {
$dataInternal[$key] = $value;
unset($data[$key]);
unset($dataState[$key]);
$toSaveInternal = true;
@@ -138,25 +175,38 @@ class ConfigWriter
}
$data[$key] = $value;
unset($dataState[$key]);
unset($dataInternal[$key]);
$toSaveMain = true;
}
foreach ($this->removeParamList as $key) {
if ($this->internalConfigHelper->isParamForInternalConfig($key)) {
unset($dataInternal[$key]);
$toSaveInternal = true;
continue;
if (array_key_exists($key, $data)) {
unset($data[$key]);
}
unset($data[$key]);
if (array_key_exists($key, $dataInternal)) {
unset($dataInternal[$key]);
}
if (array_key_exists($key, $dataState)) {
unset($dataState[$key]);
}
}
if ($toSaveInternal) {
$this->saveData($internalConfigPath, $dataInternal, 'microtimeInternal');
}
$this->saveData($configPath, $data, 'microtime');
if ($toSaveMain) {
$this->saveData($configPath, $data, 'microtime');
}
if ($toSaveState) {
$this->saveData($stateConfigPath, $dataState, 'microtimeState');
}
$this->changedData = [];
$this->removeParamList = [];

View File

@@ -34,9 +34,23 @@ use Espo\Core\Utils\Metadata;
class InternalConfigHelper
{
/** @var string[] */
private array $stateParamList = [
'appTimestamp',
'cacheTimestamp',
'version',
'latestVersion',
'latestExtensionVersions',
];
public function __construct(private Config $config, private Metadata $metadata)
{}
public function isParamForStateConfig(string $name): bool
{
return in_array($name, $this->stateParamList);
}
public function isParamForInternalConfig(string $name): bool
{
if ($this->config->isInternal($name)) {

View File

@@ -46,6 +46,7 @@ class ConfigWriterTest extends TestCase
private $configPath;
private $internalConfigPath;
private $stateConfigPath;
protected function setUp(): void
{
@@ -61,8 +62,9 @@ class ConfigWriterTest extends TestCase
$this->internalConfigHelper
);
$this->configPath = 'somepath';
$this->internalConfigPath = 'internalSomepath';
$this->configPath = 'somePath';
$this->internalConfigPath = 'internalSomePath';
$this->stateConfigPath = 'stateSomePath';
$this->config
->expects($this->any())
@@ -73,6 +75,11 @@ class ConfigWriterTest extends TestCase
->expects($this->any())
->method('getInternalConfigPath')
->willReturn($this->internalConfigPath);
$this->config
->expects($this->any())
->method('getStateConfigPath')
->willReturn($this->stateConfigPath);
}
public function testSave1(): void
@@ -121,6 +128,7 @@ class ConfigWriterTest extends TestCase
->willReturnMap([
[$this->configPath, true],
[$this->internalConfigPath, false],
[$this->stateConfigPath, false],
]);
$this->fileManager
@@ -145,6 +153,7 @@ class ConfigWriterTest extends TestCase
$this->configWriter->set('k2', 'v2');
$this->internalConfigHelper
->expects($this->any())
->method('isParamForInternalConfig')
->willReturnMap(
[
@@ -154,8 +163,19 @@ class ConfigWriterTest extends TestCase
]
);
$this->internalConfigHelper
->expects($this->any())
->method('isParamForStateConfig')
->willReturnMap(
[
['k1', false],
['k2', false],
['cacheTimestamp', true],
]
);
$this->helper
->expects($this->exactly(2))
->expects($this->exactly(3))
->method('generateMicrotime')
->willReturn(1.0);
@@ -170,16 +190,16 @@ class ConfigWriterTest extends TestCase
->willReturnMap([
[$this->configPath, true],
[$this->internalConfigPath, true],
[$this->stateConfigPath, true],
]);
$this->fileManager
->expects($this->exactly(4))
->expects($this->exactly(6))
->method('getPhpContents')
->willReturnMap([
[$this->configPath, []],
[$this->internalConfigPath, []],
[$this->internalConfigPath, []],
[$this->configPath, []],
[$this->stateConfigPath, []],
]);
$this->fileManager
@@ -192,7 +212,156 @@ class ConfigWriterTest extends TestCase
],
[
$this->configPath,
['k1' => 'v1', 'cacheTimestamp' => 1, 'microtime' => 1.0]
['k1' => 'v1', 'microtime' => 1.0]
],
[
$this->stateConfigPath,
['cacheTimestamp' => 1, 'microtimeState' => 1.0]
],
]);
$this->configWriter->save();
}
public function testSave3(): void
{
$this->configWriter->set('k1', 'v1');
$this->configWriter->set('k2', 'v2');
$this->internalConfigHelper
->expects($this->any())
->method('isParamForInternalConfig')
->willReturnMap(
[
['k1', false],
['k2', true],
['cacheTimestamp', false],
]
);
$this->internalConfigHelper
->expects($this->any())
->method('isParamForStateConfig')
->willReturnMap(
[
['k1', false],
['k2', false],
['cacheTimestamp', true],
]
);
$this->helper
->expects($this->exactly(3))
->method('generateMicrotime')
->willReturn(1.0);
$this->helper
->expects($this->once())
->method('generateCacheTimestamp')
->willReturn(1);
$this->fileManager
->expects(self::any())
->method('isFile')
->willReturnMap([
[$this->configPath, true],
[$this->internalConfigPath, true],
[$this->stateConfigPath, true],
]);
$this->fileManager
->expects($this->exactly(6))
->method('getPhpContents')
->willReturnMap([
[$this->configPath, []],
[$this->internalConfigPath, []],
[$this->stateConfigPath, []],
]);
$this->fileManager
->expects(self::any())
->method('putPhpContents')
->willReturnMap([
[
$this->internalConfigPath,
['k2' => 'v2', 'microtimeInternal' => 1.0]
],
[
$this->configPath,
['k1' => 'v1', 'microtime' => 1.0]
],
[
$this->stateConfigPath,
['cacheTimestamp' => 1, 'microtimeState' => 1.0]
],
]);
$this->configWriter->save();
}
public function testsRemove1(): void
{
$this->configWriter->remove('k1');
$this->internalConfigHelper
->expects($this->any())
->method('isParamForInternalConfig')
->willReturnMap(
[
['k1', false],
['cacheTimestamp', false],
]
);
$this->internalConfigHelper
->expects($this->any())
->method('isParamForStateConfig')
->willReturnMap(
[
['k1', false],
['cacheTimestamp', false],
]
);
$this->helper
->expects($this->once())
->method('generateCacheTimestamp')
->willReturn(1);
$this->fileManager
->expects(self::any())
->method('isFile')
->willReturnMap([
[$this->configPath, true],
[$this->internalConfigPath, true],
[$this->stateConfigPath, true],
]);
$this->fileManager
->expects(self::any())
->method('isFile')
->willReturnMap([
[$this->configPath, true],
[$this->internalConfigPath, true],
[$this->stateConfigPath, true],
]);
$this->fileManager
->expects($this->exactly(4))
->method('getPhpContents')
->willReturnMap([
[$this->configPath, ['k1' => 'v1', 'k2' => 'v2']],
[$this->internalConfigPath, []],
[$this->stateConfigPath, []],
]);
$this->fileManager
->expects($this->exactly(1))
->method('putPhpContents')
->willReturnMap([
[
$this->configPath,
['k2' => 'v2', 'microtime' => 1.0]
],
]);

View File

@@ -43,6 +43,7 @@ class ConfigTest extends TestCase
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';
private $stateConfigPath = 'tests/unit/testData/cache/state.php';
private $reflection;
private $fileManager;
@@ -63,11 +64,7 @@ class ConfigTest extends TestCase
$this->reflection->setProperty('configPath', $this->configPath);
$this->reflection->setProperty('systemConfigPath', $this->systemConfigPath);
$this->reflection->setProperty('internalConfigPath', $this->internalConfigPath);
}
protected function tearDown(): void
{
$this->config = NULL;
$this->reflection->setProperty('stateConfigPath', $this->stateConfigPath);
}
public function testLoadConfig()
@@ -123,6 +120,7 @@ class ConfigTest extends TestCase
['data/config-internal.php', true],
['application/Espo/Resources/defaults/systemConfig.php', true],
['data/config-override.php', false],
['data/state.php', true],
['data/config-internal-override.php', false],
]
);
@@ -145,6 +143,7 @@ class ConfigTest extends TestCase
[
['data/config.php', $data],
['data/config-internal.php', $dataInternal],
['data/state.php', []],
['application/Espo/Resources/defaults/systemConfig.php', $dataSystem],
]
);
@@ -181,6 +180,7 @@ class ConfigTest extends TestCase
['data/config-internal.php', true],
['application/Espo/Resources/defaults/systemConfig.php', true],
['data/config-override.php', false],
['data/state.php', true],
['data/config-internal-override.php', false],
]
);
@@ -208,6 +208,7 @@ class ConfigTest extends TestCase
[
['data/config.php', $data],
['data/config-internal.php', $dataInternal],
['data/state.php', []],
['application/Espo/Resources/defaults/systemConfig.php', $dataSystem],
]
);
@@ -239,6 +240,7 @@ class ConfigTest extends TestCase
['data/config.php', true],
['data/config-internal.php', true],
['data/config-override.php', true],
['data/state.php', true],
['data/config-internal-override.php', true],
]
);
@@ -273,6 +275,7 @@ class ConfigTest extends TestCase
['data/config.php', $data],
['data/config-internal.php', $dataInternal],
['data/config-override.php', $dataOverride],
['data/state.php', []],
['data/config-internal-override.php', $dataInternalOverride],
]
);