This commit is contained in:
Vladyslav Vildanov
2023-03-14 10:11:07 +02:00
9 changed files with 151 additions and 173 deletions
+40 -9
View File
@@ -12,8 +12,8 @@
namespace Predis\Command;
use Predis\ClientConfiguration;
use Predis\Command\Redis\FUNCTIONS;
use Predis\Command\Resolver\CommandResolverInterface;
/**
* Command factory for mainline Redis servers.
@@ -26,12 +26,9 @@ use Predis\Command\Resolver\CommandResolverInterface;
*/
class RedisFactory extends Factory
{
/**
* @var CommandResolverInterface
*/
private $commandResolver;
private const COMMANDS_NAMESPACE = "Predis\Command\Redis";
public function __construct(CommandResolverInterface $commandResolver)
public function __construct()
{
$this->commands = [
'ECHO' => 'Predis\Command\Redis\ECHO_',
@@ -40,8 +37,6 @@ class RedisFactory extends Factory
// Class name corresponds to PHP reserved word "function", added mapping to bypass restrictions
'FUNCTION' => FUNCTIONS::class,
];
$this->commandResolver = $commandResolver;
}
/**
@@ -55,7 +50,7 @@ class RedisFactory extends Factory
return $this->commands[$commandID];
}
$commandClass = $this->commandResolver->resolve($commandID);
$commandClass = $this->resolve($commandID);
if (null === $commandClass) {
return null;
@@ -78,4 +73,40 @@ class RedisFactory extends Factory
// details of the implementation of this mechanism.
$this->commands[strtoupper($commandID)] = null;
}
/**
* Resolves command object from given command ID.
*
* @param string $commandID Command ID of virtual method call
* @return string|null FQDN of corresponding command object
*/
private function resolve(string $commandID): ?string
{
if (class_exists($commandClass = self::COMMANDS_NAMESPACE . '\\' . $commandID)) {
return $commandClass;
}
$commandModule = $this->resolveCommandModuleByPrefix($commandID);
if (null === $commandModule) {
return null;
}
if (class_exists($commandClass = self::COMMANDS_NAMESPACE . '\\' . $commandModule . '\\' . $commandID)) {
return $commandClass;
}
return null;
}
private function resolveCommandModuleByPrefix(string $commandID): ?string
{
foreach (ClientConfiguration::getModules() as $module) {
if (preg_match("/^{$module['commandPrefix']}/", $commandID)) {
return $module['name'];
}
}
return null;
}
}
-63
View File
@@ -1,63 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Resolver;
use Predis\ClientConfiguration;
class CommandResolver implements CommandResolverInterface
{
private const COMMANDS_NAMESPACE = "Predis\Command\Redis";
/**
* @var array
*/
private $modules;
public function __construct()
{
$this->modules = ClientConfiguration::getModules();
}
/**
* {@inheritDoc}
*/
public function resolve(string $commandID): ?string
{
if (class_exists($commandClass = self::COMMANDS_NAMESPACE . '\\' . $commandID)) {
return $commandClass;
}
$commandModule = $this->resolveCommandModuleByPrefix($commandID);
if (null === $commandModule) {
return null;
}
if (class_exists($commandClass = self::COMMANDS_NAMESPACE . '\\' . $commandModule . '\\' . $commandID)) {
return $commandClass;
}
return null;
}
private function resolveCommandModuleByPrefix(string $commandID): ?string
{
foreach ($this->modules as $module) {
if (preg_match("/^{$module['commandPrefix']}/", $commandID)) {
return $module['name'];
}
}
return null;
}
}
@@ -1,24 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Resolver;
interface CommandResolverInterface
{
/**
* Resolves command object from given command ID.
*
* @param string $commandID Command ID of virtual method call
* @return string FQDN of corresponding command object
*/
public function resolve(string $commandID): ?string;
}
+1 -2
View File
@@ -16,7 +16,6 @@ use InvalidArgumentException;
use Predis\Command\FactoryInterface;
use Predis\Command\RawFactory;
use Predis\Command\RedisFactory;
use Predis\Command\Resolver\CommandResolver;
use Predis\Configuration\OptionInterface;
use Predis\Configuration\OptionsInterface;
@@ -136,7 +135,7 @@ class Commands implements OptionInterface
*/
public function getDefault(OptionsInterface $options)
{
$commands = new RedisFactory(new CommandResolver());
$commands = new RedisFactory();
if (isset($options->prefix)) {
$commands->setProcessor($options->prefix);
+2 -4
View File
@@ -167,9 +167,7 @@ abstract class PredisTestCase extends \PHPUnit\Framework\TestCase
protected function getDefaultOptionsArray(): array
{
return [
'commands' => new Command\RedisFactory(
new Command\Resolver\CommandResolver()
),
'commands' => new Command\RedisFactory(),
];
}
@@ -209,7 +207,7 @@ abstract class PredisTestCase extends \PHPUnit\Framework\TestCase
*/
protected function getCommandFactory(): Command\Factory
{
return new Command\RedisFactory(new Command\Resolver\CommandResolver());
return new Command\RedisFactory();
}
/**
@@ -60,21 +60,19 @@ class JSONDEBUG_Test extends PredisCommandTestCase
* @param array $jsonArguments
* @param string $key
* @param string $path
* @param array $expectedMemoryUsage
* @return void
* @requiresRedisJsonVersion >= 1.0.0
*/
public function testMemoryReturnsCorrectMemoryUsageAboutJson(
array $jsonArguments,
string $key,
string $path,
array $expectedMemoryUsage
string $path
): void {
$redis = $this->getClient();
$redis->jsonset(...$jsonArguments);
$this->assertSame($expectedMemoryUsage, $redis->jsondebug->memory($key, $path));
$this->assertGreaterThan(0, $redis->jsondebug->memory($key, $path));
}
/**
@@ -99,31 +97,26 @@ class JSONDEBUG_Test extends PredisCommandTestCase
['key', '$', '{"key1":"value1","key2":"value2"}'],
'key',
'$',
[44],
],
'on nested level' => [
['key', '$', '{"key1":{"key2":"value2"}}'],
'key',
'$..key2',
[14],
],
'with same keys on both levels' => [
['key', '$', '{"key1":{"key2":"value2"},"key2":"value2"}'],
'key',
'$..key2',
[14, 14],
],
'with wrong key' => [
['key', '$', '{"key1":{"key2":"value2"}}'],
'key1',
'$',
[],
],
'with wrong path' => [
['key', '$', '{"key1":{"key2":"value2"}}'],
'key',
'$.key3',
[],
],
];
}
+105 -17
View File
@@ -14,7 +14,6 @@ namespace Predis\Command;
use Predis\Command\Processor\ProcessorChain;
use Predis\Command\Processor\ProcessorInterface;
use Predis\Command\Resolver\CommandResolver;
use PredisTestCase;
class RedisFactoryTest extends PredisTestCase
@@ -24,7 +23,7 @@ class RedisFactoryTest extends PredisTestCase
*/
public function testSupportedCommands(): void
{
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
foreach ($this->getExpectedCommands() as $commandID) {
$this->assertTrue($factory->supports($commandID), "Command factory does not support $commandID");
@@ -36,7 +35,7 @@ class RedisFactoryTest extends PredisTestCase
*/
public function testSupportCommand(): void
{
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$this->assertTrue($factory->supports('info'));
$this->assertTrue($factory->supports('INFO'));
@@ -50,7 +49,7 @@ class RedisFactoryTest extends PredisTestCase
*/
public function testSupportCommands(): void
{
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$this->assertTrue($factory->supports('get', 'set'));
$this->assertTrue($factory->supports('GET', 'SET'));
@@ -65,7 +64,7 @@ class RedisFactoryTest extends PredisTestCase
*/
public function testGetCommandClass(): void
{
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$this->assertSame('Predis\Command\Redis\PING', $factory->getCommandClass('ping'));
$this->assertSame('Predis\Command\Redis\PING', $factory->getCommandClass('PING'));
@@ -79,7 +78,7 @@ class RedisFactoryTest extends PredisTestCase
*/
public function testDefineCommand(): void
{
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$command = $this->getMockBuilder('Predis\Command\CommandInterface')
->getMock();
@@ -97,7 +96,7 @@ class RedisFactoryTest extends PredisTestCase
*/
public function testUndefineCommandInClassAutoload(): void
{
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$this->assertTrue($factory->supports('PING'));
$this->assertSame('Predis\Command\Redis\PING', $factory->getCommandClass('PING'));
@@ -113,7 +112,7 @@ class RedisFactoryTest extends PredisTestCase
*/
public function testUndefineCommandInClassMap(): void
{
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$commandClass = get_class($this->getMockBuilder('Predis\Command\CommandInterface')->getMock());
$factory->define('MOCK', $commandClass);
@@ -135,7 +134,7 @@ class RedisFactoryTest extends PredisTestCase
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage("Class stdClass must implement Predis\Command\CommandInterface");
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$factory->define('mock', 'stdClass');
}
@@ -145,7 +144,7 @@ class RedisFactoryTest extends PredisTestCase
*/
public function testCreateCommandWithoutArguments(): void
{
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$command = $factory->create('info');
@@ -159,7 +158,7 @@ class RedisFactoryTest extends PredisTestCase
*/
public function testCreateCommandWithArguments(): void
{
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$arguments = ['foo', 'bar'];
$command = $factory->create('set', $arguments);
@@ -177,7 +176,7 @@ class RedisFactoryTest extends PredisTestCase
$this->expectException('Predis\ClientException');
$this->expectExceptionMessage('Command `UNKNOWN` is not a registered Redis command.');
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$factory->create('unknown');
}
@@ -187,7 +186,7 @@ class RedisFactoryTest extends PredisTestCase
*/
public function testGetDefaultProcessor(): void
{
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$this->assertNull($factory->getProcessor());
}
@@ -202,7 +201,7 @@ class RedisFactoryTest extends PredisTestCase
->getMockBuilder('Predis\Command\Processor\ProcessorInterface')
->getMock();
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$factory->setProcessor($processor);
$this->assertSame($processor, $factory->getProcessor());
@@ -218,7 +217,7 @@ class RedisFactoryTest extends PredisTestCase
->getMockBuilder('Predis\Command\Processor\ProcessorInterface')
->getMock();
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$factory->setProcessor($processor);
$this->assertSame($processor, $factory->getProcessor());
@@ -250,7 +249,7 @@ class RedisFactoryTest extends PredisTestCase
}
);
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$factory->setProcessor($processor);
$factory->create('set', ['foo', 'bar']);
@@ -274,7 +273,7 @@ class RedisFactoryTest extends PredisTestCase
$chain->add($processor);
$chain->add($processor);
$factory = new RedisFactory(new CommandResolver());
$factory = new RedisFactory();
$factory->setProcessor($chain);
$factory->create('info');
@@ -451,6 +450,95 @@ class RedisFactoryTest extends PredisTestCase
156 => 'GEODIST',
157 => 'GEORADIUS',
158 => 'GEORADIUSBYMEMBER',
159 => 'JSONSET',
160 => 'JSONGET',
161 => 'JSONARRAPPEND',
162 => 'JSONARRINDEX',
163 => 'JSONARRINSERT',
164 => 'JSONARRLEN',
165 => 'JSONARRPOP',
166 => 'JSONARRTRIM',
167 => 'JSONCLEAR',
168 => 'JSONDEBUG',
169 => 'JSONDEL',
170 => 'JSONFORGET',
171 => 'JSONMGET',
172 => 'JSONNUMINCRBY',
173 => 'JSONOBJKEYS',
174 => 'JSONOBJLEN',
175 => 'JSONRESP',
176 => 'JSONSTRAPPEND',
177 => 'JSONSTRLEN',
178 => 'JSONTOGGLE',
179 => 'JSONTYPE',
180 => 'BFADD',
181 => 'BFEXISTS',
182 => 'BFINFO',
183 => 'BFINSERT',
184 => 'BFLOADCHUNK',
185 => 'BFMADD',
186 => 'BFMEXISTS',
187 => 'BFRESERVE',
188 => 'BFSCANDUMP',
189 => 'CMSINCRBY',
190 => 'CMSINFO',
191 => 'CMSINITBYDIM',
192 => 'CMSINITBYPROB',
193 => 'CMSMERGE',
194 => 'CMSQUERY',
195 => 'CFADD',
196 => 'CFADDNX',
197 => 'CFCOUNT',
198 => 'CFDEL',
199 => 'CFEXISTS',
200 => 'CFINFO',
201 => 'CFINSERT',
202 => 'CFINSERTNX',
203 => 'CFLOADCHUNK',
204 => 'CFMEXISTS',
205 => 'CFRESERVE',
206 => 'CFSCANDUMP',
207 => 'TDIGESTADD',
208 => 'TDIGESTBYRANK',
209 => 'TDIGESTBYREVRANK',
210 => 'TDIGESTCDF',
211 => 'TDIGESTCREATE',
212 => 'TDIGESTINFO',
213 => 'TDIGESTMAX',
214 => 'TDIGESTMERGE',
215 => 'TDIGESTMIN',
216 => 'TDIGESTQUANTILE',
217 => 'TDIGESTRANK',
218 => 'TDIGESTRESET',
219 => 'TDIGESTREVRANK',
220 => 'TDIGESTTRIMMED_MEAN',
221 => 'TOPKADD',
222 => 'TOPKINCRBY',
223 => 'TOPKINFO',
224 => 'TOPKLIST',
225 => 'TOPKQUERY',
226 => 'TOPKRESERVE',
227 => 'FTALIASADD',
228 => 'FTALIASDEL',
229 => 'FTALIASUPDATE',
230 => 'FTALTER',
231 => 'FTCONFIG',
232 => 'FTCREATE',
233 => 'FTDICTADD',
234 => 'FTDICTDEL',
235 => 'FTDICTDUMP',
236 => 'FTDROPINDEX',
237 => 'FTINFO',
238 => 'FTPROFILE',
239 => 'FTSEARCH',
240 => 'FTSPELLCHECK',
241 => 'FTSUGADD',
242 => 'FTSUGDEL',
243 => 'FTSUGGET',
244 => 'FTSUGLEN',
245 => 'FTSYNDUMP',
246 => 'FTSYNUPDATE',
247 => 'FTTAGVALS',
];
}
}
@@ -1,43 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Resolver;
use PHPUnit\Framework\TestCase;
use Predis\Command\Redis\SET;
class CommandResolverTest extends TestCase
{
/**
* @dataProvider commandsProvider
* @param string $commandID
* @param string|null $expectedCommandClass
* @return void
*/
public function testResolveResolvesCorrectlyCommand(
string $commandID,
?string $expectedCommandClass
): void {
$resolver = new CommandResolver();
$this->assertSame($expectedCommandClass, $resolver->resolve($commandID));
}
public function commandsProvider(): array
{
return [
'core command exists' => ['SET', SET::class],
'module not exist' => ['FOOBAR', null],
'module exists, module command not exists' => ['JSONFOO', null],
];
}
}
@@ -15,7 +15,6 @@ namespace Predis\Configuration\Option;
use PHPUnit\Framework\MockObject\MockObject;
use Predis\Command\Processor\KeyPrefixProcessor;
use Predis\Command\RedisFactory;
use Predis\Command\Resolver\CommandResolver;
use Predis\Configuration\OptionsInterface;
use PredisTestCase;
use stdClass;
@@ -77,7 +76,7 @@ class CommandsTest extends PredisTestCase
/** @var OptionsInterface */
$options = $this->getMockBuilder('Predis\Configuration\OptionsInterface')->getMock();
$input = new RedisFactory(new CommandResolver());
$input = new RedisFactory();
$commands = $option->filter($options, $input);