Skip to content

Commit ec755b8

Browse files
authored
Merge pull request #26 from ethanhann/feature/low_level_client_adapter
Add Redis client adapters
2 parents bcbf62d + 35bab15 commit ec755b8

18 files changed

+346
-212
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"predis/predis": "^1.1",
3232
"friendsofphp/php-cs-fixer": "^2.2",
3333
"consolidation/robo": "^1.0",
34-
"monolog/monolog": "^1.23"
34+
"monolog/monolog": "^1.23",
35+
"cheprasov/php-redis-client": "^1.8"
3536
}
3637
}

phpunit.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
</whitelist>
2424
</filter>
2525
<php>
26-
<env name="REDIS_LIBRARY" value="Predis" />
26+
<env name="REDIS_LIBRARY" value="RedisClient" />
2727
<env name="REDIS_HOST" value="localhost" />
2828
<env name="REDIS_PORT" value="6379" />
29-
<env name="REDIS_DB" value="15" />
29+
<env name="REDIS_DB" value="0" />
3030
<env name="LOG_FILE" value="./tests.log" />
3131
<env name="IS_LOGGING_ENABLED" value="true" />
3232
</php>

src/AbstractIndex.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22

33
namespace Ehann\RediSearch;
44

5-
use Ehann\RediSearch\Redis\RedisClient;
5+
use Ehann\RediSearch\Redis\RedisClientInterface;
66

