Skip to content

Commit

Permalink
Add generalized cache handling (#36)
Browse files Browse the repository at this point in the history
* Add generalized cache handling

* Create CHANGELOG.md

* Update ConfigCache.php

* Update ConfigEntry.php

* fetchTime int -> float

* Fix remaining float conversion issues
  • Loading branch information
z4kn4fein authored Aug 28, 2023
1 parent 9129f32 commit 374996f
Show file tree
Hide file tree
Showing 17 changed files with 306 additions and 267 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Please check the [Github Releases](https://github.com/configcat/php-sdk/releases) page for the changelog of the ConfigCat SDK for PHP.
19 changes: 0 additions & 19 deletions src/Cache/CacheItem.php

This file was deleted.

19 changes: 8 additions & 11 deletions src/Cache/ConfigCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ abstract protected function set(string $key, string $value): void;
* @throws InvalidArgumentException
* If the $key is not a legal value.
*/
public function store(string $key, CacheItem $value): void
public function store(string $key, ConfigEntry $value): void
{
if (empty($key)) {
throw new InvalidArgumentException("key cannot be empty.");
}

try {
$this->set($key, serialize($value));
$this->set($key, $value->serialize());
} catch (Exception $exception) {
$this->logger->error("Error occurred while writing the cache.", [
'event_id' => 2201, 'exception' => $exception
Expand All @@ -59,34 +59,31 @@ public function store(string $key, CacheItem $value): void
* Reads the value identified by the given $key from the underlying cache.
*
* @param string $key Identifier for the cached value.
* @return ?CacheItem Cached value for the given key, or null if it's missing.
* @return ConfigEntry Cached value for the given key, or null if it's missing.
*
* @throws InvalidArgumentException
* If the $key is not a legal value.
*/
public function load(string $key): ?CacheItem
public function load(string $key): ConfigEntry
{
if (empty($key)) {
throw new InvalidArgumentException("key cannot be empty.");
}

try {
$cached = $this->get($key);
if (!$cached) {
return null;
if (empty($cached)) {
return ConfigEntry::empty();
}

$result = unserialize($cached);
if ($result instanceof CacheItem) {
return $result;
}
return ConfigEntry::fromCached($cached);
} catch (Exception $exception) {
$this->logger->error("Error occurred while reading the cache.", [
'event_id' => 2200, 'exception' => $exception
]);
}

return null;
return ConfigEntry::empty();
}

/**
Expand Down
93 changes: 93 additions & 0 deletions src/Cache/ConfigEntry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace ConfigCat\Cache;

use UnexpectedValueException;

/**
* Represents the cached configuration.
* @package ConfigCat
*/
class ConfigEntry
{
private static ?ConfigEntry $empty = null;

private function __construct(
private readonly string $configJson,
private readonly array $config,
private readonly string $etag,
private readonly float $fetchTime,
) {
}

public function getConfigJson(): string
{
return $this->configJson;
}

public function getConfig(): array
{
return $this->config;
}

public function getEtag(): string
{
return $this->etag;
}

public function getFetchTime(): float
{
return $this->fetchTime;
}

public function serialize(): string
{
return $this->fetchTime . "\n" . $this->etag . "\n" . $this->configJson;
}

public function withTime(float $time): ConfigEntry
{
return new ConfigEntry($this->configJson, $this->config, $this->etag, $time);
}

public static function empty(): ConfigEntry
{
if (self::$empty == null) {
self::$empty = new ConfigEntry("", [], "", 0);
}

return self::$empty;
}

public static function fromConfigJson(string $configJson, string $etag, float $fetchTime): ConfigEntry
{
$deserialized = json_decode($configJson, true);
if ($deserialized == null) {
return self::empty();
}

return new ConfigEntry($configJson, $deserialized, $etag, $fetchTime);
}

public static function fromCached(string $cached): ConfigEntry
{
$timePos = strpos($cached, "\n");
$etagPos = strpos($cached, "\n", $timePos + 1);

if ($timePos === false || $etagPos === false) {
throw new UnexpectedValueException("Number of values is fewer than expected.");
}

$fetchTimeString = substr($cached, 0, $timePos);
$fetchTime = floatval($fetchTimeString);

if ($fetchTime == 0) {
throw new UnexpectedValueException("Invalid fetch time: " . $fetchTimeString);
}

$etag = substr($cached, $timePos + 1, $etagPos - $timePos - 1);
$configJson = substr($cached, $etagPos + 1);

return self::fromConfigJson($configJson, $etag, $fetchTime);
}
}
24 changes: 0 additions & 24 deletions src/ClientInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,6 @@ public function getValue(string $key, mixed $defaultValue, ?User $user = null):
*/
public function getValueDetails(string $key, mixed $defaultValue, ?User $user = null): EvaluationDetails;

/**
* Gets the Variation ID (analytics) of a feature flag or setting by the given key.
*
* @param string $key The identifier of the configuration value.
* @param mixed $defaultVariationId In case of any failure, this value will be returned.
* @param ?User $user The user object to identify the caller.
* @return ?string The Variation ID identified by the given key.
*
* @deprecated This method is obsolete and will be removed in a future major version.
* Please use getValueDetails() instead.
*/
public function getVariationId(string $key, ?string $defaultVariationId, ?User $user = null): ?string;

/**
* Gets the Variation IDs (analytics) of all feature flags or settings.
*
* @param ?User $user The user object to identify the caller.
* @return array of all Variation IDs.
*
* @deprecated This method is obsolete and will be removed in a future major version.
* Please use getAllValueDetails() instead.
*/
public function getAllVariationIds(?User $user = null): array;

/**
* Gets the key of a setting and its value identified by the given Variation ID (analytics).
*
Expand Down
2 changes: 1 addition & 1 deletion src/ClientOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class ClientOptions
public const EXCEPTIONS_TO_IGNORE = "exceptions-to-ignore";

/**
* Sets how frequent the cached configuration should be refreshed.
* Sets how frequent the cached configuration should be refreshed in seconds.
*/
public const CACHE_REFRESH_INTERVAL = "cache-refresh-interval";

Expand Down
Loading

0 comments on commit 374996f

Please sign in to comment.