Bläddra i källkod

Add the ability for the client to send raw commands to Redis.

When sending raw commands their arguments are not filtered, responses
are not parsed and key prefixes are not applied. The client also does
not throw any exception on Redis errors regardless of its settings.

The first parameter takes the raw arguments of the command (included
its identifier) as defined by the Redis documentation while the second
optional parameter is always populated by reference to indicate when
Redis actually returned an error response.

  $client->raw(['PING']);                // "PONG"
  $client->raw(['SET','foo','bar']);     // "OK"
  $client->raw(['GET','foo'], $err);     // "bar", $err=FALSE
  $client->raw(['LPUSH','foo',1], $err); // "WRONGTYPE...", $err=TRUE

Internally, this method creates instances of Predis\Command\RawCommand
that get passed to the underlying connection instance for execution as
if they were usual commands defined by Predis.

Raw commands work in both cluster and replication scenarios since they
are recognized by their command ID, but key prefixing is not supported
since it is done by the profile instance when instantiating commands.
Daniele Alessandri 11 år sedan
förälder
incheckning
583b924085
3 ändrade filer med 105 tillägg och 0 borttagningar
  1. 2 0
      CHANGELOG.md
  2. 33 0
      lib/Predis/Client.php
  3. 70 0
      tests/Predis/ClientTest.php

+ 2 - 0
CHANGELOG.md

@@ -56,6 +56,8 @@ v0.9.0 (201x-xx-xx)
   they can also define the needed logic in their command classes by implementing
   `Predis\Command\PrefixableCommandInterface` just like before.
 
+- The client can now send raw commands using the `Predis\Client::raw()` method.
+
 
 v0.8.5 (2013-xx-xx)
 ===============================================================================

+ 33 - 0
lib/Predis/Client.php

@@ -14,6 +14,7 @@ namespace Predis;
 use InvalidArgumentException;
 use UnexpectedValueException;
 use Predis\Command\CommandInterface;
+use Predis\Command\RawCommand;
 use Predis\Command\ScriptCommand;
 use Predis\Configuration;
 use Predis\Connection\AggregatedConnectionInterface;
@@ -248,6 +249,38 @@ class Client implements ClientInterface
         return $this->connection->getConnectionById($connectionID);
     }
 
+    /**
+     * Executes a command without filtering its arguments, parsing the response,
+     * applying any prefix to keys or throwing exceptions on Redis errors even
+     * regardless of client options.
+     *
+     * It is possibile to indentify Redis error responses from normal responses
+     * using the second optional argument which is populated by reference.
+     *
+     * @param array $arguments Command arguments as defined by the command signature.
+     * @param bool $error Set to TRUE when Redis returned an error response.
+     * @return mixed
+     */
+    public function raw(array $arguments, &$error = null)
+    {
+        $error = false;
+
+        $command = new RawCommand($arguments);
+        $response = $this->connection->executeCommand($command);
+
+        if ($response instanceof Response\ObjectInterface) {
+            if ($response instanceof Response\ErrorInterface) {
+                $error = true;
+            }
+
+            return (string) $response;
+        } else if ($response === true) {
+            return 'OK';
+        } else {
+            return $response;
+        }
+    }
+
     /**
      * Creates a Redis command with the specified arguments and sends a request
      * to the server.

+ 70 - 0
tests/Predis/ClientTest.php

@@ -497,6 +497,76 @@ class ClientTest extends PredisTestCase
         $this->assertSame($response, $expectedResponse);
     }
 
+    /**
+     * @group disconnected
+     */
+    public function testRawCommand()
+    {
+        $connection = $this->getMock('Predis\Connection\ConnectionInterface');
+        $connection->expects($this->at(0))
+                   ->method('executeCommand')
+                   ->with($this->isRedisCommand('SET', array('foo', 'bar')))
+                   ->will($this->returnValue(true));
+        $connection->expects($this->at(1))
+                   ->method('executeCommand')
+                   ->with($this->isRedisCommand('GET', array('foo')))
+                   ->will($this->returnValue('bar'));
+        $connection->expects($this->at(2))
+                   ->method('executeCommand')
+                   ->with($this->isRedisCommand('PING'))
+                   ->will($this->returnValue('PONG'));
+
+        $client = new Client($connection);
+
+        $this->assertSame('OK', $client->raw(['SET', 'foo', 'bar']));
+        $this->assertSame('bar', $client->raw(['GET', 'foo']));
+
+        $error = true;  // $error is always populated by reference.
+        $this->assertSame('PONG', $client->raw(['PING'], $error));
+        $this->assertFalse($error);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testRawCommandNeverAppliesPrefix()
+    {
+        $connection = $this->getMock('Predis\Connection\ConnectionInterface');
+        $connection->expects($this->at(0))
+                   ->method('executeCommand')
+                   ->with($this->isRedisCommand('SET', array('foo', 'bar')))
+                   ->will($this->returnValue(true));
+        $connection->expects($this->at(1))
+                   ->method('executeCommand')
+                   ->with($this->isRedisCommand('GET', array('foo')))
+                   ->will($this->returnValue('bar'));
+
+        $client = new Client($connection, array('prefix' => 'predis:'));
+
+        $this->assertSame('OK', $client->raw(['SET', 'foo', 'bar']));
+        $this->assertSame('bar', $client->raw(['GET', 'foo']));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testRawCommandNeverThrowsExceptions()
+    {
+        $message = 'ERR Mock error response';
+        $response = new Response\Error($message);
+
+        $connection = $this->getMock('Predis\Connection\ConnectionInterface');
+        $connection->expects($this->once())
+                   ->method('executeCommand')
+                   ->with($this->isRedisCommand('PING'))
+                   ->will($this->returnValue($response));
+
+        $client = new Client($connection, array('exceptions' => true));
+
+        $this->assertSame($message, $client->raw(['PING'], $error));
+        $this->assertTrue($error);
+    }
+
     /**
      * @group disconnected
      * @expectedException Predis\ClientException