diff --git a/composer.json b/composer.json index 1279518f..ea84a726 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "onebot/libonebot": "^0.5", "php-di/php-di": "^7", "psr/container": "^2.0", + "psr/simple-cache": "^3.0", "psy/psysh": "^0.11.8", "symfony/console": "^6.0", "symfony/polyfill-ctype": "^1.19", diff --git a/config/container.php b/config/container.php index 6bc44c2f..5a79ffda 100644 --- a/config/container.php +++ b/config/container.php @@ -1,11 +1,13 @@ [ - 'worker_id' => fn() => ProcessManager::getProcessId(), - Driver::class => fn() => Framework::getInstance()->getDriver(), - LoggerInterface::class => fn() => logger(), + 'worker_id' => fn () => ProcessManager::getProcessId(), + Driver::class => fn () => Framework::getInstance()->getDriver(), + LoggerInterface::class => fn () => logger(), ], ]; diff --git a/src/Globals/global_functions.php b/src/Globals/global_functions.php index 6b709ac7..fd4bffdc 100644 --- a/src/Globals/global_functions.php +++ b/src/Globals/global_functions.php @@ -248,11 +248,12 @@ function bot(): ZM\Context\BotContext return new \ZM\Context\BotContext('', ''); } -function kv(string $name = ''): KVInterface +function kv(string $name = ''): Psr\SimpleCache\CacheInterface { global $kv_class; if (!$kv_class) { $kv_class = config('global.kv.use', \LightCache::class); } - return $kv_class::open($name); + /* @phpstan-ignore-next-line */ + return is_a($kv_class, KVInterface::class, true) ? $kv_class::open($name) : new $kv_class($name); } diff --git a/src/ZM/Store/KV/KVInterface.php b/src/ZM/Store/KV/KVInterface.php index 038e1d15..1944585d 100644 --- a/src/ZM/Store/KV/KVInterface.php +++ b/src/ZM/Store/KV/KVInterface.php @@ -4,44 +4,9 @@ declare(strict_types=1); namespace ZM\Store\KV; +use Psr\SimpleCache\CacheInterface; + interface KVInterface { - /** - * 打开一个 KV 库 - * - * @param string $name KV 的库名称 - */ - public static function open(string $name = ''): KVInterface; - - /** - * 返回一个 KV 键值对的数据 - * - * @param string $key 键名 - * @param null|mixed $default 如果不存在时返回的默认值 - */ - public function get(string $key, mixed $default = null): mixed; - - /** - * 设置一个 KV 键值对的数据 - * - * @param string $key 键名 - * @param mixed $value 键值 - * @param int $ttl 超时秒数(如果等于 0 代表永不超时) - */ - public function set(string $key, mixed $value, int $ttl = 0): bool; - - /** - * 强制删除一个 KV 键值对数据 - * - * @param string $key 键名 - * @return bool 当键存在并被删除时返回 true - */ - public function unset(string $key): bool; - - /** - * 键值对数据是否存在 - * - * @param string $key 键名 - */ - public function isset(string $key): bool; + public static function open(string $name = ''): CacheInterface; } diff --git a/src/ZM/Store/KV/LightCache.php b/src/ZM/Store/KV/LightCache.php index 1ec2db71..c920e11a 100644 --- a/src/ZM/Store/KV/LightCache.php +++ b/src/ZM/Store/KV/LightCache.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace ZM\Store\KV; +use Psr\SimpleCache\CacheInterface; use ZM\Exception\InvalidArgumentException; use ZM\Process\ProcessStateManager; use ZM\Store\FileSystem; @@ -11,7 +12,7 @@ use ZM\Store\FileSystem; /** * 轻量、基于本地 JSON 文件的 KV 键值对缓存 */ -class LightCache implements KVInterface +class LightCache implements CacheInterface, KVInterface { /** @var array 存放库对象的列表 */ private static array $objs = []; @@ -49,7 +50,7 @@ class LightCache implements KVInterface /** * @throws InvalidArgumentException */ - public static function open(string $name = ''): KVInterface + public static function open(string $name = ''): CacheInterface { if (!isset(self::$objs[$name])) { self::$objs[$name] = new LightCache($name); @@ -82,18 +83,6 @@ class LightCache implements KVInterface ], JSON_THROW_ON_ERROR)); } - /** - * 删除该 KV 库的所有数据,并且永远无法恢复 - */ - public function removeSelf(): bool - { - if (file_exists($this->find_dir . '/' . $this->name . '.json')) { - unlink($this->find_dir . '/' . $this->name . '.json'); - } - unset(self::$caches[$this->name], self::$ttys[$this->name], self::$objs[$this->name]); - return true; - } - public function get(string $key, mixed $default = null): mixed { // 首先判断在不在缓存变量里 @@ -115,24 +104,62 @@ class LightCache implements KVInterface /** * @throws InvalidArgumentException */ - public function set(string $key, mixed $value, int $ttl = 0): bool + public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool { $this->validateKey($key); self::$caches[$this->name][$key] = $value; - if ($ttl > 0) { + if ($ttl !== null) { + if ($ttl instanceof \DateInterval) { + $ttl = $ttl->days * 86400 + $ttl->h * 3600 + $ttl->i * 60 + $ttl->s; + } self::$ttys[$this->name][$key] = time() + $ttl; } return true; } - public function unset(string $key): bool + public function delete(string $key): bool { unset(self::$caches[$this->name][$key], self::$ttys[$this->name][$key]); - return true; } - public function isset(string $key): bool + public function clear(): bool + { + if (file_exists($this->find_dir . '/' . $this->name . '.json')) { + unlink($this->find_dir . '/' . $this->name . '.json'); + } + unset(self::$caches[$this->name], self::$ttys[$this->name], self::$objs[$this->name]); + return true; + } + + public function getMultiple(iterable $keys, mixed $default = null): iterable + { + foreach ($keys as $v) { + yield $v => $this->get($v, $default); + } + } + + public function setMultiple(iterable $values, \DateInterval|int|null $ttl = null): bool + { + foreach ($values as $k => $v) { + if (!$this->set($k, $v, $ttl)) { + return false; + } + } + return true; + } + + public function deleteMultiple(iterable $keys): bool + { + foreach ($keys as $v) { + if (!$this->delete($v)) { + return false; + } + } + return true; + } + + public function has(string $key): bool { if (!isset(self::$caches[$this->name][$key])) { return false; diff --git a/tests/ZM/Store/KV/LightCacheTest.php b/tests/ZM/Store/KV/LightCacheTest.php index 98ee38a3..1578f751 100644 --- a/tests/ZM/Store/KV/LightCacheTest.php +++ b/tests/ZM/Store/KV/LightCacheTest.php @@ -17,7 +17,7 @@ class LightCacheTest extends TestCase $a = LightCache::open('asd'); $this->assertInstanceOf(LightCache::class, $a); /* @phpstan-ignore-next-line */ - $this->assertTrue($a->removeSelf()); + $this->assertTrue($a->clear()); } public function testSet() @@ -27,7 +27,7 @@ class LightCacheTest extends TestCase public function testIsset() { - $this->assertFalse(LightCache::open()->isset('test111')); + $this->assertFalse(LightCache::open()->has('test111')); } public function testGet() @@ -41,7 +41,7 @@ class LightCacheTest extends TestCase $kv = LightCache::open('sss'); $kv->set('test', 'test'); $this->assertSame($kv->get('test'), 'test'); - $kv->unset('test'); + $kv->delete('test'); $this->assertNull($kv->get('test')); } }