sharedState = $sharedState = new class { use Struct; /** @var string[] */ public $cache = []; /** @var int[] */ public $cacheTimeouts = []; /** @var bool */ public $isSortNeeded = \false; public function collectGarbage() : void { $now = \time(); if ($this->isSortNeeded) { \asort($this->cacheTimeouts); $this->isSortNeeded = \false; } foreach ($this->cacheTimeouts as $key => $expiry) { if ($now <= $expiry) { break; } unset($this->cache[$key], $this->cacheTimeouts[$key]); } } }; $this->ttlWatcherId = Loop::repeat($gcInterval, [$sharedState, "collectGarbage"]); $this->maxSize = $maxSize; Loop::unreference($this->ttlWatcherId); } public function __destruct() { $this->sharedState->cache = []; $this->sharedState->cacheTimeouts = []; Loop::cancel($this->ttlWatcherId); } /** @inheritdoc */ public function get(string $key) : Promise { if (!isset($this->sharedState->cache[$key])) { return new Success(null); } if (isset($this->sharedState->cacheTimeouts[$key]) && \time() > $this->sharedState->cacheTimeouts[$key]) { unset($this->sharedState->cache[$key], $this->sharedState->cacheTimeouts[$key]); return new Success(null); } return new Success($this->sharedState->cache[$key]); } /** @inheritdoc */ public function set(string $key, string $value, int $ttl = null) : Promise { if ($ttl === null) { unset($this->sharedState->cacheTimeouts[$key]); } elseif ($ttl >= 0) { $expiry = \time() + $ttl; $this->sharedState->cacheTimeouts[$key] = $expiry; $this->sharedState->isSortNeeded = \true; } else { throw new \Error("Invalid cache TTL ({$ttl}; integer >= 0 or null required"); } unset($this->sharedState->cache[$key]); if (\count($this->sharedState->cache) === $this->maxSize) { \array_shift($this->sharedState->cache); } $this->sharedState->cache[$key] = $value; /** @var Promise */ return new Success(); } /** @inheritdoc */ public function delete(string $key) : Promise { $exists = isset($this->sharedState->cache[$key]); unset($this->sharedState->cache[$key], $this->sharedState->cacheTimeouts[$key]); return new Success($exists); } }