diff --git a/CHANGELOG.md b/CHANGELOG.md index d5a7a61e..a22bd0c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,11 @@ v0.9.0 (201x-xx-xx) - `fire-and-forget`: returns a pipeline that does not read back responses (class: `Predis\Pipeline\FireAndForget`). +- It is now possible to execute "raw commands" using `Predis\Command\RawCommand` + and a variable list of command arguments. Input arguments are not filtered and + responses are not parsed, which means arguments must follow the signature of + the command as defined by Redis and complex responses are left untouched. + - The two base abstract command classes have been renamed in the following way: - `Predis\Command\AbstractCommand` is now `Predis\Command\Command` diff --git a/lib/Predis/Command/RawCommand.php b/lib/Predis/Command/RawCommand.php new file mode 100644 index 00000000..782b01a5 --- /dev/null +++ b/lib/Predis/Command/RawCommand.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +use InvalidArgumentException; + +/** + * Class for generic "anonymous" Redis commands. + * + * This command class does not filter input arguments or parse responses, but + * can be used to leverage the standard Predis API to execute any command simply + * by providing the needed arguments following the command signature as defined + * by Redis in its documentation. + * + * @author Daniele Alessandri + */ +class RawCommand implements CommandInterface +{ + private $hash; + private $commandID; + private $arguments; + + public function __construct(array $arguments) + { + if (!$arguments) { + throw new InvalidArgumentException("Arguments array is missing the command ID"); + } + + $this->commandID = array_shift($arguments); + $this->arguments = $arguments; + } + + /** + * Creates a new raw command using a variadic method. + * + * @param string $commandID Redis command ID. + * @param string ... Arguments list for the command. + */ + public static function create($commandID /* [ $arg, ... */) + { + $arguments = func_get_args(); + $command = new self($arguments); + + return $command; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->commandID; + } + + /** + * {@inheritdoc} + */ + public function setArguments(array $arguments) + { + $this->arguments = $arguments; + unset($this->hash); + } + + /** + * {@inheritdoc} + */ + public function setRawArguments(array $arguments) + { + $this->setArguments($arguments); + } + + /** + * {@inheritdoc} + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Gets the argument from the arguments list at the specified index. + * + * @param array $arguments Position of the argument. + */ + public function getArgument($index) + { + if (isset($this->arguments[$index])) { + return $this->arguments[$index]; + } + } + + /** + * {@inheritdoc} + */ + public function setHash($hash) + { + $this->hash = $hash; + } + + /** + * {@inheritdoc} + */ + public function getHash() + { + if (isset($this->hash)) { + return $this->hash; + } + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data; + } + + /** + * Helper function used to reduce a list of arguments to a string. + * + * @param string $accumulator Temporary string. + * @param string $argument Current argument. + * @return string + */ + protected function toStringArgumentReducer($accumulator, $argument) + { + if (strlen($argument) > 32) { + $argument = substr($argument, 0, 32) . '[...]'; + } + + $accumulator .= " $argument"; + + return $accumulator; + } + + /** + * Returns a partial string representation of the command with its arguments. + * + * @return string + */ + public function __toString() + { + return array_reduce( + $this->getArguments(), + array($this, 'toStringArgumentReducer'), + $this->getId() + ); + } +} diff --git a/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php b/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php index 87db64a3..6fd1c74b 100644 --- a/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php +++ b/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php @@ -13,7 +13,7 @@ namespace Predis\Command\Processor; use stdClass; use PHPUnit_Framework_TestCase as StandardTestCase; -use Predis\Command\Command; +use Predis\Command\RawCommand; /** * @@ -321,7 +321,7 @@ class KeyPrefixProcessorTest extends StandardTestCase public function getCommandInstance($commandID, array $arguments) { - $command = new DummyCommand($commandID); + $command = new RawCommand(array($commandID)); $command->setRawArguments($arguments); return $command; @@ -803,18 +803,3 @@ class KeyPrefixProcessorTest extends StandardTestCase ); } } - -class DummyCommand extends Command -{ - protected $commandID; - - public function __construct($commandID) - { - $this->commandID = $commandID; - } - - public function getId() - { - return $this->commandID; - } -} diff --git a/tests/Predis/Command/RawCommandTest.php b/tests/Predis/Command/RawCommandTest.php new file mode 100644 index 00000000..9c683ef6 --- /dev/null +++ b/tests/Predis/Command/RawCommandTest.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Predis\Command; + +use PHPUnit_Framework_TestCase as StandardTestCase; + +/** + * + */ +class RawCommandTest extends StandardTestCase +{ + /** + * @group disconnected + */ + public function testConstructorWithCommandID() + { + $commandID = 'PING'; + $command = new RawCommand(array($commandID)); + + $this->assertSame($commandID, $command->getId()); + $this->assertEmpty($command->getArguments()); + } + + /** + * @group disconnected + */ + public function testConstructorWithCommandIDAndArguments() + { + $commandID = 'SET'; + $commandArgs = array('foo', 'bar'); + + $command = new RawCommand(array_merge((array) $commandID, $commandArgs)); + + $this->assertSame($commandID, $command->getId()); + $this->assertSame($commandArgs, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testStaticCreate() + { + $command = RawCommand::create('SET'); + $this->assertSame('SET', $command->getId()); + $this->assertEmpty($command->getArguments()); + + $command = RawCommand::create('SET', 'foo', 'bar'); + $this->assertSame('SET', $command->getId()); + $this->assertSame(array('foo', 'bar'), $command->getArguments()); + } + + /** + * @group disconnected + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Arguments array is missing the command ID + */ + public function testExceptionOnMissingCommandID() + { + $command = new RawCommand(array()); + } + + /** + * @group disconnected + * @expectedException PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage Missing argument 1 for Predis\Command\RawCommand::create() + */ + public function testPHPWarningOnMissingCommandIDWithStaticCreate() + { + RawCommand::create(); + } + + /** + * @group disconnected + */ + public function testSetArguments() + { + $commandID = 'SET'; + $command = new RawCommand(array($commandID)); + + $command->setArguments($commandArgs = array('foo', 'bar')); + $this->assertSame($commandArgs, $command->getArguments()); + + $command->setArguments($commandArgs = array('hoge', 'piyo')); + $this->assertSame($commandArgs, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testSetRawArguments() + { + $commandID = 'SET'; + $command = new RawCommand(array($commandID)); + + $command->setRawArguments($commandArgs = array('foo', 'bar')); + $this->assertSame($commandArgs, $command->getArguments()); + + $command->setRawArguments($commandArgs = array('hoge', 'piyo')); + $this->assertSame($commandArgs, $command->getArguments()); + } + + /** + * @group disconnected + */ + public function testSetAndGetHash() + { + $hash = "key-hash"; + $arguments = array('SET', 'key', 'value'); + $command = new RawCommand($arguments); + + $this->assertNull($command->getHash()); + + $command->setHash($hash); + $this->assertSame($hash, $command->getHash()); + + $command->setArguments(array('hoge', 'piyo')); + $this->assertNull($command->getHash()); + } + + /** + * @group disconnected + */ + public function testToString() + { + $arguments = array('SET', 'key', 'value'); + $expected = implode(' ', $arguments); + + $command = new RawCommand($arguments); + + $this->assertEquals($expected, (string) $command); + } +}