Explorar el Código

Add new command: BITFIELD (Redis 3.2.0).

Daniele Alessandri hace 8 años
padre
commit
8cbbd073cb

+ 1 - 0
src/ClientContextInterface.php

@@ -38,6 +38,7 @@ use Predis\Command\CommandInterface;
  * @method $this append($key, $value)
  * @method $this bitcount($key, $start = null, $end = null)
  * @method $this bitop($operation, $destkey, $key)
+ * @method $this bitfield($key, ...)
  * @method $this decr($key)
  * @method $this decrby($key, $decrement)
  * @method $this get($key)

+ 1 - 0
src/ClientInterface.php

@@ -46,6 +46,7 @@ use Predis\Profile\ProfileInterface;
  * @method int    append($key, $value)
  * @method int    bitcount($key, $start = null, $end = null)
  * @method int    bitop($operation, $destkey, $key)
+ * @method array  bitfield($key, ...)
  * @method int    decr($key)
  * @method int    decrby($key, $decrement)
  * @method string get($key)

+ 1 - 0
src/Cluster/ClusterStrategy.php

@@ -80,6 +80,7 @@ abstract class ClusterStrategy implements StrategyInterface
             'SUBSTR' => $getKeyFromFirstArgument,
             'BITOP' => array($this, 'getKeyFromBitOp'),
             'BITCOUNT' => $getKeyFromFirstArgument,
+            'BITFIELD' => $getKeyFromFirstArgument,
 
             /* commands operating on lists */
             'LINSERT' => $getKeyFromFirstArgument,

+ 1 - 0
src/Command/Processor/KeyPrefixProcessor.php

@@ -159,6 +159,7 @@ class KeyPrefixProcessor implements ProcessorInterface
             'BITPOS' => 'static::first',
             /* ---------------- Redis 3.2 ---------------- */
             'HSTRLEN' => 'static::first',
+            'BITFIELD' => 'static::first',
         );
     }
 

+ 28 - 0
src/Command/StringBitField.php

@@ -0,0 +1,28 @@
+<?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\Command;
+
+/**
+ * @link http://redis.io/commands/bitfield
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class StringBitField extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'BITFIELD';
+    }
+}

+ 1 - 0
src/Profile/RedisVersion320.php

@@ -267,6 +267,7 @@ class RedisVersion320 extends RedisProfile
 
             /* commands operating on hashes */
             'HSTRLEN' => 'Predis\Command\HashStringLength',
+            'BITFIELD' => 'Predis\Command\StringBitField',
         );
     }
 }

+ 26 - 0
src/Replication/ReplicationStrategy.php

@@ -114,6 +114,31 @@ class ReplicationStrategy
         return true;
     }
 
+    /**
+     * Checks if BITFIELD performs a read-only operation by looking for certain
+     * SET and INCRYBY modifiers in the arguments array of the command.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return bool
+     */
+    protected function isBitfieldReadOnly(CommandInterface $command)
+    {
+        $arguments = $command->getArguments();
+        $argc = count($arguments);
+
+        if ($argc >= 2) {
+            for ($i = 1; $i < $argc; $i++) {
+                $argument = strtoupper($arguments[$i]);
+                if ($argument === 'SET' || $argument === 'INCRBY') {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
     /**
      * Marks a command as a read-only operation.
      *
@@ -242,6 +267,7 @@ class ReplicationStrategy
             'TIME' => true,
             'PFCOUNT' => true,
             'SORT' => array($this, 'isSortReadOnly'),
+            'BITFIELD' => array($this, 'isBitfieldReadOnly'),
         );
     }
 }

+ 1 - 0
tests/Predis/Cluster/PredisStrategyTest.php

@@ -310,6 +310,7 @@ class PredisStrategyTest extends PredisTestCase
             'SUBSTR' => 'keys-first',
             'BITOP' => 'keys-bitop',
             'BITCOUNT' => 'keys-first',
+            'BITFIELD' => 'keys-first',
 
             /* commands operating on lists */
             'LINSERT' => 'keys-first',

+ 1 - 0
tests/Predis/Cluster/RedisStrategyTest.php

@@ -320,6 +320,7 @@ class RedisStrategyTest extends PredisTestCase
             'SUBSTR' => 'keys-first',
             'BITOP' => 'keys-bitop',
             'BITCOUNT' => 'keys-first',
+            'BITFIELD' => 'keys-first',
 
             /* commands operating on lists */
             'LINSERT' => 'keys-first',

+ 4 - 0
tests/Predis/Command/Processor/KeyPrefixProcessorTest.php

@@ -873,6 +873,10 @@ class KeyPrefixProcessorTest extends PredisTestCase
                 array('key', 'field'),
                 array('prefix:key', 'field'),
             ),
+            array('BITFIELD',
+                array('key', 'GET', 'u8', '0', 'SET', 'u8', '0', '1'),
+                array('prefix:key', 'GET', 'u8', '0', 'SET', 'u8', '0', '1'),
+            ),
         );
     }
 }

+ 155 - 0
tests/Predis/Command/StringBitFieldTest.php