77
abstract class AbstractIndex
88
{
9-
/** @var RedisClient */
9+
/** @var RedisClientInterface */
1010
protected $redisClient;
1111
/** @var string */
1212
protected $indexName;
1313

14-
public function __construct(RedisClient $redisClient = null, string $indexName = '')
14+
public function __construct(RedisClientInterface $redisClient = null, string $indexName = '')
1515
{
16-
$this->redisClient = $redisClient ?? new RedisClient();
16+
$this->redisClient = $redisClient;
1717
$this->indexName = $indexName;
1818
}
1919
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Ehann\RediSearch\Exceptions;
4+
5+
use Exception;
6+
7+
class RawCommandErrorException extends Exception
8+
{
9+
public function __construct($message = '', $code = 0, Exception $previous = null)
10+
{
11+
parent::__construct(trim("The Redis client threw an exception. See the inner exception for details. $message"), $code, $previous);
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Ehann\RediSearch\Exceptions;
4+
5+
use Exception;
6+
7+
class UnsupportedRedisDatabaseException extends Exception
8+
{
9+
public function __construct($message = '', $code = 0, Exception $previous = null)
10+
{
11+
parent::__construct(trim("Only database 0 is supported by the RediSearch module. $message"), $code, $previous);
12+
}
13+
}

src/Index.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
use Ehann\RediSearch\Query\Builder as QueryBuilder;
1313
use Ehann\RediSearch\Query\BuilderInterface as QueryBuilderInterface;
1414
use Ehann\RediSearch\Query\SearchResult;
15-
use Ehann\RediSearch\Redis\RedisClient;
15+
use Ehann\RediSearch\Redis\RedisClientInterface;
1616

1717
class Index extends AbstractIndex implements IndexInterface
1818
{
@@ -143,6 +143,7 @@ protected function rawCommand(string $command, array $arguments)
143143
/**
144144
* @param null $id
145145
* @return DocumentInterface
146+
* @throws Exceptions\FieldNotInSchemaException
146147
*/
147148
public function makeDocument($id = null): DocumentInterface
148149
{
@@ -152,18 +153,18 @@ public function makeDocument($id = null): DocumentInterface
152153
}
153154

154155
/**
155-
* @return RedisClient
156+
* @return RedisClientInterface
156157
*/
157-
public function getRedisClient(): RedisClient
158+
public function getRedisClient(): RedisClientInterface
158159
{
159160
return $this->redisClient;
160161
}
161162

162163
/**
163-
* @param RedisClient $redisClient
164+
* @param RedisClientInterface $redisClient
164165
* @return IndexInterface
165166
*/
166-
public function setRedisClient(RedisClient $redisClient): IndexInterface
167+
public function setRedisClient(RedisClientInterface $redisClient): IndexInterface
167168
{
168169
$this->redisClient = $redisClient;
169170
return $this;
@@ -297,6 +298,7 @@ public function explain(string $query): string
297298
* @param string $query
298299
* @param bool $documentsAsArray
299300
* @return SearchResult
301+
* @throws Exceptions\RedisRawCommandException
300302
*/
301303
public function search(string $query, bool $documentsAsArray = false): SearchResult
302304
{
@@ -415,6 +417,7 @@ protected function _add(DocumentInterface $document)
415417
/**
416418
* @param $document
417419
* @return bool
420+
* @throws Exceptions\FieldNotInSchemaException
418421
*/
419422
public function add($document): bool
420423
{
@@ -427,6 +430,7 @@ public function add($document): bool
427430
/**
428431
* @param $document
429432
* @return bool
433+
* @throws Exceptions\FieldNotInSchemaException
430434
*/
431435
public function replace($document): bool
432436
{

src/IndexInterface.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Ehann\RediSearch\Document\DocumentInterface;
66
use Ehann\RediSearch\Query\BuilderInterface;
7-
use Ehann\RediSearch\Redis\RedisClient;
7+
use Ehann\RediSearch\Redis\RedisClientInterface;
88

99
interface IndexInterface extends BuilderInterface
1010
{
@@ -13,8 +13,8 @@ public function drop();
1313
public function info();
1414
public function delete($id);
1515
public function makeDocument($id = null): DocumentInterface;
16-
public function getRedisClient(): RedisClient;
17-
public function setRedisClient(RedisClient $redisClient): IndexInterface;
16+
public function getRedisClient(): RedisClientInterface;
17+
public function setRedisClient(RedisClientInterface $redisClient): IndexInterface;
1818
public function getIndexName(): string;
1919
public function setIndexName(string $indexName): IndexInterface;
2020
public function isNoOffsetsEnabled(): bool;

src/Query/Builder.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace Ehann\RediSearch\Query;
44

55
use Ehann\RediSearch\Exceptions\RedisRawCommandException;
6-
use Ehann\RediSearch\Redis\RedisClient;
6+
use Ehann\RediSearch\Redis\RedisClientInterface;
77
use InvalidArgumentException;
88

99
class Builder implements BuilderInterface
@@ -28,7 +28,7 @@ class Builder implements BuilderInterface
2828
/** @var string */
2929
private $indexName;
3030

31-
public function __construct(RedisClient $redis, string $indexName)
31+
public function __construct(RedisClientInterface $redis, string $indexName)
3232
{
3333
$this->redis = $redis;
3434
$this->indexName = $indexName;

src/Redis/AbstractRedisClient.php

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
namespace Ehann\RediSearch\Redis;
4+
5+
use Ehann\RediSearch\Exceptions\RawCommandErrorException;
6+
use Ehann\RediSearch\Exceptions\UnknownIndexNameException;
7+
use Ehann\RediSearch\Exceptions\UnknownRediSearchCommandException;
8+
use Ehann\RediSearch\Exceptions\UnsupportedLanguageException;
9+
use Ehann\RediSearch\Exceptions\UnsupportedRedisDatabaseException;
10+
use Exception;
11+
use Psr\Log\LoggerInterface;
12+
13+
abstract class AbstractRedisClient implements RedisClientInterface
14+
{
15+
public $redis;
16+
/** @var LoggerInterface */
17+
protected $logger;
18+
19+
public function connect($hostname = '127.0.0.1', $port = 6379, $db = 0, $password = null): RedisClientInterface
20+
{
21+
return $this;
22+
}
23+
24+
public function flushAll()
25+
{
26+
$this->redis->flushAll();
27+
}
28+
29+
public function multi(bool $usePipeline = false)
30+
{
31+
}
32+
33+
public function rawCommand(string $command, array $arguments)
34+
{
35+
}
36+
37+
public function prepareRawCommandArguments(string $command, array $arguments) : array
38+
{
39+
foreach ($arguments as $index => $argument) {
40+
if (!is_scalar($arguments[$index])) {
41+
$arguments[$index] = (string)$argument;
42+
}
43+
}
44+
array_unshift($arguments, $command);
45+
if ($this->logger) {
46+
$this->logger->debug(implode(' ', $arguments));
47+
}
48+
return $arguments;
49+
}
50+
51+
/**
52+
* @param $payload
53+
* @return mixed
54+
* @throws RawCommandErrorException
55+
* @throws UnknownIndexNameException
56+
* @throws UnknownRediSearchCommandException
57+
* @throws UnsupportedLanguageException
58+
* @throws UnsupportedRedisDatabaseException
59+
*/
60+
public function validateRawCommandResults($payload)
61+
{
62+
$isPayloadException = $payload instanceof Exception;
63+
$message = $isPayloadException ? $payload->getMessage() : $payload;
64+
65+
if (!is_string($message)) {
66+
return;
67+
}
68+
if ($message === 'Cannot create index on db != 0') {
69+
throw new UnsupportedRedisDatabaseException();
70+
}
71+
if ($message === 'Unknown Index name') {
72+
throw new UnknownIndexNameException();
73+
}
74+
if (in_array($message, ['Unsupported Language', 'Unsupported Stemmer Language'])) {
75+
throw new UnsupportedLanguageException();
76+
}
77+
if (strpos($message, 'ERR unknown command \'FT.') !== false) {
78+
throw new UnknownRediSearchCommandException($message);
79+
}
80+
if ($isPayloadException) {
81+
throw new RawCommandErrorException('', 0, $payload);
82+
}
83+
}
84+
85+
public function normalizeRawCommandResult($rawResult)
86+
{
87+
return $rawResult === 'OK' ? true : $rawResult;
88+
}
89+
90+
public function setLogger(LoggerInterface $logger): RedisClientInterface
91+
{
92+
$this->logger = $logger;
93+
return $this;
94+
}
95+
}

src/Redis/PhpRedisAdapter.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace Ehann\RediSearch\Redis;
4+
5+
use Redis;
6+
use RedisException;
7+
8+
/**
9+
* Class PhpRedisAdapter
10+
* @package Ehann\RediSearch\Redis
11+
*
12+
* This class wraps the PhpRedis client: https://github.com/phpredis/phpredis
13+
*/
14+
class PhpRedisAdapter extends AbstractRedisClient
15+
{
16+
/** @var Redis */
17+
public $redis;
18+
19+
public function connect($hostname = '127.0.0.1', $port = 6379, $db = 0, $password = null): RedisClientInterface
20+
{
21+
$this->redis = new Redis();
22+
$this->redis->connect($hostname, $port);
23+
$this->redis->select($db);
24+
$this->redis->auth($password);
25+
return $this;
26+
}
27+
28+
public function multi(bool $usePipeline = false)
29+
{
30+
return $this->redis->multi($usePipeline ? Redis::PIPELINE : Redis::MULTI);
31+
}
32+
33+
public function rawCommand(string $command, array $arguments)
34+
{
35+
$arguments = $this->prepareRawCommandArguments($command, $arguments);
36+
$rawResult = null;
37+
try {
38+
$rawResult = call_user_func_array([$this->redis, 'rawCommand'], $arguments);
39+
} catch (RedisException $exception) {
40+
$this->validateRawCommandResults($exception);
41+
}
42+
return $this->normalizeRawCommandResult($rawResult);
43+
}
44+
}

src/Redis/PredisAdapter.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Ehann\RediSearch\Redis;
4+
5+
use Predis\Client;
6+
7+
/**
8+
* Class PredisAdapter
9+
* @package Ehann\RediSearch\Redis
10+
*
11+
* This class wraps the NRK client: https://github.com/nrk/predis
12+
*/
13+
class PredisAdapter extends AbstractRedisClient
14+
{
15+
/** @var Client */
16+
public $redis;
17+
18+
public function connect($hostname = '127.0.0.1', $port = 6379, $db = 0, $password = null): RedisClientInterface
19+
{
20+
$this->redis = new Client([
21+
'scheme' => 'tcp',
22+
'host' => $hostname,
23+
'port' => $port,
24+
'database' => $db,
25+
'password' => $password,
26+
]);
27+
$this->redis->connect();
28+
return $this;
29+
}
30+
31+
public function multi(bool $usePipeline = false)
32+
{
33+
return $this->redis->pipeline();
34+
}
35+
36+
public function rawCommand(string $command, array $arguments)
37+
{
38+
$preparedArguments = $this->prepareRawCommandArguments($command, $arguments);
39+
$rawResult = $this->redis->executeRaw($preparedArguments);
40+
$this->validateRawCommandResults($rawResult);
41+
return $this->normalizeRawCommandResult($rawResult);
42+
}
43+
}

0 commit comments

Comments
 (0)