Implement caching for config file parsing to improve performance

This commit is contained in:
crazywhalecc
2026-04-07 17:10:33 +08:00
parent 8e91e02806
commit baa21d6e94
5 changed files with 111 additions and 14 deletions

3
.gitignore vendored
View File

@@ -67,3 +67,6 @@ spc.exe
# dumped files from StaticPHP v3
/dump-*.json
# config parse cache
/.spc.cache.php

View File

@@ -42,13 +42,22 @@ class ArtifactConfig
if ($content === false) {
throw new WrongUsageException("Failed to read artifact config file: {$file}");
}
$data = match (pathinfo($file, PATHINFO_EXTENSION)) {
'json' => json_decode($content, true),
'yml', 'yaml' => Yaml::parse($content),
default => throw new WrongUsageException("Unsupported artifact config file format: {$file}"),
};
if (!is_array($data)) {
throw new WrongUsageException("Invalid JSON format in artifact config file: {$file}");
// use cache to skip redundant parsing
$data = ConfigCache::get($content);
if ($data !== null) {
logger()->debug("Config cache hit: {$file}");
} else {
$data = match (pathinfo($file, PATHINFO_EXTENSION)) {
'json' => json_decode($content, true),
'yml', 'yaml' => extension_loaded('yaml') ? yaml_parse($content) : Yaml::parse($content),
default => throw new WrongUsageException("Unsupported artifact config file format: {$file}"),
};
if (!is_array($data)) {
throw new WrongUsageException("Invalid JSON format in artifact config file: {$file}");
}
if (is_array($data)) {
ConfigCache::set($content, $data);
}
}
ConfigValidator::validateAndLintArtifacts(basename($file), $data);
foreach ($data as $artifact_name => $config) {
@@ -68,6 +77,16 @@ class ArtifactConfig
return self::$artifact_configs;
}
/**
* Restore artifact configs from cache without re-parsing YAML files.
*
* @internal used by Registry cache layer only
*/
public static function _restoreFromCache(array $configs): void
{
self::$artifact_configs = array_merge(self::$artifact_configs, $configs);
}
/**
* Get the configuration for a specific artifact by name.
*

View File

@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace StaticPHP\Config;
/**
* Simple parse-result cache for YAML/JSON config files.
*
* Key = raw file content string (files are small, direct comparison is fine).
* Value = parsed PHP array.
*
* Storage: <cwd>/.spc.cache.php (plain PHP, var_export'd array).
* Written once on shutdown when any new entry was added.
*/
class ConfigCache
{
private static ?array $cache = null;
private static bool $dirty = false;
/**
* Return the cached parsed result for $content, or null on miss.
*/
public static function get(string $content): ?array
{
self::load();
return self::$cache[$content] ?? null;
}
/**
* Store a parsed result. Will be persisted to disk on shutdown.
*/
public static function set(string $content, array $data): void
{
self::load();
self::$cache[$content] = $data;
self::$dirty = true;
}
/**
* Write cache to disk if anything changed. Called automatically on shutdown.
*/
public static function flush(): void
{
if (!self::$dirty) {
return;
}
file_put_contents(self::cachePath(), '<?php return ' . var_export(self::$cache, true) . ";\n");
self::$dirty = false;
}
private static function cachePath(): string
{
return getcwd() . '/.spc.cache.php';
}
private static function load(): void
{
if (self::$cache !== null) {
return;
}
$path = self::cachePath();
self::$cache = file_exists($path) ? (require $path) : [];
register_shutdown_function([self::class, 'flush']);
}
}

View File

@@ -50,12 +50,20 @@ class PackageConfig
if ($content === false) {
throw new WrongUsageException("Failed to read package config file: {$file}");
}
// judge extension
$data = match (pathinfo($file, PATHINFO_EXTENSION)) {
'json' => json_decode($content, true),
'yml', 'yaml' => Yaml::parse($content),
default => throw new WrongUsageException("Unsupported package config file format: {$file}"),
};
// judge extension — use cache to skip redundant parsing
$data = ConfigCache::get($content);
if ($data !== null) {
logger()->debug("Config cache hit: {$file}");
} else {
$data = match (pathinfo($file, PATHINFO_EXTENSION)) {
'json' => json_decode($content, true),
'yml', 'yaml' => extension_loaded('yaml') ? yaml_parse($content) : Yaml::parse($content),
default => throw new WrongUsageException("Unsupported package config file format: {$file}"),
};
if (is_array($data)) {
ConfigCache::set($content, $data);
}
}
ConfigValidator::validateAndLintPackages(basename($file), $data);
foreach ($data as $pkg_name => $config) {
self::$package_configs[$pkg_name] = $config;

View File

@@ -64,7 +64,7 @@ class Registry
}
$data = match (pathinfo($registry_file, PATHINFO_EXTENSION)) {
'json' => json_decode($yaml, true),
'yaml', 'yml' => Yaml::parse($yaml),
'yaml', 'yml' => extension_loaded('yaml') ? yaml_parse($yaml) : Yaml::parse($yaml),
default => throw new RegistryException("Unsupported registry file format: {$registry_file}"),
};
if (!is_array($data)) {