mirror of
https://github.com/predis/predis.git
synced 2026-08-22 11:01:02 +00:00
Extended Key Space support by implementing COPY command (#866)
* Added support for COPY command * Revert getCLient signature changes, used createClient instead Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
This commit is contained in:
committed by
GitHub
parent
b47aa7ad3a
commit
b8cc7c643a
@@ -18,6 +18,7 @@ use Predis\Command\CommandInterface;
|
||||
/**
|
||||
* Interface defining a client-side context such as a pipeline or transaction.
|
||||
*
|
||||
* @method $this copy(string $source, string $destination, int $db = -1, bool $replace = false)
|
||||
* @method $this del(array|string $keys)
|
||||
* @method $this dump($key)
|
||||
* @method $this exists($key)
|
||||
|
||||
@@ -27,6 +27,7 @@ use Predis\Response\Status;
|
||||
* and more friendly interface to ease programming which is described in the
|
||||
* following list of methods:
|
||||
*
|
||||
* @method int copy(string $source, string $destination, int $db = -1, bool $replace = false)
|
||||
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method string|null dump(string $key)
|
||||
* @method int exists(string $key)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
use Predis\Command\Traits\DB;
|
||||
use Predis\Command\Traits\Replace;
|
||||
|
||||
/**
|
||||
* @link https://redis.io/commands/copy/
|
||||
*
|
||||
* This command copies the value stored at the source key to the destination key.
|
||||
*/
|
||||
class COPY extends RedisCommand
|
||||
{
|
||||
use DB {
|
||||
DB::setArguments as setDB;
|
||||
}
|
||||
use Replace {
|
||||
Replace::setArguments as setReplace;
|
||||
}
|
||||
|
||||
protected static $dbArgumentPositionOffset = 2;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return 'COPY';
|
||||
}
|
||||
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$this->setDB($arguments);
|
||||
$arguments = $this->getArguments();
|
||||
|
||||
$this->setReplace($arguments);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Predis\Command\Traits;
|
||||
|
||||
use UnexpectedValueException;
|
||||
|
||||
trait DB
|
||||
{
|
||||
private $dbModifier = 'DB';
|
||||
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$argumentsLength = count($arguments);
|
||||
|
||||
if (static::$dbArgumentPositionOffset >= $argumentsLength) {
|
||||
parent::setArguments($arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_numeric($arguments[static::$dbArgumentPositionOffset])) {
|
||||
throw new UnexpectedValueException('DB argument should be a valid numeric value');
|
||||
}
|
||||
|
||||
if ($arguments[static::$dbArgumentPositionOffset] < 0) {
|
||||
array_splice($arguments, static::$dbArgumentPositionOffset, 1);
|
||||
parent::setArguments($arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
$argument = $arguments[static::$dbArgumentPositionOffset];
|
||||
$argumentsBefore = array_slice($arguments, 0, static::$dbArgumentPositionOffset);
|
||||
$argumentsAfter = array_slice($arguments, static::$dbArgumentPositionOffset + 1);
|
||||
|
||||
parent::setArguments(array_merge(
|
||||
$argumentsBefore,
|
||||
[$this->dbModifier],
|
||||
[$argument],
|
||||
$argumentsAfter
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Predis\Command\Traits;
|
||||
|
||||
use Predis\Command\Command;
|
||||
|
||||
/**
|
||||
* @mixin Command
|
||||
*/
|
||||
trait Replace
|
||||
{
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$replace = array_pop($arguments);
|
||||
|
||||
if (is_bool($replace) && $replace) {
|
||||
$arguments[] = 'REPLACE';
|
||||
} else if (!is_bool($replace)) {
|
||||
$arguments[] = $replace;
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,6 @@ abstract class PredisCommandTestCase extends PredisTestCase
|
||||
* Returns a new client instance.
|
||||
*
|
||||
* @param bool $flushdb Flush selected database before returning the client
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function getClient(bool $flushdb = true): Client
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
class COPY_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return COPY::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'COPY';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @dataProvider argumentsProvider
|
||||
*/
|
||||
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
|
||||
{
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($actualArguments);
|
||||
|
||||
$this->assertSame($expectedArguments, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse(): void
|
||||
{
|
||||
$this->assertSame(1, $this->getCommand()->parseResponse(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testSuccessfullyCopyValueOnNonExistingDestinationKey(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$redis->set('key', 'value');
|
||||
|
||||
$actualResponse = $redis->copy('key', 'destination');
|
||||
|
||||
$this->assertSame(1, $actualResponse);
|
||||
$this->assertSame($redis->get('key'), $redis->get('destination'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testSuccessfullyCopyValueFromSourceToAnotherDb(): void
|
||||
{
|
||||
$defaultDatabaseIndexClient = $this->getClient();
|
||||
$defaultDatabaseIndexClient->set('key', 'value');
|
||||
|
||||
$copyResponse = $defaultDatabaseIndexClient->copy('key', 'new_key', 14);
|
||||
|
||||
$anotherDatabaseIndexClient = $this->createClient(['database' => 14], null, false);
|
||||
$actualValue = $anotherDatabaseIndexClient->get('new_key');
|
||||
$anotherDatabaseIndexClient->flushdb();
|
||||
|
||||
$this->assertNull($anotherDatabaseIndexClient->get('new_key'));
|
||||
$this->assertSame(1, $copyResponse);
|
||||
$this->assertSame('value', $actualValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testDoNotCopyValueOnAlreadyExistingDestinationKey(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$redis->set('key', 'value');
|
||||
$redis->set('destination', 'destination_value');
|
||||
|
||||
$actualResponse = $redis->copy('key', 'destination');
|
||||
|
||||
$this->assertSame(0, $actualResponse);
|
||||
$this->assertSame('destination_value', $redis->get('destination'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testSuccessfullyCopyValueWithReplaceArgumentOnAlreadyExistingDestinationKey(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$redis->set('key', 'value');
|
||||
$redis->set('destination', 'destination_value');
|
||||
|
||||
$actualResponse = $redis->copy('key', 'destination', -1, true);
|
||||
|
||||
$this->assertSame(1, $actualResponse);
|
||||
$this->assertSame('value', $redis->get('destination'));
|
||||
}
|
||||
|
||||
public function argumentsProvider(): array
|
||||
{
|
||||
return [
|
||||
'with default arguments' => [
|
||||
['source', 'destination'],
|
||||
['source', 'destination']
|
||||
],
|
||||
'with DB argument' => [
|
||||
['source', 'destination', 1],
|
||||
['source', 'destination', 'DB', 1]
|
||||
],
|
||||
'with replace argument' => [
|
||||
['source', 'destination', -1, true],
|
||||
['source', 'destination', 'REPLACE']
|
||||
],
|
||||
'with all arguments' => [
|
||||
['source', 'destination', 1, true],
|
||||
['source', 'destination', 'DB', 1, 'REPLACE']
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Predis\Command\Traits;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
use PredisTestCase;
|
||||
use UnexpectedValueException;
|
||||
|
||||
class DbTest extends PredisTestCase
|
||||
{
|
||||
private $testClass;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->testClass = new class extends RedisCommand {
|
||||
use DB;
|
||||
|
||||
public static $dbArgumentPositionOffset = 0;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return 'test';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider argumentsProvider
|
||||
* @param int $offset
|
||||
* @param array $arguments
|
||||
* @param array $expectedResponse
|
||||
* @return void
|
||||
*/
|
||||
public function testReturnsCorrectArguments(int $offset, array $arguments, array $expectedResponse): void
|
||||
{
|
||||
$this->testClass::$dbArgumentPositionOffset = $offset;
|
||||
|
||||
$this->testClass->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expectedResponse, $this->testClass->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testThrowsErrorOnUnexpectedValueGiven(): void
|
||||
{
|
||||
$this->testClass::$dbArgumentPositionOffset = 0;
|
||||
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage('DB argument should be a valid numeric value');
|
||||
|
||||
$this->testClass->setArguments(['wrong']);
|
||||
}
|
||||
|
||||
public function argumentsProvider(): array
|
||||
{
|
||||
return [
|
||||
'with positive integer db argument' => [
|
||||
0,
|
||||
[1],
|
||||
['DB', 1]
|
||||
],
|
||||
'with wrong offset' => [
|
||||
1,
|
||||
[1],
|
||||
[1]
|
||||
],
|
||||
'with negative integer db argument' => [
|
||||
1,
|
||||
['argument1', -1],
|
||||
['argument1']
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Predis\Command\Traits;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
use PredisTestCase;
|
||||
|
||||
class ReplaceTest extends PredisTestCase
|
||||
{
|
||||
private $testClass;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->testClass = new class extends RedisCommand {
|
||||
use Replace;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return 'test';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider argumentsProvider
|
||||
* @param array $arguments
|
||||
* @param array $expectedResponse
|
||||
* @return void
|
||||
*/
|
||||
public function testReturnsCorrectArguments(array $arguments, array $expectedResponse): void
|
||||
{
|
||||
$this->testClass->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expectedResponse, $this->testClass->getArguments());
|
||||
}
|
||||
|
||||
public function argumentsProvider(): array
|
||||
{
|
||||
return [
|
||||
'with boolean - true' => [[true],['REPLACE']],
|
||||
'with boolean - false' => [[false], []],
|
||||
'with non boolean' => [['string'], ['string']],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user