@@ -0,0 +1,155 @@
+<?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\Command;
+
+/**
+ * @group commands
+ * @group realm-string
+ */
+class StringBitFieldTest extends PredisCommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Command\StringBitField';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'BITFIELD';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key');
+        $expected = array('key');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterMultipleArguments()
+    {
+        $arguments = array('key', 'incrby', 'u2', '100', '1', 'OVERFLOW', 'SAT', 'incrby', 'u2', '102', '1', 'GET', 'u2', '100');
+        $expected = array('key', 'incrby', 'u2', '100', '1', 'OVERFLOW', 'SAT', 'incrby', 'u2', '102', '1', 'GET', 'u2', '100');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = array(1);
+        $expected = array(1);
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponseComplex()
+    {
+        $raw = array(1, 0, 3);
+        $expected = array(1, 0, 3);
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group connected
+     * @requiresRedisVersion >= 3.2.0
+     */
+    public function testBitfieldWithGetModifier()
+    {
+        $redis = $this->getClient();
+
+        $redis->setbit('string', 0, 1);
+        $redis->setbit('string', 8, 1);
+
+        $this->assertSame(array(128), $redis->bitfield('string', 'GET', 'u8', 0));
+        $this->assertSame(array(128, 1, 128), $redis->bitfield('string', 'GET', 'u8', 0, 'GET', 'u8', 1, 'GET', 'u8', 8));
+    }
+
+    /**
+     * @group connected
+     * @requiresRedisVersion >= 3.2.0
+     */
+    public function testBitfieldWithSetModifier()
+    {
+        $redis = $this->getClient();
+
+        $redis->setbit('string', 0, 1);
+        $redis->setbit('string', 8, 1);
+
+        $this->assertSame(array(128), $redis->bitfield('string', 'SET', 'u8', 0, 1));
+        $this->assertSame(array(1, 128), $redis->bitfield('string', 'SET', 'u8', 0, 128, 'SET', 'u8', 8, 1));
+        $this->assertSame(array(1, 128), $redis->bitfield('string', 'SET', 'u8', 8, 128, 'GET', 'u8', 8));
+
+        $this->assertSame("\x80\x80", $redis->get('string'));
+    }
+
+    /**
+     * @group connected
+     * @requiresRedisVersion >= 3.2.0
+     */
+    public function testBitfieldWithIncrbyModifier()
+    {
+        $redis = $this->getClient();
+
+        $redis->setbit('string', 0, 1);
+        $redis->setbit('string', 8, 1);
+
+        $this->assertSame(array(138), $redis->bitfield('string', 'INCRBY', 'u8', 0, 10));
+        $this->assertSame(array(143, 128), $redis->bitfield('string', 'INCRBY', 'u8', 0, 5, 'INCRBY', 'u8', 0, -15));
+
+        $this->assertSame("\x80\x80", $redis->get('string'));
+    }
+
+    /**
+     * @group connected
+     * @requiresRedisVersion >= 3.2.0
+     * @expectedException \Predis\Response\ServerException
+     * @expectedExceptionMessage Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $this->markTestSkipped('Currently skipped due issues in Redis (see antirez/redis#3259).');
+
+        $redis = $this->getClient();
+
+        $redis->lpush('metavars', 'foo');
+        $redis->bitfield('metavars', 'SET', 'u4', '0', '1');
+    }
+}

+ 1 - 0
tests/Predis/Profile/RedisUnstableTest.php

@@ -190,6 +190,7 @@ class RedisUnstableTest extends PredisProfileTestCase
             149 => 'PFMERGE',
             150 => 'COMMAND',
             151 => 'HSTRLEN',
+            152 => 'BITFIELD',
         );
     }
 }

+ 1 - 0
tests/Predis/Profile/RedisVersion320Test.php

@@ -190,6 +190,7 @@ class RedisVersion320Test extends PredisProfileTestCase
             149 => 'PFMERGE',
             150 => 'COMMAND',
             151 => 'HSTRLEN',
+            152 => 'BITFIELD',
         );
     }
 }

+ 46 - 0
tests/Predis/Replication/ReplicationStrategyTest.php

@@ -94,6 +94,51 @@ class ReplicationStrategyTest extends PredisTestCase
         );
     }
 
+    /**
+     * @group disconnected
+     */
+    public function testBitFieldCommand()
+    {
+        $profile = Profile\Factory::getDevelopment();
+        $strategy = new ReplicationStrategy();
+
+        $command = $profile->createCommand('BITFIELD', array('key'));
+        $this->assertTrue(
+            $strategy->isReadOperation($command),
+            'BITFIELD with no modifiers is expected to be a read operation.'
+        );
+
+        $command = $profile->createCommand('BITFIELD', array('key', 'GET', 'u4', '0'));
+        $this->assertTrue(
+            $strategy->isReadOperation($command),
+            'BITFIELD with GET only is expected to be a read operation.'
+        );
+
+        $command = $profile->createCommand('BITFIELD', array('key', 'SET', 'u4', '0', 1));
+        $this->assertFalse(
+            $strategy->isReadOperation($command),
+            'BITFIELD with SET is expected to be a write operation.'
+        );
+
+        $command = $profile->createCommand('BITFIELD', array('key', 'INCRBY', 'u4', '0', 1));
+        $this->assertFalse(
+            $strategy->isReadOperation($command),
+            'BITFIELD with INCRBY is expected to be a write operation.'
+        );
+
+        $command = $profile->createCommand('BITFIELD', array('key', 'GET', 'u4', '0', 'INCRBY', 'u4', '0', 1));
+        $this->assertFalse(
+            $strategy->isReadOperation($command),
+            'BITFIELD with GET and INCRBY is expected to be a write operation.'
+        );
+
+        $command = $profile->createCommand('BITFIELD', array('key', 'GET', 'u4', '0', 'SET', 'u4', '0', 1));
+        $this->assertFalse(
+            $strategy->isReadOperation($command),
+            'BITFIELD with GET and SET is expected to be a write operation.'
+        );
+    }
+
     /**
      * @group disconnected
      * @expectedException \Predis\NotSupportedException
@@ -316,6 +361,7 @@ class ReplicationStrategyTest extends PredisTestCase
             'SETRANGE' => 'write',
             'STRLEN' => 'read',
             'SUBSTR' => 'read',
+            'BITFIELD' => 'variable',
 
             /* commands operating on lists */
             'LINSERT' => 'write',