Skip to content

Commit 8938517

Browse files
committed
Add support to persistent CachedConfig
1 parent 7b5eaa7 commit 8938517

File tree

4 files changed

+93
-27
lines changed

4 files changed

+93
-27
lines changed

src/CachedConfig.php

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
namespace FastForward\Config;
1717

1818
use Psr\SimpleCache\CacheInterface;
19+
use Psr\SimpleCache\InvalidArgumentException;
1920

2021
/**
2122
* Class CachedConfig.
@@ -36,25 +37,54 @@ final class CachedConfig implements ConfigInterface
3637
*
3738
* @param CacheInterface $cache the cache implementation used for storing configuration data
3839
* @param ConfigInterface $defaultConfig the configuration source to be cached
40+
* @param bool $persistent whether the cache should be persistent or not
41+
* @param string|null $cacheKey the cache key to use for storing the configuration data
3942
*/
4043
public function __construct(
4144
private readonly CacheInterface $cache,
4245
private readonly ConfigInterface $defaultConfig,
43-
) {}
46+
private readonly bool $persistent = false,
47+
private ?string $cacheKey = null,
48+
) {
49+
$this->cacheKey ??= $this->defaultConfig::class;
50+
}
4451

4552
/**
4653
* Invokes the configuration and returns the cached configuration data.
4754
*
4855
* If the configuration has not yet been cached, it MUST be stored in the cache upon first invocation.
56+
* This method MUST return a ConfigInterface implementation containing the cached configuration data.
57+
*
58+
* @throws InvalidArgumentException if the cache key is invalid
4959
*
5060
* @return ConfigInterface a ConfigInterface implementation containing the cached configuration data
5161
*/
5262
public function __invoke(): ConfigInterface
5363
{
54-
if (!$this->cache->has($this->defaultConfig::class)) {
55-
$this->cache->set($this->defaultConfig::class, $this->defaultConfig->toArray());
64+
if (!$this->cache->has($this->cacheKey)) {
65+
$this->cache->set($this->cacheKey, $this->defaultConfig->toArray());
5666
}
5767

58-
return new ArrayConfig($this->cache->get($this->defaultConfig::class));
68+
return new ArrayConfig($this->cache->get($this->cacheKey));
69+
}
70+
71+
/**
72+
* Sets configuration data.
73+
*
74+
* This method MUST update the cached configuration data in the cache if the persistent flag is set to true.
75+
*
76+
* @param array|ConfigInterface|string $key the configuration key or an array of key-value pairs to set
77+
* @param mixed $value the value to set for the specified key
78+
*
79+
* @throws InvalidArgumentException if the key is invalid
80+
*/
81+
public function set(array|ConfigInterface|string $key, mixed $value = null): void
82+
{
83+
$config = $this->getConfig();
84+
$config->set($key, $value);
85+
86+
if ($this->persistent) {
87+
$this->cache->set($this->cacheKey, $config->toArray());
88+
}
5989
}
6090
}

src/Container/ConfigContainer.php

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
use Psr\Container\ContainerInterface;
2121

