Initial Commit

This commit is contained in:
David Stone
2024-11-30 18:24:12 -07:00
commit e8f7955c1c
5432 changed files with 1397750 additions and 0 deletions

View File

@ -0,0 +1,99 @@
<?php
namespace WP_Ultimo\Dependencies\Amp\Cache;
use WP_Ultimo\Dependencies\Amp\Loop;
use WP_Ultimo\Dependencies\Amp\Promise;
use WP_Ultimo\Dependencies\Amp\Struct;
use WP_Ultimo\Dependencies\Amp\Success;
final class ArrayCache implements Cache
{
/** @var object */
private $sharedState;
/** @var string */
private $ttlWatcherId;
/** @var int|null */
private $maxSize;
/**
* @param int $gcInterval The frequency in milliseconds at which expired cache entries should be garbage collected.
* @param int $maxSize The maximum size of cache array (number of elements).
*/
public function __construct(int $gcInterval = 5000, int $maxSize = null)
{
// By using a shared state object we're able to use `__destruct()` for "normal" garbage collection of both this
// instance and the loop's watcher. Otherwise this object could only be GC'd when the TTL watcher was cancelled
// at the loop layer.
$this->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<void> */
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);
}
}