Bladeren bron

New commands: BITOP, BITCOUNT (Redis v2.6-dev).

Daniele Alessandri 13 jaren geleden
bovenliggende
commit
3aff21f8cc

+ 18 - 0
lib/Predis/Commands/PrefixHelpers.php

@@ -67,6 +67,24 @@ class PrefixHelpers
         $command->setRawArguments($arguments);
     }
 
+    /**
+     * Applies the specified prefix to all the arguments but the first one.
+     *
+     * @param ICommand $command Command instance.
+     * @param string $prefix Prefix string.
+     */
+    public static function skipFirst(ICommand $command, $prefix)
+    {
+        $arguments = $command->getArguments();
+        $length = count($arguments);
+
+        for ($i = 1; $i < $length; $i++) {
+            $arguments[$i] = "$prefix{$arguments[$i]}";
+        }
+
+        $command->setRawArguments($arguments);
+    }
+
     /**
      * Applies the specified prefix to all the arguments but the last one.
      *

+ 59 - 0
lib/Predis/Commands/StringBitOp.php

@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+/**
+ * @link http://redis.io/commands/bitop
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class StringBitOp extends Command implements IPrefixable
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'BITOP';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(Array $arguments)
+    {
+        if (count($arguments) === 3 && is_array($arguments[2])) {
+            list($operation, $destination, ) = $arguments;
+            $arguments = $arguments[2];
+            array_unshift($arguments, $operation, $destination);
+        }
+
+        return $arguments;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function prefixKeys($prefix)
+    {
+        PrefixHelpers::skipFirst($this, $prefix);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function canBeHashed()
+    {
+        return $this->checkSameHashForKeys(
+            array_slice(($args = $this->getArguments()), 1, count($args))
+        );
+    }
+}

+ 1 - 0
lib/Predis/Network/MasterSlaveReplication.php

@@ -409,6 +409,7 @@ class MasterSlaveReplication implements IConnectionReplication
             'ECHO'              => true,
             'QUIT'              => true,
             'OBJECT'            => true,
+            'BITCOUNT'          => true,
             'SORT'              => array($this, 'isSortReadOnly'),
         );
     }

+ 2 - 0
lib/Predis/Profiles/ServerVersion26.php

@@ -217,6 +217,8 @@ class ServerVersion26 extends ServerProfile
             /* commands operating on string values */
             'psetex'                    => 'Predis\Commands\StringPreciseSetExpire',
             'incrbyfloat'               => 'Predis\Commands\StringIncrementByFloat',
+            'bitop'                     => 'Predis\Commands\StringBitOp',
+            'bitcount'                  => 'Predis\Commands\StringBitCount',
 
             /* commands operating on hashes */
             'hincrbyfloat'              => 'Predis\Commands\HashIncrementByFloat',

+ 193 - 0
tests/Predis/Commands/StringBitOpTest.php

@@ -0,0 +1,193 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-string
+ */
+class StringBitOpTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\StringBitOp';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'BITOP';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('AND', 'key:dst', 'key:01', 'key:02');
+        $expected = array('AND', 'key:dst', 'key:01', 'key:02');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArgumentsKeysAsSingleArray()
+    {
+        $arguments = array('AND', 'key:dst', array('key:01', 'key:02'));
+        $expected = array('AND', 'key:dst', 'key:01', 'key:02');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = 10;
+        $expected = 10;
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('AND', 'key:dst', 'key:01', 'key:02');
+        $expected = array('AND', 'prefix:key:dst', 'prefix:key:01', 'prefix:key:02');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testCanPerformBitwiseAND()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('key:src:1', "h\x80");
+        $redis->set('key:src:2', "R");
+
+        $this->assertSame(2, $redis->bitop('AND', 'key:dst', 'key:src:1', 'key:src:2'));
+        $this->assertSame("@\x00", $redis->get('key:dst'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testCanPerformBitwiseOR()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('key:src:1', "h\x80");
+        $redis->set('key:src:2', "R");
+
+        $this->assertSame(2, $redis->bitop('OR', 'key:dst', 'key:src:1', 'key:src:2'));
+        $this->assertSame("z\x80", $redis->get('key:dst'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testCanPerformBitwiseXOR()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('key:src:1', "h\x80");
+        $redis->set('key:src:2', "R");
+
+        $this->assertSame(2, $redis->bitop('XOR', 'key:dst', 'key:src:1', 'key:src:2'));
+        $this->assertSame(":\x80", $redis->get('key:dst'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testCanPerformBitwiseNOT()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('key:src:1', "h\x80");
+
+        $this->assertSame(2, $redis->bitop('NOT', 'key:dst', 'key:src:1'));
+        $this->assertSame("\x97\x7f", $redis->get('key:dst'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR BITOP NOT must be called with a single source key.
+     */
+    public function testBitwiseNOTAcceptsOnlyOneSourceKey()
+    {
+        $this->getClient()->bitop('NOT', 'key:dst', 'key:src:1', 'key:src:2');
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR syntax error
+     */
+    public function testThrowsExceptionOnInvalidOperation()
+    {
+        $this->getClient()->bitop('NOOP', 'key:dst', 'key:src:1', 'key:src:2');
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnInvalidSourceKey()
+    {
+        $redis = $this->getClient();
+
+        $redis->lpush('key:src:1', 'list');
+        $redis->bitop('AND', 'key:dst', 'key:src:1', 'key:src:2');
+    }
+
+    /**
+     * @group connected
+     */
+    public function testDoesNotThrowExceptionOnInvalidDestinationKey()
+    {
+        $redis = $this->getClient();
+
+        $redis->lpush('key:dst', 'list');
+        $redis->bitop('AND', 'key:dst', 'key:src:1', 'key:src:2');
+
+        $this->assertSame('none', $redis->type('key:dst'));
+    }
+}

+ 7 - 5
tests/Predis/Profiles/ServerVersion26Test.php

@@ -164,11 +164,13 @@ class ServerVersion26Test extends ServerVersionTestCase
             123 => 'pexpireat',
             124 => 'psetex',
             125 => 'incrbyfloat',
-            126 => 'hincrbyfloat',
-            127 => 'eval',
-            128 => 'evalsha',
-            129 => 'script',
-            130 => 'time',
+            126 => 'bitop',
+            127 => 'bitcount',
+            128 => 'hincrbyfloat',
+            129 => 'eval',
+            130 => 'evalsha',
+            131 => 'script',
+            132 => 'time',
         );
     }
 }

+ 7 - 5
tests/Predis/Profiles/ServerVersionNextTest.php

@@ -164,11 +164,13 @@ class ServerVersionNextTest extends ServerVersionTestCase
             123 => 'pexpireat',
             124 => 'psetex',
             125 => 'incrbyfloat',
-            126 => 'hincrbyfloat',
-            127 => 'eval',
-            128 => 'evalsha',
-            129 => 'script',
-            130 => 'time',
+            126 => 'bitop',
+            127 => 'bitcount',
+            128 => 'hincrbyfloat',
+            129 => 'eval',
+            130 => 'evalsha',
+            131 => 'script',
+            132 => 'time',
         );
     }
 }