diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 77d9a8a..10c3161 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -15,6 +15,7 @@ jobs:
- "8.3"
- "8.2"
- "8.1"
+ - "8.0"
steps:
- uses: actions/checkout@v3
- uses: shivammathur/setup-php@v2
diff --git a/README.md b/README.md
index 3f4ae2c..b65d495 100644
--- a/README.md
+++ b/README.md
@@ -8,20 +8,17 @@
@@ -121,6 +118,7 @@
- 支持 HSet/HGet/HDel/HKeys/HExists
- 支持 HIncr/HDecr,支持浮点运算
- 支持 储存对象数据
+ - 支持 HashKey的秒级过期时间【版本 ≥ 0.5】
- **通配符/正则匹配Search**
```php
@@ -138,6 +136,8 @@
}
);
```
+ **Tips:Cache::Search()本质上是个扫表匹配的过程,是O(N)的操作,如果需要对特定族群的数据进行监听,推荐使用Channel相关函数实现监听。**
+
- **原子性执行**
```php
diff --git a/composer.json b/composer.json
index 2dee76a..b159ced 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,7 @@
"source": "https://github.com/workbunny/webman-shared-cache"
},
"require": {
- "php": "^8.1",
+ "php": "^8.0",
"ext-apcu": "*"
},
"require-dev": {
diff --git a/src/Cache.php b/src/Cache.php
index 9a2d489..4d8ce9c 100644
--- a/src/Cache.php
+++ b/src/Cache.php
@@ -40,7 +40,7 @@ public static function __callStatic(string $name, array $arguments)
throw new Error('PHP-ext apcu not enable. ');
}
if (!apcu_enabled()) {
- throw new Error('You need run shared-cache-enable.sh. ');
+ throw new Error('You need run workbunny:shared-cache-enable/shared-cache-enable.sh command to enable APCu. ');
}
return call_user_func([self::class, "_$name"], ...$arguments);
}
diff --git a/src/Commands/AbstractCommand.php b/src/Commands/AbstractCommand.php
index c9e2133..9da0291 100644
--- a/src/Commands/AbstractCommand.php
+++ b/src/Commands/AbstractCommand.php
@@ -7,18 +7,52 @@
abstract class AbstractCommand extends Command
{
+ /**
+ * 兼容webman console,需重写
+ *
+ * @var string
+ */
+ protected static string $defaultName = '';
+ /**
+ * 兼容webman console,需重写
+ *
+ * @var string
+ */
+ protected static string $defaultDescription = '';
+
+ /**
+ * 输出info
+ *
+ * @param OutputInterface $output
+ * @param string $message
+ * @return void
+ */
protected function info(OutputInterface $output, string $message): void
{
$output->writeln("ℹ️ $message");
}
+ /**
+ * 输出error
+ *
+ * @param OutputInterface $output
+ * @param string $message
+ * @return int
+ */
protected function error(OutputInterface $output, string $message): int
{
$output->writeln("❌ $message");
return self::FAILURE;
}
+ /**
+ * 输出success
+ *
+ * @param OutputInterface $output
+ * @param string $message
+ * @return int
+ */
protected function success(OutputInterface $output, string $message): int
{
$output->writeln("✅ $message");
diff --git a/src/Commands/WorkbunnyWebmanSharedCacheClean.php b/src/Commands/WorkbunnyWebmanSharedCacheClean.php
index ea5f1e3..a521b95 100644
--- a/src/Commands/WorkbunnyWebmanSharedCacheClean.php
+++ b/src/Commands/WorkbunnyWebmanSharedCacheClean.php
@@ -10,13 +10,15 @@
class WorkbunnyWebmanSharedCacheClean extends AbstractCommand
{
+ protected static string $defaultName = 'workbunny:shared-cache-clean';
+ protected static string $defaultDescription = 'Remove all workbunny/webman-shared-cache caches. ';
+
/**
* @return void
*/
protected function configure(): void
{
- $this->setName('workbunny:shared-cache-clean')
- ->setDescription('Remove all workbunny/webman-shared-cache caches. ');
+ $this->setName(static::$defaultName)->setDescription(static::$defaultDescription);
}
/**
diff --git a/src/Commands/WorkbunnyWebmanSharedCacheEnable.php b/src/Commands/WorkbunnyWebmanSharedCacheEnable.php
new file mode 100644
index 0000000..1c81871
--- /dev/null
+++ b/src/Commands/WorkbunnyWebmanSharedCacheEnable.php
@@ -0,0 +1,72 @@
+setDescription('Enable APCu cache with specified settings.')
+ ->addOption('file', 'f', InputOption::VALUE_REQUIRED, 'Specify configuration name', 'apcu-cache.ini')
+ ->addOption('target', 't', InputOption::VALUE_REQUIRED, 'Specify target location', '/usr/local/etc/php/conf.d')
+ ->addOption('size', 'si', InputOption::VALUE_REQUIRED, 'Configure apcu.shm_size', '1024M')
+ ->addOption('segments', 'se', InputOption::VALUE_REQUIRED, 'Configure apcu.shm_segments', 1)
+ ->addOption('mmap', 'm', InputOption::VALUE_REQUIRED, 'Configure apcu.mmap_file_mask', '')
+ ->addOption('gc_ttl', 'gc', InputOption::VALUE_REQUIRED, 'Configure apcu.gc_ttl', 3600);
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @return int
+ */
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $fileName = $input->getOption('file');
+ $target = $input->getOption('target');
+ $shmSize = $input->getOption('size');
+ $shmSegments = $input->getOption('segments');
+ $mmapFileMask = $input->getOption('mmap');
+ $gcTtl = $input->getOption('gc_ttl');
+
+ if (!is_dir($target)) {
+ return $this->error($output, "Target directory does not exist: $target. ");
+ }
+ $configContent = <<getHelper('question');
+ $question = new ConfirmationQuestion("Configuration file already exists at $filePath. Overwrite? (y/N) ", false);
+
+ if (!$helper->ask($input, $output, $question)) {
+ return $this->success($output, "Operation aborted. ");
+ }
+ }
+
+ file_put_contents($filePath, $configContent);
+ return $this->success($output, "Configuration file created at: $filePath. ");
+ }
+
+}
diff --git a/src/Commands/WorkbunnyWebmanSharedCacheHRecycle.php b/src/Commands/WorkbunnyWebmanSharedCacheHRecycle.php
new file mode 100644
index 0000000..11c72cd
--- /dev/null
+++ b/src/Commands/WorkbunnyWebmanSharedCacheHRecycle.php
@@ -0,0 +1,48 @@
+setName(static::$defaultName)->setDescription(static::$defaultDescription);
+ $this->addOption('key', 'k', InputOption::VALUE_OPTIONAL, 'Cache Key. ');
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @return int
+ */
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $key = $input->getOption('key');
+ if ($key) {
+ Cache::HRecycle($key);
+ } else {
+ $progressBar = new ProgressBar($output);
+ $progressBar->start();
+ $keys = Cache::Keys();
+ $progressBar->setMaxSteps(count($keys));
+ foreach ($keys as $key) {
+ Cache::HRecycle($key);
+ $progressBar->advance();
+ }
+ $progressBar->finish();
+ }
+ return $this->success($output, 'HRecycle Success. ');
+ }
+}
diff --git a/src/Commands/WorkbunnyWebmanSharedCacheList.php b/src/Commands/WorkbunnyWebmanSharedCacheList.php
deleted file mode 100644
index 413e918..0000000
--- a/src/Commands/WorkbunnyWebmanSharedCacheList.php
+++ /dev/null
@@ -1,44 +0,0 @@
-setName('workbunny:shared-cache-list')
- ->setDescription('Show workbunny/webman-shared-cache caches list. ');
-
- $this->addOption('page', 'p', InputOption::VALUE_OPTIONAL, 'Page. ', 1);
- $this->addOption('size', 's', InputOption::VALUE_OPTIONAL, 'Page size. ', 20);
- }
-
- /**
- * @param InputInterface $input
- * @param OutputInterface $output
- * @return int
- */
- protected function execute(InputInterface $input, OutputInterface $output): int
- {
- $page = $input->getOption('page');
- $size = $input->getOption('size');
- $headers = ['name', 'value'];
- $rows = [];
- // todo
-
- $table = new Table($output);
- $table->setHeaders($headers);
- $table->setRows($rows);
- $table->render();
-
- return self::SUCCESS;
- }
-}
diff --git a/src/Future.php b/src/Future.php
index eadfcef..8303883 100644
--- a/src/Future.php
+++ b/src/Future.php
@@ -22,9 +22,10 @@ class Future
/**
* @param Closure $func
* @param array $args
+ * @param float|int|null $interval
* @return int|false
*/
- public static function add(Closure $func, array $args = []): int|false
+ public static function add(Closure $func, array $args = [], float|int|null $interval = null): int|false
{
if (self::$debug) {
self::$debugFunc = $func;
diff --git a/src/Traits/ChannelMethods.php b/src/Traits/ChannelMethods.php
index 2e27c19..677e734 100644
--- a/src/Traits/ChannelMethods.php
+++ b/src/Traits/ChannelMethods.php
@@ -21,10 +21,24 @@ trait ChannelMethods
protected static string $_CHANNEL = '#Channel#';
/**
- * @var array = [channelKey => listeners]
+ * @var array = [channelKey => futureId]
*/
protected static array $_listeners = [];
+ /**
+ * @var float|int|null
+ */
+ protected static float|int|null $interval = null;
+
+ /**
+ * @param float|int|null $interval
+ * @return void
+ */
+ public static function SetChannelListenerInterval(float|int|null $interval): void
+ {
+ self::$interval = $interval;
+ }
+
/**
* @param string $key
* @return string
@@ -66,7 +80,7 @@ protected static function _ChPublish(string $key, mixed $message, bool $store =
$func = __FUNCTION__;
$params = func_get_args();
self::_Atomic($key, function () use (
- $key, $message, $func, $params, $store
+ $key, $message, $func, $params, $store, $workerId
) {
/**
* [
@@ -80,13 +94,30 @@ protected static function _ChPublish(string $key, mixed $message, bool $store =
// 如果还没有监听器,将数据投入默认
if (!$channel) {
if ($store) {
- $channel['--default--']['value'][] = $message;
+ // 非指定workerId
+ if ($workerId === null) {
+ $channel['--default--']['value'][] = $message;
+ }
+ // 指定workerId
+ else {
+ $channel[$workerId]['value'][] = $message;
+ }
+
}
}
// 否则将消息投入到每个worker的监听器数据中
else {
- foreach ($channel as $workerId => $item) {
- if ($store or isset($item['futureId'])) {
+ // 非指定workerId
+ if ($workerId === null) {
+ foreach ($channel as $workerId => $item) {
+ if ($store or isset($item['futureId'])) {
+ $channel[$workerId]['value'][] = $message;
+ }
+ }
+ }
+ // 指定workerId
+ else {
+ if ($store or isset($channel[$workerId]['futureId'])) {
$channel[$workerId]['value'][] = $message;
}
}
@@ -123,7 +154,7 @@ protected static function _ChCreateListener(string $key, string|int $workerId, C
throw new Error("Channel $key listener already exist. ");
}
self::_Atomic($key, function () use (
- $key, $workerId, $func, $params, &$result
+ $key, $workerId, $func, $params, $listener, &$result
) {
/**
* [
@@ -138,20 +169,20 @@ protected static function _ChCreateListener(string $key, string|int $workerId, C
// 设置回调
$channel[$workerId]['futureId'] =
self::$_listeners[$key] =
- $result = Future::add(function () use ($key, $workerId) {
+ $result = Future::add(function () use ($key, $workerId, $listener) {
// 原子性执行
- self::_Atomic($key, function () use ($key, $workerId) {
+ self::_Atomic($key, function () use ($key, $workerId, $listener) {
$channel = self::_Get($channelName = self::GetChannelKey($key), []);
if ((!empty($value = $channel[$workerId]['value'] ?? []))) {
// 先进先出
$msg = array_shift($value);
$channel[$workerId]['value'] = $value;
- call_user_func(self::$_listeners[$key], $key, $workerId, $msg);
+ call_user_func($listener, $key, $workerId, $msg);
self::_Set($channelName, $channel);
}
});
- });
+ }, interval: self::$interval);
$channel[$workerId]['value'] = [];
// 如果存在默认数据
if ($default = $channel['--default--']['value'] ?? []) {
@@ -202,7 +233,6 @@ protected static function _ChRemoveListener(string $key, string|int $workerId, b
self::_Set($channelName, $channel);
}
unset(self::$_listeners[$key]);
-
}
return [
diff --git a/src/Traits/HashMethods.php b/src/Traits/HashMethods.php
index ebed670..aba6970 100644
--- a/src/Traits/HashMethods.php
+++ b/src/Traits/HashMethods.php
@@ -10,6 +10,7 @@
* @method static bool|int|float HIncr(string $key, string|int $hashKey, int|float $value = 1) Hash 自增
* @method static bool|int|float HDecr(string $key, string|int $hashKey, int|float $value = 1) Hash 自减
* @method static array HExists(string $key, string|int ...$hashKey) Hash key 判断
+ * @method static void HRecycle(string $key) Hash key 过期回收
*/
trait HashMethods
{
@@ -23,17 +24,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 +57,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 +98,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 +174,50 @@ 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;
+ }
+
+ /**
+ * 回收过期 hashKey
+ *
+ * @param string $key
+ * @return void
+ */
+ protected static function _HRecycle(string $key): void
+ {
+ $func = __FUNCTION__;
+ $params = func_get_args();
+ self::_Atomic($key, function () use (
+ $key, $func, $params
+ ) {
+ $hash = self::_Get($key, []);
+ if (isset($hash['_ttl']) and isset($hash['_timestamp'])) {
+ $now = time();
+ $set = false;
+ foreach ($hash as $hashKey => $hashValue) {
+ $ttl = $hashValue['_ttl'] ?? 0;
+ $timestamp = $hashValue['_timestamp'] ?? 0;
+ if ($ttl > 0 and $timestamp > 0 and $timestamp + $ttl < $now) {
+ $set = true;
+ unset($hash[$hashKey]);
+ }
+ }
+ if ($set) {
+ self::_Set($key, $hash);
+ }
+ }
+ return [
+ 'timestamp' => microtime(true),
+ 'method' => $func,
+ 'params' => $params,
+ 'result' => null
+ ];
+ }, true);
}
/**
@@ -163,8 +231,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 +252,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/src/config/plugin/workbunny/webman-shared-cache/command.php b/src/config/plugin/workbunny/webman-shared-cache/command.php
new file mode 100644
index 0000000..aff331a
--- /dev/null
+++ b/src/config/plugin/workbunny/webman-shared-cache/command.php
@@ -0,0 +1,18 @@
+
+ * @copyright chaz6chez
+ * @link https://github.com/workbunny/webman-push-server
+ * @license https://github.com/workbunny/webman-push-server/blob/main/LICENSE
+ */
+declare(strict_types=1);
+
+return [
+ Workbunny\WebmanSharedCache\Commands\WorkbunnyWebmanSharedCacheEnable::class,
+ Workbunny\WebmanSharedCache\Commands\WorkbunnyWebmanSharedCacheClean::class,
+ Workbunny\WebmanSharedCache\Commands\WorkbunnyWebmanSharedCacheHRecycle::class
+];
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);