Skip to content

Commit baa21d6

Browse files
committed
Implement caching for config file parsing to improve performance
1 parent 8e91e02 commit baa21d6

File tree

5 files changed

+111
-14
lines changed

5 files changed

+111
-14
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,6 @@ spc.exe
6767

6868
# dumped files from StaticPHP v3
6969
/dump-*.json
70+
71+
# config parse cache
72+
/.spc.cache.php

src/StaticPHP/Config/ArtifactConfig.php

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,22 @@ public static function loadFromFile(string $file, string $registry_name): string
4242
if ($content === false) {
4343
throw new WrongUsageException("Failed to read artifact config file: {$file}");
4444
}
45-
$data = match (pathinfo($file, PATHINFO_EXTENSION)) {
46-
'json' => json_decode($content, true),
47-
'yml', 'yaml' => Yaml::parse($content),
48-
default => throw new WrongUsageException("Unsupported artifact config file format: {$file}"),
49-
};
50-
if (!is_array($data)) {
51-
throw new WrongUsageException("Invalid JSON format in artifact config file: {$file}");
45+
// use cache to skip redundant parsing
46+
$data = ConfigCache::get($content);
47+
if ($data !== null) {
48+
logger()->debug("Config cache hit: {$file}");
49+
} else {
50+
$data = match (pathinfo($file, PATHINFO_EXTENSION)) {
51+
'json' => json_decode($content, true),
52+
'yml', 'yaml' => extension_loaded('yaml') ? yaml_parse($content) : Yaml::parse($content),
53+
default => throw new WrongUsageException("Unsupported artifact config file format: {$file}"),
54+
};
55+
if (!is_array($data)) {
56+
throw new WrongUsageException("Invalid JSON format in artifact config file: {$file}");
57+
}
58+
if (is_array($data)) {
59+
ConfigCache::set($content, $data);
60+
}
5261
}
5362
ConfigValidator::validateAndLintArtifacts(basename($file), $data);
5463
foreach ($data as $artifact_name => $config) {
@@ -68,6 +77,16 @@ public static function getAll(): array
6877
return self::$artifact_configs;
6978
}
7079

80+
/**
81+
* Restore artifact configs from cache without re-parsing YAML files.
82+
*
83+
* @internal used by Registry cache layer only
84+
*/
85+
public static function _restoreFromCache(array $configs): void
86+
{
87+
self::$artifact_configs = array_merge(self::$artifact_configs, $configs);
88+
}
89+
7190
/**
7291
* Get the configuration for a specific artifact by name.
7392
*
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace StaticPHP\Config;
6+
7+
/**
8+
* Simple parse-result cache for YAML/JSON config files.
9+
*
10+
* Key = raw file content string (files are small, direct comparison is fine).
11+
* Value = parsed PHP array.
12+
*
13+
* Storage: <cwd>/.spc.cache.php (plain PHP, var_export'd array).
14+
* Written once on shutdown when any new entry was added.
15+
*/
16+
class ConfigCache
17+
{
18+
private static ?array $cache = null;
19+
20+
private static bool $dirty = false;
21+
22+
/**
23+
* Return the cached parsed result for $content, or null on miss.
24+
*/
25+
public static function get(string $content): ?array
26+
{
27+
self::load();
28+
return self::$cache[$content] ?? null;
29+
}
30+
31+
/**
32+
* Store a parsed result. Will be persisted to disk on shutdown.
33+
*/
34+
public static function set(string $content, array $data): void
35+
{
36+
self::load();
37+
self::$cache[$content] = $data;
38+
self::$dirty = true;
39+
}
40+
41+
/**
42+
* Write cache to disk if anything changed. Called automatically on shutdown.
43+
*/
44+
public static function flush(): void
45+
{
46+
if (!self::$dirty) {
47+
return;
48+
}
49+
file_put_contents(self::cachePath(), '<?php return ' . var_export(self::$cache, true) . ";\n");
50+
self::$dirty = false;
51+
}
52+
53+
private static function cachePath(): string
54+
{
55+
return getcwd() . '/.spc.cache.php';
56+
}
57+
58+
private static function load(): void
59+
{
60+
if (self::$cache !== null) {
61+
return;
62+
}
63+
$path = self::cachePath();
64+
self::$cache = file_exists($path) ? (require $path) : [];
65+
register_shutdown_function([self::class, 'flush']);
66+
}
67+
}

src/StaticPHP/Config/PackageConfig.php

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,20 @@ public static function loadFromFile(string $file, string $registry_name): string
5050
if ($content === false) {
5151
throw new WrongUsageException("Failed to read package config file: {$file}");
5252
}
53-
// judge extension
54-
$data = match (pathinfo($file, PATHINFO_EXTENSION)) {
55-
'json' => json_decode($content, true),
56-
'yml', 'yaml' => Yaml::parse($content),
57-
default => throw new WrongUsageException("Unsupported package config file format: {$file}"),
58-
};
53+
// judge extension — use cache to skip redundant parsing
54+
$data = ConfigCache::get($content);
55+
if ($data !== null) {
56+
logger()->debug("Config cache hit: {$file}");
57+
} else {
58+
$data = match (pathinfo($file, PATHINFO_EXTENSION)) {
59+
'json' => json_decode($content, true),
60+
'yml', 'yaml' => extension_loaded('yaml') ? yaml_parse($content) : Yaml::parse($content),
61+
default => throw new WrongUsageException("Unsupported package config file format: {$file}"),
62+
};
63+
if (is_array($data)) {
64+
ConfigCache::set($content, $data);
65+
}
66+
}
5967
ConfigValidator::validateAndLintPackages(basename($file), $data);
6068
foreach ($data as $pkg_name => $config) {
6169
self::$package_configs[$pkg_name] = $config;

src/StaticPHP/Registry/Registry.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public static function loadRegistry(string $registry_file, bool $auto_require =
6464
}
6565
$data = match (pathinfo($registry_file, PATHINFO_EXTENSION)) {
6666
'json' => json_decode($yaml, true),
67-
'yaml', 'yml' => Yaml::parse($yaml),
67+
'yaml', 'yml' => extension_loaded('yaml') ? yaml_parse($yaml) : Yaml::parse($yaml),
6868
default => throw new RegistryException("Unsupported registry file format: {$registry_file}"),
6969
};
7070
if (!is_array($data)) {

0 commit comments

Comments
 (0)