diff --git a/src/Traits/HashMethods.php b/src/Traits/HashMethods.php index ebed670..ec813a7 100644 --- a/src/Traits/HashMethods.php +++ b/src/Traits/HashMethods.php @@ -23,17 +23,22 @@ trait HashMethods * @param string $key * @param string|int $hashKey * @param mixed $hashValue + * @param int $ttl * @return bool */ - protected static function _HSet(string $key, string|int $hashKey, mixed $hashValue): bool + protected static function _HSet(string $key, string|int $hashKey, mixed $hashValue, int $ttl = 0): bool { $func = __FUNCTION__; $params = func_get_args(); self::_Atomic($key, function () use ( - $key, $hashKey, $hashValue, $func, $params + $key, $hashKey, $hashValue, $ttl, $func, $params ) { $hash = self::_Get($key, []); - $hash[$hashKey] = $hashValue; + $hash[$hashKey] = [ + '_value' => $hashValue, + '_ttl' => $ttl, + '_timestamp' => time() + ]; self::_Set($key, $hash); return [ 'timestamp' => microtime(true), @@ -51,19 +56,29 @@ protected static function _HSet(string $key, string|int $hashKey, mixed $hashVal * @param string $key * @param string|int $hashKey * @param int|float $hashValue + * @param int $ttl * @return bool|int|float */ - protected static function _HIncr(string $key, string|int $hashKey, int|float $hashValue = 1): bool|int|float + protected static function _HIncr(string $key, string|int $hashKey, int|float $hashValue = 1, int $ttl = 0): bool|int|float { $func = __FUNCTION__; $result = false; $params = func_get_args(); self::_Atomic($key, function () use ( - $key, $hashKey, $hashValue, $func, $params, &$result + $key, $hashKey, $hashValue, $ttl, $func, $params, &$result ) { $hash = self::_Get($key, []); - if (is_numeric($v = ($hash[$hashKey] ?? 0))) { - $hash[$hashKey] = $result = $v + $hashValue; + $value = $hash[$hashKey]['_value'] ?? 0; + $oldTtl = $hash[$hashKey]['_ttl'] ?? 0; + $timestamp = $hash[$hashKey]['_timestamp'] ?? 0; + if (is_numeric($value)) { + $now = time(); + $value = ($oldTtl <= 0 or (($timestamp + $oldTtl) >= $now)) ? $value : 0; + $hash[$hashKey] = [ + '_value' => $result = $value + $hashValue, + '_ttl' => ($ttl > 0) ? $ttl : ($timestamp > 0 ? $now - $timestamp : 0), + '_timestamp' => $now, + ]; self::_Set($key, $hash); } return [ @@ -82,19 +97,29 @@ protected static function _HIncr(string $key, string|int $hashKey, int|float $ha * @param string $key * @param string|int $hashKey * @param int|float $hashValue + * @param int $ttl * @return bool|int|float */ - protected static function _HDecr(string $key, string|int $hashKey, int|float $hashValue = 1): bool|int|float + protected static function _HDecr(string $key, string|int $hashKey, int|float $hashValue = 1, int $ttl = 0): bool|int|float { $func = __FUNCTION__; $result = false; $params = func_get_args(); self::_Atomic($key, function () use ( - $key, $hashKey, $hashValue, $func, $params, &$result + $key, $hashKey, $hashValue, $ttl, $func, $params, &$result ) { $hash = self::_Get($key, []); - if (is_numeric($v = ($hash[$hashKey] ?? 0))) { - $hash[$hashKey] = $result = $v - $hashValue; + $value = $hash[$hashKey]['_value'] ?? 0; + $oldTtl = $hash[$hashKey]['_ttl'] ?? 0; + $timestamp = $hash[$hashKey]['_timestamp'] ?? 0; + if (is_numeric($value)) { + $now = time(); + $value = ($oldTtl <= 0 or (($timestamp + $oldTtl) >= $now)) ? $value : 0; + $hash[$hashKey] = [ + '_value' => $result = $value - $hashValue, + '_ttl' => ($ttl > 0) ? $ttl : ($timestamp > 0 ? $now - $timestamp : 0), + '_timestamp' => $now, + ]; self::_Set($key, $hash); } return [ @@ -148,8 +173,12 @@ protected static function _HDel(string $key, string|int ...$hashKey): bool */ protected static function _HGet(string $key, string|int $hashKey, mixed $default = null): mixed { + $now = time(); $hash = self::_Get($key, []); - return $hash[$hashKey] ?? $default; + $value = $hash[$hashKey]['_value'] ?? $default; + $ttl = $hash[$hashKey]['_ttl'] ?? 0; + $timestamp = $hash[$hashKey]['_timestamp'] ?? 0; + return ($ttl <= 0 or (($timestamp + $ttl) >= $now)) ? $value : $default; } /** @@ -163,8 +192,11 @@ protected static function _HExists(string $key, string|int ...$hashKey): array { $hash = self::_Get($key, []); $result = []; + $now = time(); foreach ($hashKey as $hk) { - if (isset($hash[$hk])) { + $ttl = $hash[$hk]['_ttl'] ?? 0; + $timestamp = $hash[$hk]['_timestamp'] ?? 0; + if (($ttl <= 0 or (($timestamp + $ttl) >= $now)) and isset($hash[$hk]['_value'])) { $result[$hk] = true; } } @@ -181,11 +213,17 @@ protected static function _HExists(string $key, string|int ...$hashKey): array protected static function _HKeys(string $key, null|string $regex = null): array { $hash = self::_Get($key, []); - $keys = array_keys($hash); - if ($regex !== null) { - $keys = array_values(array_filter($keys, function($key) use ($regex) { - return preg_match($regex, $key); - })); + $keys = []; + $now = time(); + foreach ($hash as $hashKey => $hashValue) { + $ttl = $hashValue['_ttl'] ?? 0; + $timestamp = $hashValue['_timestamp'] ?? 0; + if (($ttl <= 0 or (($timestamp + $ttl) >= $now)) and isset($hashValue['_value'])) { + if ($regex !== null and preg_match($regex, $key)) { + continue; + } + $keys[] = $hashKey; + } } return $keys; } diff --git a/tests/HashTest.php b/tests/HashTest.php index d04126a..639fda8 100644 --- a/tests/HashTest.php +++ b/tests/HashTest.php @@ -13,7 +13,11 @@ public function testHashGet(): void // 单进程执行 $this->assertEquals(null, Cache::HGet($key, $hash)); apcu_add($key, [ - $hash => $hash + $hash => [ + '_value' => $hash, + '_ttl' => 0, + '_timestamp' => time() + ] ]); $this->assertEquals([], Cache::LockInfo()); $this->assertEquals($hash, Cache::HGet($key, $hash)); @@ -24,7 +28,11 @@ public function testHashGet(): void $this->assertEquals(null, Cache::HGet($key, $hash)); $this->childExec(static function (string $key, string $hash) { apcu_add($key, [ - $hash => $hash + $hash => [ + '_value' => $hash, + '_ttl' => 0, + '_timestamp' => time() + ] ]); }, $key, $hash); $this->assertEquals([], Cache::LockInfo()); @@ -42,7 +50,11 @@ public function testHashSet(): void $this->assertTrue(Cache::HSet($key, $hash, $hash)); $this->assertEquals([], Cache::LockInfo()); $this->assertEquals([ - $hash => $hash + $hash => [ + '_value' => $hash, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); // 清理 apcu_delete($key); @@ -54,7 +66,11 @@ public function testHashSet(): void }, $key, $hash); $this->assertEquals([], Cache::LockInfo()); $this->assertEquals([ - $hash => $hash + $hash => [ + '_value' => $hash, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); // 清理 apcu_delete($key); @@ -98,8 +114,12 @@ public function testHashExists(): void $this->assertEquals([], Cache::HExists($key, 'a')); apcu_add($key, [ - 'a' => 1, - 'b' => 2 + 'a' => [ + '_value' => 1 + ], + 'b' => [ + '_value' => 2 + ] ]); $this->assertEquals([ 'a' => true, 'b' => true @@ -116,15 +136,27 @@ public function testHashIncr(): void $this->assertFalse(apcu_fetch($key)); $this->assertEquals(1, Cache::HIncr($key, 'a')); $this->assertEquals([ - 'a' => 1 + 'a' => [ + '_value' => 1, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); $this->assertEquals(3, Cache::HIncr($key, 'a', 2)); $this->assertEquals([ - 'a' => 3 + 'a' => [ + '_value' => 3, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); $this->assertEquals(4.1, Cache::HIncr($key, 'a', 1.1)); $this->assertEquals([ - 'a' => 4.1 + 'a' => [ + '_value' => 4.1, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); // 清理 apcu_delete($key); @@ -135,19 +167,31 @@ public function testHashIncr(): void Cache::HIncr($key, 'a'); }, $key); $this->assertEquals([ - 'a' => 1 + 'a' => [ + '_value' => 1, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); $this->childExec(static function (string $key) { Cache::HIncr($key, 'a',2); }, $key); $this->assertEquals([ - 'a' => 3 + 'a' => [ + '_value' => 3, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); $this->childExec(static function (string $key) { Cache::HIncr($key, 'a',1.1); }, $key); $this->assertEquals([ - 'a' => 4.1 + 'a' => [ + '_value' => 4.1, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); // 清理 apcu_delete($key); @@ -160,15 +204,27 @@ public function testHashDecr(): void $this->assertFalse(apcu_fetch($key)); $this->assertEquals(-1, Cache::HDecr($key, 'a')); $this->assertEquals([ - 'a' => -1 + 'a' => [ + '_value' => -1, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); $this->assertEquals(-3, Cache::HDecr($key, 'a', 2)); $this->assertEquals([ - 'a' => -3 + 'a' => [ + '_value' => -3, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); $this->assertEquals(-4.1, Cache::HDecr($key, 'a', 1.1)); $this->assertEquals([ - 'a' => -4.1 + 'a' => [ + '_value' => -4.1, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); // 清理 apcu_delete($key); @@ -179,19 +235,31 @@ public function testHashDecr(): void Cache::HDecr($key, 'a'); }, $key); $this->assertEquals([ - 'a' => -1 + 'a' => [ + '_value' => -1, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); $this->childExec(static function (string $key) { Cache::HDecr($key, 'a', 2); }, $key); $this->assertEquals([ - 'a' => -3 + 'a' => [ + '_value' => -3, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); $this->childExec(static function (string $key) { Cache::HDecr($key, 'a', 1.1); }, $key); $this->assertEquals([ - 'a' => -4.1 + 'a' => [ + '_value' => -4.1, + '_ttl' => 0, + '_timestamp' => time() + ] ], apcu_fetch($key)); // 清理 apcu_delete($key);