Skip to content

Commit 7351fdf

Browse files
committed
Moved redis-lua into redis.
1 parent ef71660 commit 7351fdf

File tree

10 files changed

+452
-0
lines changed

10 files changed

+452
-0
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://doc.hyperf.io
9+
* @contact [email protected]
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace Hyperf\Redis\Exception;
14+
15+
class RedisNotFoundException extends \RuntimeException
16+
{
17+
}

src/Lua/Hash/HGetAllMultiple.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://doc.hyperf.io
9+
* @contact [email protected]
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace Hyperf\Redis\Lua\Hash;
14+
15+
use Hyperf\Redis\Lua\Script;
16+
17+
class HGetAllMultiple extends Script
18+
{
19+
public function getScript(): string
20+
{
21+
return <<<'LUA'
22+
local values = {};
23+
for i,v in ipairs(KEYS) do
24+
if(redis.call('type',v).ok == 'hash') then
25+
values[#values+1] = redis.call('hgetall',v);
26+
end
27+
end
28+
return values;
29+
LUA;
30+
}
31+
32+
public function format($data): array
33+
{
34+
$result = [];
35+
foreach ($data ?? [] as $item) {
36+
if (! empty($item) && is_array($item)) {
37+
$temp = [];
38+
$count = count($item);
39+
for ($i = 0; $i < $count; ++$i) {
40+
$temp[$item[$i]] = $item[++$i];
41+
}
42+
43+
$result[] = $temp;
44+
}
45+
}
46+
47+
return $result;
48+
}
49+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://doc.hyperf.io
9+
* @contact [email protected]
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace Hyperf\Redis\Lua\Hash;
14+
15+
use Hyperf\Redis\Lua\Script;
16+
17+
class HIncrByFloatIfExists extends Script
18+
{
19+
public function getScript(): string
20+
{
21+
return <<<'LUA'
22+
if(redis.call('type', KEYS[1]).ok == 'hash') then
23+
return redis.call('HINCRBYFLOAT', KEYS[1], ARGV[1], ARGV[2]);
24+
end
25+
return "";
26+
LUA;
27+
}
28+
29+
/**
30+
* @param null|float $data
31+
* @return null|float
32+
*/
33+
public function format($data)
34+
{
35+
if (is_numeric($data)) {
36+
return $data;
37+
}
38+
return null;
39+
}
40+
41+
protected function getKeyNumber(array $arguments): int
42+
{
43+
return 1;
44+
}
45+
}

src/Lua/Script.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://doc.hyperf.io
9+
* @contact [email protected]
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace Hyperf\Redis\Lua;
14+
15+
use Hyperf\Contract\StdoutLoggerInterface;
16+
use Hyperf\Redis\Exception\RedisNotFoundException;
17+
use Psr\Container\ContainerInterface;
18+
19+
abstract class Script implements ScriptInterface
20+
{
21+
/**
22+
* PHPRedis client or proxy client.
23+
* @var mixed|\Redis
24+
*/
25+
protected $redis;
26+
27+
/**
28+
* @var string
29+
*/
30+
protected $sha;
31+
32+
/**
33+
* @var StdoutLoggerInterface
34+
*/
35+
protected $logger;
36+
37+
public function __construct(ContainerInterface $container)
38+
{
39+
if ($container->has(\Redis::class)) {
40+
$this->redis = $container->get(\Redis::class);
41+
}
42+
43+
if ($container->has(StdoutLoggerInterface::class)) {
44+
$this->logger = $container->get(StdoutLoggerInterface::class);
45+
}
46+
}
47+
48+
public function eval(array $arguments = [], $sha = true)
49+
{
50+
if ($this->redis === null) {
51+
throw new RedisNotFoundException('Redis client is not found.');
52+
}
53+
54+
if ($sha) {
55+
$result = $this->redis->evalSha($this->getSha(), $arguments, $this->getKeyNumber($arguments));
56+
if ($result !== false) {
57+
return $this->format($result);
58+
}
59+
60+
$this->sha = null;
61+
$this->logger && $this->logger->warning(sprintf('NOSCRIPT No matching script[%s]. Use EVAL instead.', static::class));
62+
}
63+
64+
$result = $this->redis->eval($this->getScript(), $arguments, $this->getKeyNumber($arguments));
65+
66+
return $this->format($result);
67+
}
68+
69+
protected function getKeyNumber(array $arguments): int
70+
{
71+
return count($arguments);
72+
}
73+
74+
protected function getSha(): string
75+
{
76+
if (! empty($this->sha)) {
77+
return $this->sha;
78+
}
79+
80+
return $this->sha = $this->redis->script('load', $this->getScript());
81+
}
82+
}

src/Lua/ScriptInterface.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://doc.hyperf.io
9+
* @contact [email protected]
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace Hyperf\Redis\Lua;
14+
15+
interface ScriptInterface
16+
{
17+
public function getScript(): string;
18+
19+
public function format($data);
20+
21+
public function eval(array $arguments = [], $sha = true);
22+
}

tests/Lua/EvalTest.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://doc.hyperf.io
9+
* @contact [email protected]
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace HyperfTest\Redis\Lua;
14+
15+
use Hyperf\Contract\StdoutLoggerInterface;
16+
use Hyperf\Utils\Str;
17+
use HyperfTest\Redis\Stub\ContainerStub;
18+
use HyperfTest\Redis\Stub\HGetAllMultipleStub;
19+
use Mockery;
20+
use PHPUnit\Framework\TestCase;
21+
22+
/**
23+
* @internal
24+
* @coversNothing
25+
*/
26+
class EvalTest extends TestCase
27+
{
28+
protected function tearDown()
29+
{
30+
$container = ContainerStub::mockContainer();
31+
$redis = $container->get(\Redis::class);
32+
$redis->flushDB();
33+
34+
Mockery::close();
35+
}
36+
37+
public function testEvalShaButNotExists()
38+
{
39+
$container = ContainerStub::mockContainer();
40+
$logger = $container->get(StdoutLoggerInterface::class);
41+
$logger->shouldReceive('warning')->once()->andReturnUsing(function ($message) {
42+
$this->assertSame('NOSCRIPT No matching script[HyperfTest\\Redis\\Stub\\HGetAllMultipleStub]. Use EVAL instead.', $message);
43+
});
44+
45+
$redis = $container->get(\Redis::class);
46+
$redis->hMSet('{hash}:1', ['id' => 1, 'name' => $name1 = 'Hyperf']);
47+
$redis->hMSet('{hash}:2', ['id' => 2, 'name' => $name2 = Str::random(16)]);
48+
49+
$script = new HGetAllMultipleStub($container);
50+
$result = $script->eval(['{hash}:2', '{hash}:1'], false);
51+
$this->assertEquals(['id' => 2, 'name' => $name2], array_shift($result));
52+
$this->assertEquals(['id' => 1, 'name' => $name1], array_shift($result));
53+
54+
$result = $script->eval(['{hash}:2', '{hash}:1']);
55+
$this->assertEquals(['id' => 2, 'name' => $name2], array_shift($result));
56+
$this->assertEquals(['id' => 1, 'name' => $name1], array_shift($result));
57+
}
58+
}

tests/Lua/HashTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://doc.hyperf.io
9+
* @contact [email protected]
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace HyperfTest\Redis\Lua;
14+
15+
use Hyperf\Redis\Lua\Hash\HGetAllMultiple;
16+
use Hyperf\Redis\Lua\Hash\HIncrByFloatIfExists;
17+
use Hyperf\Utils\Str;
18+
use HyperfTest\Redis\Stub\ContainerStub;
19+
use Mockery;
20+
use PHPUnit\Framework\TestCase;
21+
22+
/**
23+
* @internal
24+
* @coversNothing
25+
*/
26+
class HashTest extends TestCase
27+
{
28+
protected function tearDown()
29+
{
30+
$container = ContainerStub::mockContainer();
31+
$redis = $container->get(\Redis::class);
32+
$redis->flushDB();
33+
34+
Mockery::close();
35+
}
36+
37+
public function testEvalHGetAllMultiple()
38+
{
39+
$container = ContainerStub::mockContainer();
40+
$redis = $container->get(\Redis::class);
41+
$redis->hMSet('{hash}:1', ['id' => 1, 'name' => $name1 = 'Hyperf']);
42+
$redis->hMSet('{hash}:2', ['id' => 2, 'name' => $name2 = Str::random(16)]);
43+
$redis->hMSet('{hash}:3', ['id' => 3, 'name' => $name3 = uniqid()]);
44+
$script = new HGetAllMultiple($container);
45+
$result = $script->eval(['{hash}:1', '{hash}:2', '{hash}:3']);
46+
47+
$this->assertEquals(['id' => 1, 'name' => $name1], array_shift($result));
48+
$this->assertEquals(['id' => 2, 'name' => $name2], array_shift($result));
49+
$this->assertEquals(['id' => 3, 'name' => $name3], array_shift($result));
50+
$this->assertSame([], $result);
51+
}
52+
53+
public function testEvalHIncrByFloatIfExists()
54+
{
55+
$container = ContainerStub::mockContainer();
56+
$redis = $container->get(\Redis::class);
57+
$redis->hMSet('{hash}:1', ['id' => 1, 'name' => 'Hyperf', 'incr' => 0]);
58+
59+
$script = new HIncrByFloatIfExists($container);
60+
$this->assertEquals(2.2, $script->eval(['{hash}:1', 'incr', 2.2]));
61+
62+
$script = new HIncrByFloatIfExists($container);
63+
$this->assertSame(null, $script->eval(['{hash}:2', 'incr', 1]));
64+
}
65+
}

0 commit comments

Comments
 (0)