2222
/**
23-
* Class ConfigContainer
23+
* Class ConfigContainer.
2424
*
2525
* Provides a PSR-11 compatible container interface for accessing configuration values.
2626
*
@@ -45,7 +45,7 @@ final class ConfigContainer implements ContainerInterface
4545
* This constructor SHALL wrap an existing ConfigInterface instance and expose it
4646
* through PSR-11 `get()` and `has()` methods with namespace-style key resolution.
4747
*
48-
* @param ConfigInterface $config The configuration instance to expose as a container.
48+
* @param ConfigInterface $config the configuration instance to expose as a container
4949
*/
5050
public function __construct(
5151
private ConfigInterface $config,
@@ -58,8 +58,9 @@ public function __construct(
5858
* - The identifier matches known internal bindings (alias, interface, or class).
5959
* - The identifier is prefixed with 'config' and corresponds to an existing key in the configuration.
6060
*
61-
* @param string $id Identifier of the entry to look for.
62-
* @return bool True if the entry can be resolved; false otherwise.
61+
* @param string $id identifier of the entry to look for
62+
*
63+
* @return bool true if the entry can be resolved; false otherwise
6364
*/
6465
public function has(string $id): bool
6566
{
@@ -84,10 +85,11 @@ public function has(string $id): bool
8485
* If the configuration key exists, its value SHALL be returned.
8586
* - If the identifier cannot be resolved, a ContainerNotFoundException MUST be thrown.
8687
*
87-
* @param string $id Identifier of the entry to retrieve.
88-
* @return mixed The value associated with the identifier.
88+
* @param string $id identifier of the entry to retrieve
89+
*
90+
* @return mixed the value associated with the identifier
8991
*
90-
* @throws ContainerNotFoundException If the identifier cannot be resolved.
92+
* @throws ContainerNotFoundException if the identifier cannot be resolved
9193
*/
9294
public function get(string $id)
9395
{
@@ -118,8 +120,9 @@ public function get(string $id)
118120
* - the ConfigInterface::class string
119121
* - the concrete class of the injected configuration instance
120122
*
121-
* @param string $id The identifier to check.
122-
* @return bool True if the identifier is resolved internally; false otherwise.
123+
* @param string $id the identifier to check
124+
*
125+
* @return bool true if the identifier is resolved internally; false otherwise
123126
*/
124127
private function isResolvedByContainer(string $id): bool
125128
{

tests/CachedConfigTest.php

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,6 @@
22

33
declare(strict_types=1);
44

5-
/**
6-
* This file is part of php-fast-forward/config.
7-
*
8-
* This source file is subject to the license bundled
9-
* with this source code in the file LICENSE.
10-
*
11-
* @link https://github.com/php-fast-forward/config
12-
* @copyright Copyright (c) 2025 Felipe Sayão Lobato Abreu <github@mentordosnerds.com>
13-
* @license https://opensource.org/licenses/MIT MIT License
14-
*/
15-
165
namespace FastForward\Config\Tests;
176

187
use FastForward\Config\ArrayConfig;
@@ -26,9 +15,6 @@
2615
use Prophecy\Prophecy\ObjectProphecy;
2716
use Psr\SimpleCache\CacheInterface;
2817

29-
/**
30-
* @internal
31-
*/
3218
#[CoversClass(CachedConfig::class)]
3319
#[UsesClass(ArrayConfig::class)]
3420
final class CachedConfigTest extends TestCase
@@ -49,6 +35,7 @@ protected function setUp(): void
4935
$this->cachedConfig = new CachedConfig(
5036
cache: $this->cache->reveal(),
5137
defaultConfig: $this->defaultConfig->reveal(),
38+
persistent: true
5239
);
5340
}
5441

@@ -82,4 +69,41 @@ public function testInvokeWillReturnCachedConfigInstanceWhenAlreadyCached(): voi
8269
self::assertInstanceOf(ArrayConfig::class, $result);
8370
self::assertSame($data, $result->toArray());
8471
}
72+
73+
#[Test]
74+
public function testSetWillUpdateCacheWhenPersistentIsTrue(): void
75+
{
76+
$key = uniqid();
77+
$value = uniqid();
78+
$data = [$key => $value];
79+
80+
$this->cache->has($this->defaultConfig->reveal()::class)->willReturn(true);
81+
$this->cache->get($this->defaultConfig->reveal()::class)->willReturn([]);
82+
83+
$this->cache->set($this->defaultConfig->reveal()::class, $data)->shouldBeCalled();
84+
85+
$this->cachedConfig->set($key, $value);
86+
87+
self::assertSame($value, $this->cachedConfig->get($key));
88+
}
89+
90+
#[Test]
91+
public function testSetWillNotUpdateCacheWhenPersistentIsFalse(): void
92+
{
93+
$cachedConfig = new CachedConfig(
94+
cache: $this->cache->reveal(),
95+
defaultConfig: $this->defaultConfig->reveal(),
96+
persistent: false
97+
);
98+
99+
$key = uniqid();
100+
$value = uniqid();
101+
102+
$this->cache->has($this->defaultConfig->reveal()::class)->willReturn(true);
103+
$this->cache->get($this->defaultConfig->reveal()::class)->willReturn([]);
104+
105+
$this->cache->set($this->defaultConfig->reveal()::class, [$key => $value])->shouldNotBeCalled();
106+
107+
$cachedConfig->set($key, $value);
108+
}
85109
}

tests/Container/ConfigContainerTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,13 @@ public function testGetThrowsExceptionForUnknownKey(): void
9797

9898
$container->get(uniqid('unknown_', true));
9999
}
100+
101+
#[Test]
102+
public function testGetWithConfigContainerWillReturnConfigContainer(): void
103+
{
104+
$config = new ArrayConfig();
105+
$container = new ConfigContainer($config);
106+
107+
self::assertSame($container, $container->get(ConfigContainer::class));
108+
}
100109
}

0 commit comments

Comments
 (0)