code:
<?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\Profile;

/**
 * Server profile for Redis 2.6.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class RedisVersion260 extends RedisProfile
{
    /**
     * {@inheritdoc}
     */
    public function getVersion()
    {
        return '2.6';
    }

    /**
     * {@inheritdoc}
     */
    public function getSupportedCommands()
    {
        return array(
            /* ---------------- Redis 1.2 ---------------- */

            /* commands operating on the key space */
            'EXISTS' => 'Predis\Command\KeyExists',
            'DEL' => 'Predis\Command\KeyDelete',
            'TYPE' => 'Predis\Command\KeyType',
            'KEYS' => 'Predis\Command\KeyKeys',
            'RANDOMKEY' => 'Predis\Command\KeyRandom',
            'RENAME' => 'Predis\Command\KeyRename',
            'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
            'EXPIRE' => 'Predis\Command\KeyExpire',
            'EXPIREAT' => 'Predis\Command\KeyExpireAt',
            'TTL' => 'Predis\Command\KeyTimeToLive',
            'MOVE' => 'Predis\Command\KeyMove',
            'SORT' => 'Predis\Command\KeySort',
            'DUMP' => 'Predis\Command\KeyDump',
            'RESTORE' => 'Predis\Command\KeyRestore',

            /* commands operating on string values */
            'SET' => 'Predis\Command\StringSet',
            'SETNX' => 'Predis\Command\StringSetPreserve',
            'MSET' => 'Predis\Command\StringSetMultiple',
            'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
            'GET' => 'Predis\Command\StringGet',
            'MGET' => 'Predis\Command\StringGetMultiple',
            'GETSET' => 'Predis\Command\StringGetSet',
            'INCR' => 'Predis\Command\StringIncrement',
            'INCRBY' => 'Predis\Command\StringIncrementBy',
            'DECR' => 'Predis\Command\StringDecrement',
            'DECRBY' => 'Predis\Command\StringDecrementBy',

            /* commands operating on lists */
            'RPUSH' => 'Predis\Command\ListPushTail',
            'LPUSH' => 'Predis\Command\ListPushHead',
            'LLEN' => 'Predis\Command\ListLength',
            'LRANGE' => 'Predis\Command\ListRange',
            'LTRIM' => 'Predis\Command\ListTrim',
            'LINDEX' => 'Predis\Command\ListIndex',
            'LSET' => 'Predis\Command\ListSet',
            'LREM' => 'Predis\Command\ListRemove',
            'LPOP' => 'Predis\Command\ListPopFirst',
            'RPOP' => 'Predis\Command\ListPopLast',
            'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',

            /* commands operating on sets */
            'SADD' => 'Predis\Command\SetAdd',
            'SREM' => 'Predis\Command\SetRemove',
            'SPOP' => 'Predis\Command\SetPop',
            'SMOVE' => 'Predis\Command\SetMove',
            'SCARD' => 'Predis\Command\SetCardinality',
            'SISMEMBER' => 'Predis\Command\SetIsMember',
            'SINTER' => 'Predis\Command\SetIntersection',
            'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
            'SUNION' => 'Predis\Command\SetUnion',
            'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
            'SDIFF' => 'Predis\Command\SetDifference',
            'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
            'SMEMBERS' => 'Predis\Command\SetMembers',
            'SRANDMEMBER' => 'Predis\Command\SetRandomMember',

            /* commands operating on sorted sets */
            'ZADD' => 'Predis\Command\ZSetAdd',
            'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
            'ZREM' => 'Predis\Command\ZSetRemove',
            'ZRANGE' => 'Predis\Command\ZSetRange',
            'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
            'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
            'ZCARD' => 'Predis\Command\ZSetCardinality',
            'ZSCORE' => 'Predis\Command\ZSetScore',
            'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',

            /* connection related commands */
            'PING' => 'Predis\Command\ConnectionPing',
            'AUTH' => 'Predis\Command\ConnectionAuth',
            'SELECT' => 'Predis\Command\ConnectionSelect',
            'ECHO' => 'Predis\Command\ConnectionEcho',
            'QUIT' => 'Predis\Command\ConnectionQuit',

            /* remote server control commands */
            'INFO' => 'Predis\Command\ServerInfoV26x',
            'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
            'MONITOR' => 'Predis\Command\ServerMonitor',
            'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
            'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
            'FLUSHALL' => 'Predis\Command\ServerFlushAll',
            'SAVE' => 'Predis\Command\ServerSave',
            'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
            'LASTSAVE' => 'Predis\Command\ServerLastSave',
            'SHUTDOWN' => 'Predis\Command\ServerShutdown',
            'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',

            /* ---------------- Redis 2.0 ---------------- */

            /* commands operating on string values */
            'SETEX' => 'Predis\Command\StringSetExpire',
            'APPEND' => 'Predis\Command\StringAppend',
            'SUBSTR' => 'Predis\Command\StringSubstr',

            /* commands operating on lists */
            'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
            'BRPOP' => 'Predis\Command\ListPopLastBlocking',

            /* commands operating on sorted sets */
            'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
            'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
            'ZCOUNT' => 'Predis\Command\ZSetCount',
            'ZRANK' => 'Predis\Command\ZSetRank',
            'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
            'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',

            /* commands operating on hashes */
            'HSET' => 'Predis\Command\HashSet',
            'HSETNX' => 'Predis\Command\HashSetPreserve',
            'HMSET' => 'Predis\Command\HashSetMultiple',
            'HINCRBY' => 'Predis\Command\HashIncrementBy',
            'HGET' => 'Predis\Command\HashGet',
            'HMGET' => 'Predis\Command\HashGetMultiple',
            'HDEL' => 'Predis\Command\HashDelete',
            'HEXISTS' => 'Predis\Command\HashExists',
            'HLEN' => 'Predis\Command\HashLength',
            'HKEYS' => 'Predis\Command\HashKeys',
            'HVALS' => 'Predis\Command\HashValues',
            'HGETALL' => 'Predis\Command\HashGetAll',

            /* transactions */
            'MULTI' => 'Predis\Command\TransactionMulti',
            'EXEC' => 'Predis\Command\TransactionExec',
            'DISCARD' => 'Predis\Command\TransactionDiscard',

            /* publish - subscribe */
            'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
            'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
            'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
            'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
            'PUBLISH' => 'Predis\Command\PubSubPublish',

            /* remote server control commands */
            'CONFIG' => 'Predis\Command\ServerConfig',

            /* ---------------- Redis 2.2 ---------------- */

            /* commands operating on the key space */
            'PERSIST' => 'Predis\Command\KeyPersist',

            /* commands operating on string values */
            'STRLEN' => 'Predis\Command\StringStrlen',
            'SETRANGE' => 'Predis\Command\StringSetRange',
            'GETRANGE' => 'Predis\Command\StringGetRange',
            'SETBIT' => 'Predis\Command\StringSetBit',
            'GETBIT' => 'Predis\Command\StringGetBit',

            /* commands operating on lists */
            'RPUSHX' => 'Predis\Command\ListPushTailX',
            'LPUSHX' => 'Predis\Command\ListPushHeadX',
            'LINSERT' => 'Predis\Command\ListInsert',
            'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',

            /* commands operating on sorted sets */
            'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',

            /* transactions */
            'WATCH' => 'Predis\Command\TransactionWatch',
            'UNWATCH' => 'Predis\Command\TransactionUnwatch',

            /* remote server control commands */
            'OBJECT' => 'Predis\Command\ServerObject',
            'SLOWLOG' => 'Predis\Command\ServerSlowlog',

            /* ---------------- Redis 2.4 ---------------- */

            /* remote server control commands */
            'CLIENT' => 'Predis\Command\ServerClient',

            /* ---------------- Redis 2.6 ---------------- */

            /* commands operating on the key space */
            'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
            'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
            'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
            'MIGRATE' => 'Predis\Command\KeyMigrate',

            /* commands operating on string values */
            'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
            'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
            'BITOP' => 'Predis\Command\StringBitOp',
            'BITCOUNT' => 'Predis\Command\StringBitCount',

            /* commands operating on hashes */
            'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',

            /* scripting */
            'EVAL' => 'Predis\Command\ServerEval',
            'EVALSHA' => 'Predis\Command\ServerEvalSHA',
            'SCRIPT' => 'Predis\Command\ServerScript',

            /* remote server control commands */
            'TIME' => 'Predis\Command\ServerTime',
            'SENTINEL' => 'Predis\Command\ServerSentinel',
        );
    }
}


code:
<?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\Profile;

/**
 * Server profile for Redis 2.8.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class RedisVersion280 extends RedisProfile
{
    /**
     * {@inheritdoc}
     */
    public function getVersion()
    {
        return '2.8';
    }

    /**
     * {@inheritdoc}
     */
    public function getSupportedCommands()
    {
        return array(
            /* ---------------- Redis 1.2 ---------------- */

            /* commands operating on the key space */
            'EXISTS' => 'Predis\Command\KeyExists',
            'DEL' => 'Predis\Command\KeyDelete',
            'TYPE' => 'Predis\Command\KeyType',
            'KEYS' => 'Predis\Command\KeyKeys',
            'RANDOMKEY' => 'Predis\Command\KeyRandom',
            'RENAME' => 'Predis\Command\KeyRename',
            'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
            'EXPIRE' => 'Predis\Command\KeyExpire',
            'EXPIREAT' => 'Predis\Command\KeyExpireAt',
            'TTL' => 'Predis\Command\KeyTimeToLive',
            'MOVE' => 'Predis\Command\KeyMove',
            'SORT' => 'Predis\Command\KeySort',
            'DUMP' => 'Predis\Command\KeyDump',
            'RESTORE' => 'Predis\Command\KeyRestore',

            /* commands operating on string values */
            'SET' => 'Predis\Command\StringSet',
            'SETNX' => 'Predis\Command\StringSetPreserve',
            'MSET' => 'Predis\Command\StringSetMultiple',
            'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
            'GET' => 'Predis\Command\StringGet',
            'MGET' => 'Predis\Command\StringGetMultiple',
            'GETSET' => 'Predis\Command\StringGetSet',
            'INCR' => 'Predis\Command\StringIncrement',
            'INCRBY' => 'Predis\Command\StringIncrementBy',
            'DECR' => 'Predis\Command\StringDecrement',
            'DECRBY' => 'Predis\Command\StringDecrementBy',

            /* commands operating on lists */
            'RPUSH' => 'Predis\Command\ListPushTail',
            'LPUSH' => 'Predis\Command\ListPushHead',
            'LLEN' => 'Predis\Command\ListLength',
            'LRANGE' => 'Predis\Command\ListRange',
            'LTRIM' => 'Predis\Command\ListTrim',
            'LINDEX' => 'Predis\Command\ListIndex',
            'LSET' => 'Predis\Command\ListSet',
            'LREM' => 'Predis\Command\ListRemove',
            'LPOP' => 'Predis\Command\ListPopFirst',
            'RPOP' => 'Predis\Command\ListPopLast',
            'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',

            /* commands operating on sets */
            'SADD' => 'Predis\Command\SetAdd',
            'SREM' => 'Predis\Command\SetRemove',
            'SPOP' => 'Predis\Command\SetPop',
            'SMOVE' => 'Predis\Command\SetMove',
            'SCARD' => 'Predis\Command\SetCardinality',
            'SISMEMBER' => 'Predis\Command\SetIsMember',
            'SINTER' => 'Predis\Command\SetIntersection',
            'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
            'SUNION' => 'Predis\Command\SetUnion',
            'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
            'SDIFF' => 'Predis\Command\SetDifference',
            'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
            'SMEMBERS' => 'Predis\Command\SetMembers',
            'SRANDMEMBER' => 'Predis\Command\SetRandomMember',

            /* commands operating on sorted sets */
            'ZADD' => 'Predis\Command\ZSetAdd',
            'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
            'ZREM' => 'Predis\Command\ZSetRemove',
            'ZRANGE' => 'Predis\Command\ZSetRange',
            'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
            'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
            'ZCARD' => 'Predis\Command\ZSetCardinality',
            'ZSCORE' => 'Predis\Command\ZSetScore',
            'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',

            /* connection related commands */
            'PING' => 'Predis\Command\ConnectionPing',
            'AUTH' => 'Predis\Command\ConnectionAuth',
            'SELECT' => 'Predis\Command\ConnectionSelect',
            'ECHO' => 'Predis\Command\ConnectionEcho',
            'QUIT' => 'Predis\Command\ConnectionQuit',

            /* remote server control commands */
            'INFO' => 'Predis\Command\ServerInfoV26x',
            'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
            'MONITOR' => 'Predis\Command\ServerMonitor',
            'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
            'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
            'FLUSHALL' => 'Predis\Command\ServerFlushAll',
            'SAVE' => 'Predis\Command\ServerSave',
            'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
            'LASTSAVE' => 'Predis\Command\ServerLastSave',
            'SHUTDOWN' => 'Predis\Command\ServerShutdown',
            'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',

            /* ---------------- Redis 2.0 ---------------- */

            /* commands operating on string values */
            'SETEX' => 'Predis\Command\StringSetExpire',
            'APPEND' => 'Predis\Command\StringAppend',
            'SUBSTR' => 'Predis\Command\StringSubstr',

            /* commands operating on lists */
            'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
            'BRPOP' => 'Predis\Command\ListPopLastBlocking',

            /* commands operating on sorted sets */
            'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
            'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
            'ZCOUNT' => 'Predis\Command\ZSetCount',
            'ZRANK' => 'Predis\Command\ZSetRank',
            'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
            'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',

            /* commands operating on hashes */
            'HSET' => 'Predis\Command\HashSet',
            'HSETNX' => 'Predis\Command\HashSetPreserve',
            'HMSET' => 'Predis\Command\HashSetMultiple',
            'HINCRBY' => 'Predis\Command\HashIncrementBy',
            'HGET' => 'Predis\Command\HashGet',
            'HMGET' => 'Predis\Command\HashGetMultiple',
            'HDEL' => 'Predis\Command\HashDelete',
            'HEXISTS' => 'Predis\Command\HashExists',
            'HLEN' => 'Predis\Command\HashLength',
            'HKEYS' => 'Predis\Command\HashKeys',
            'HVALS' => 'Predis\Command\HashValues',
            'HGETALL' => 'Predis\Command\HashGetAll',

            /* transactions */
            'MULTI' => 'Predis\Command\TransactionMulti',
            'EXEC' => 'Predis\Command\TransactionExec',
            'DISCARD' => 'Predis\Command\TransactionDiscard',

            /* publish - subscribe */
            'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
            'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
            'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
            'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
            'PUBLISH' => 'Predis\Command\PubSubPublish',

            /* remote server control commands */
            'CONFIG' => 'Predis\Command\ServerConfig',

            /* ---------------- Redis 2.2 ---------------- */

            /* commands operating on the key space */
            'PERSIST' => 'Predis\Command\KeyPersist',

            /* commands operating on string values */
            'STRLEN' => 'Predis\Command\StringStrlen',
            'SETRANGE' => 'Predis\Command\StringSetRange',
            'GETRANGE' => 'Predis\Command\StringGetRange',
            'SETBIT' => 'Predis\Command\StringSetBit',
            'GETBIT' => 'Predis\Command\StringGetBit',

            /* commands operating on lists */
            'RPUSHX' => 'Predis\Command\ListPushTailX',
            'LPUSHX' => 'Predis\Command\ListPushHeadX',
            'LINSERT' => 'Predis\Command\ListInsert',
            'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',

            /* commands operating on sorted sets */
            'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',

            /* transactions */
            'WATCH' => 'Predis\Command\TransactionWatch',
            'UNWATCH' => 'Predis\Command\TransactionUnwatch',

            /* remote server control commands */
            'OBJECT' => 'Predis\Command\ServerObject',
            'SLOWLOG' => 'Predis\Command\ServerSlowlog',

            /* ---------------- Redis 2.4 ---------------- */

            /* remote server control commands */
            'CLIENT' => 'Predis\Command\ServerClient',

            /* ---------------- Redis 2.6 ---------------- */

            /* commands operating on the key space */
            'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
            'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
            'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
            'MIGRATE' => 'Predis\Command\KeyMigrate',

            /* commands operating on string values */
            'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
            'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
            'BITOP' => 'Predis\Command\StringBitOp',
            'BITCOUNT' => 'Predis\Command\StringBitCount',

            /* commands operating on hashes */
            'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',

            /* scripting */
            'EVAL' => 'Predis\Command\ServerEval',
            'EVALSHA' => 'Predis\Command\ServerEvalSHA',
            'SCRIPT' => 'Predis\Command\ServerScript',

            /* remote server control commands */
            'TIME' => 'Predis\Command\ServerTime',
            'SENTINEL' => 'Predis\Command\ServerSentinel',

            /* ---------------- Redis 2.8 ---------------- */

            /* commands operating on the key space */
            'SCAN' => 'Predis\Command\KeyScan',

            /* commands operating on string values */
            'BITPOS' => 'Predis\Command\StringBitPos',

            /* commands operating on sets */
            'SSCAN' => 'Predis\Command\SetScan',

            /* commands operating on sorted sets */
            'ZSCAN' => 'Predis\Command\ZSetScan',
            'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount',
            'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex',
            'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex',
            'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex',

            /* commands operating on hashes */
            'HSCAN' => 'Predis\Command\HashScan',

            /* publish - subscribe */
            'PUBSUB' => 'Predis\Command\PubSubPubsub',

            /* commands operating on HyperLogLog */
            'PFADD' => 'Predis\Command\HyperLogLogAdd',
            'PFCOUNT' => 'Predis\Command\HyperLogLogCount',
            'PFMERGE' => 'Predis\Command\HyperLogLogMerge',

            /* remote server control commands */
            'COMMAND' => 'Predis\Command\ServerCommand',
        );
    }
}

repo_name: spookie/gnu

license: mit


code:
<?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\Profile;

/**
 * Server profile for Redis 3.0.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class RedisVersion300 extends RedisProfile
{
    /**
     * {@inheritdoc}
     */
    public function getVersion()
    {
        return '3.0';
    }

    /**
     * {@inheritdoc}
     */
    public function getSupportedCommands()
    {
        return array(
            /* ---------------- Redis 1.2 ---------------- */

            /* commands operating on the key space */
            'EXISTS' => 'Predis\Command\KeyExists',
            'DEL' => 'Predis\Command\KeyDelete',
            'TYPE' => 'Predis\Command\KeyType',
            'KEYS' => 'Predis\Command\KeyKeys',
            'RANDOMKEY' => 'Predis\Command\KeyRandom',
            'RENAME' => 'Predis\Command\KeyRename',
            'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
            'EXPIRE' => 'Predis\Command\KeyExpire',
            'EXPIREAT' => 'Predis\Command\KeyExpireAt',
            'TTL' => 'Predis\Command\KeyTimeToLive',
            'MOVE' => 'Predis\Command\KeyMove',
            'SORT' => 'Predis\Command\KeySort',
            'DUMP' => 'Predis\Command\KeyDump',
            'RESTORE' => 'Predis\Command\KeyRestore',

            /* commands operating on string values */
            'SET' => 'Predis\Command\StringSet',
            'SETNX' => 'Predis\Command\StringSetPreserve',
            'MSET' => 'Predis\Command\StringSetMultiple',
            'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
            'GET' => 'Predis\Command\StringGet',
            'MGET' => 'Predis\Command\StringGetMultiple',
            'GETSET' => 'Predis\Command\StringGetSet',
            'INCR' => 'Predis\Command\StringIncrement',
            'INCRBY' => 'Predis\Command\StringIncrementBy',
            'DECR' => 'Predis\Command\StringDecrement',
            'DECRBY' => 'Predis\Command\StringDecrementBy',

            /* commands operating on lists */
            'RPUSH' => 'Predis\Command\ListPushTail',
            'LPUSH' => 'Predis\Command\ListPushHead',
            'LLEN' => 'Predis\Command\ListLength',
            'LRANGE' => 'Predis\Command\ListRange',
            'LTRIM' => 'Predis\Command\ListTrim',
            'LINDEX' => 'Predis\Command\ListIndex',
            'LSET' => 'Predis\Command\ListSet',
            'LREM' => 'Predis\Command\ListRemove',
            'LPOP' => 'Predis\Command\ListPopFirst',
            'RPOP' => 'Predis\Command\ListPopLast',
            'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',

            /* commands operating on sets */
            'SADD' => 'Predis\Command\SetAdd',
            'SREM' => 'Predis\Command\SetRemove',
            'SPOP' => 'Predis\Command\SetPop',
            'SMOVE' => 'Predis\Command\SetMove',
            'SCARD' => 'Predis\Command\SetCardinality',
            'SISMEMBER' => 'Predis\Command\SetIsMember',
            'SINTER' => 'Predis\Command\SetIntersection',
            'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
            'SUNION' => 'Predis\Command\SetUnion',
            'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
            'SDIFF' => 'Predis\Command\SetDifference',
            'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
            'SMEMBERS' => 'Predis\Command\SetMembers',
            'SRANDMEMBER' => 'Predis\Command\SetRandomMember',

            /* commands operating on sorted sets */
            'ZADD' => 'Predis\Command\ZSetAdd',
            'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
            'ZREM' => 'Predis\Command\ZSetRemove',
            'ZRANGE' => 'Predis\Command\ZSetRange',
            'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
            'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
            'ZCARD' => 'Predis\Command\ZSetCardinality',
            'ZSCORE' => 'Predis\Command\ZSetScore',
            'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',

            /* connection related commands */
            'PING' => 'Predis\Command\ConnectionPing',
            'AUTH' => 'Predis\Command\ConnectionAuth',
            'SELECT' => 'Predis\Command\ConnectionSelect',
            'ECHO' => 'Predis\Command\ConnectionEcho',
            'QUIT' => 'Predis\Command\ConnectionQuit',

            /* remote server control commands */
            'INFO' => 'Predis\Command\ServerInfoV26x',
            'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
            'MONITOR' => 'Predis\Command\ServerMonitor',
            'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
            'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
            'FLUSHALL' => 'Predis\Command\ServerFlushAll',
            'SAVE' => 'Predis\Command\ServerSave',
            'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
            'LASTSAVE' => 'Predis\Command\ServerLastSave',
            'SHUTDOWN' => 'Predis\Command\ServerShutdown',
            'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',

            /* ---------------- Redis 2.0 ---------------- */

            /* commands operating on string values */
            'SETEX' => 'Predis\Command\StringSetExpire',
            'APPEND' => 'Predis\Command\StringAppend',
            'SUBSTR' => 'Predis\Command\StringSubstr',

            /* commands operating on lists */
            'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
            'BRPOP' => 'Predis\Command\ListPopLastBlocking',

            /* commands operating on sorted sets */
            'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
            'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
            'ZCOUNT' => 'Predis\Command\ZSetCount',
            'ZRANK' => 'Predis\Command\ZSetRank',
            'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
            'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',

            /* commands operating on hashes */
            'HSET' => 'Predis\Command\HashSet',
            'HSETNX' => 'Predis\Command\HashSetPreserve',
            'HMSET' => 'Predis\Command\HashSetMultiple',
            'HINCRBY' => 'Predis\Command\HashIncrementBy',
            'HGET' => 'Predis\Command\HashGet',
            'HMGET' => 'Predis\Command\HashGetMultiple',
            'HDEL' => 'Predis\Command\HashDelete',
            'HEXISTS' => 'Predis\Command\HashExists',
            'HLEN' => 'Predis\Command\HashLength',
            'HKEYS' => 'Predis\Command\HashKeys',
            'HVALS' => 'Predis\Command\HashValues',
            'HGETALL' => 'Predis\Command\HashGetAll',

            /* transactions */
            'MULTI' => 'Predis\Command\TransactionMulti',
            'EXEC' => 'Predis\Command\TransactionExec',
            'DISCARD' => 'Predis\Command\TransactionDiscard',

            /* publish - subscribe */
            'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
            'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
            'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
            'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
            'PUBLISH' => 'Predis\Command\PubSubPublish',

            /* remote server control commands */
            'CONFIG' => 'Predis\Command\ServerConfig',

            /* ---------------- Redis 2.2 ---------------- */

            /* commands operating on the key space */
            'PERSIST' => 'Predis\Command\KeyPersist',

            /* commands operating on string values */
            'STRLEN' => 'Predis\Command\StringStrlen',
            'SETRANGE' => 'Predis\Command\StringSetRange',
            'GETRANGE' => 'Predis\Command\StringGetRange',
            'SETBIT' => 'Predis\Command\StringSetBit',
            'GETBIT' => 'Predis\Command\StringGetBit',

            /* commands operating on lists */
            'RPUSHX' => 'Predis\Command\ListPushTailX',
            'LPUSHX' => 'Predis\Command\ListPushHeadX',
            'LINSERT' => 'Predis\Command\ListInsert',
            'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',

            /* commands operating on sorted sets */
            'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',

            /* transactions */
            'WATCH' => 'Predis\Command\TransactionWatch',
            'UNWATCH' => 'Predis\Command\TransactionUnwatch',

            /* remote server control commands */
            'OBJECT' => 'Predis\Command\ServerObject',
            'SLOWLOG' => 'Predis\Command\ServerSlowlog',

            /* ---------------- Redis 2.4 ---------------- */

            /* remote server control commands */
            'CLIENT' => 'Predis\Command\ServerClient',

            /* ---------------- Redis 2.6 ---------------- */

            /* commands operating on the key space */
            'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
            'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
            'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
            'MIGRATE' => 'Predis\Command\KeyMigrate',

            /* commands operating on string values */
            'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
            'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
            'BITOP' => 'Predis\Command\StringBitOp',
            'BITCOUNT' => 'Predis\Command\StringBitCount',

            /* commands operating on hashes */
            'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',

            /* scripting */
            'EVAL' => 'Predis\Command\ServerEval',
            'EVALSHA' => 'Predis\Command\ServerEvalSHA',
            'SCRIPT' => 'Predis\Command\ServerScript',

            /* remote server control commands */
            'TIME' => 'Predis\Command\ServerTime',
            'SENTINEL' => 'Predis\Command\ServerSentinel',

            /* ---------------- Redis 2.8 ---------------- */

            /* commands operating on the key space */
            'SCAN' => 'Predis\Command\KeyScan',

            /* commands operating on string values */
            'BITPOS' => 'Predis\Command\StringBitPos',

            /* commands operating on sets */
            'SSCAN' => 'Predis\Command\SetScan',

            /* commands operating on sorted sets */
            'ZSCAN' => 'Predis\Command\ZSetScan',
            'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount',
            'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex',
            'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex',
            'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex',

            /* commands operating on hashes */
            'HSCAN' => 'Predis\Command\HashScan',

            /* publish - subscribe */
            'PUBSUB' => 'Predis\Command\PubSubPubsub',

            /* commands operating on HyperLogLog */
            'PFADD' => 'Predis\Command\HyperLogLogAdd',
            'PFCOUNT' => 'Predis\Command\HyperLogLogCount',
            'PFMERGE' => 'Predis\Command\HyperLogLogMerge',

            /* remote server control commands */
            'COMMAND' => 'Predis\Command\ServerCommand',

            /* ---------------- Redis 3.0 ---------------- */

        );
    }
}


code:
<?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\Profile;

/**
 * Server profile for Redis 3.0.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class RedisVersion320 extends RedisProfile
{
    /**
     * {@inheritdoc}
     */
    public function getVersion()
    {
        return '3.2';
    }

    /**
     * {@inheritdoc}
     */
    public function getSupportedCommands()
    {
        return array(
            /* ---------------- Redis 1.2 ---------------- */

            /* commands operating on the key space */
            'EXISTS' => 'Predis\Command\KeyExists',
            'DEL' => 'Predis\Command\KeyDelete',
            'TYPE' => 'Predis\Command\KeyType',
            'KEYS' => 'Predis\Command\KeyKeys',
            'RANDOMKEY' => 'Predis\Command\KeyRandom',
            'RENAME' => 'Predis\Command\KeyRename',
            'RENAMENX' => 'Predis\Command\KeyRenamePreserve',
            'EXPIRE' => 'Predis\Command\KeyExpire',
            'EXPIREAT' => 'Predis\Command\KeyExpireAt',
            'TTL' => 'Predis\Command\KeyTimeToLive',
            'MOVE' => 'Predis\Command\KeyMove',
            'SORT' => 'Predis\Command\KeySort',
            'DUMP' => 'Predis\Command\KeyDump',
            'RESTORE' => 'Predis\Command\KeyRestore',

            /* commands operating on string values */
            'SET' => 'Predis\Command\StringSet',
            'SETNX' => 'Predis\Command\StringSetPreserve',
            'MSET' => 'Predis\Command\StringSetMultiple',
            'MSETNX' => 'Predis\Command\StringSetMultiplePreserve',
            'GET' => 'Predis\Command\StringGet',
            'MGET' => 'Predis\Command\StringGetMultiple',
            'GETSET' => 'Predis\Command\StringGetSet',
            'INCR' => 'Predis\Command\StringIncrement',
            'INCRBY' => 'Predis\Command\StringIncrementBy',
            'DECR' => 'Predis\Command\StringDecrement',
            'DECRBY' => 'Predis\Command\StringDecrementBy',

            /* commands operating on lists */
            'RPUSH' => 'Predis\Command\ListPushTail',
            'LPUSH' => 'Predis\Command\ListPushHead',
            'LLEN' => 'Predis\Command\ListLength',
            'LRANGE' => 'Predis\Command\ListRange',
            'LTRIM' => 'Predis\Command\ListTrim',
            'LINDEX' => 'Predis\Command\ListIndex',
            'LSET' => 'Predis\Command\ListSet',
            'LREM' => 'Predis\Command\ListRemove',
            'LPOP' => 'Predis\Command\ListPopFirst',
            'RPOP' => 'Predis\Command\ListPopLast',
            'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead',

            /* commands operating on sets */
            'SADD' => 'Predis\Command\SetAdd',
            'SREM' => 'Predis\Command\SetRemove',
            'SPOP' => 'Predis\Command\SetPop',
            'SMOVE' => 'Predis\Command\SetMove',
            'SCARD' => 'Predis\Command\SetCardinality',
            'SISMEMBER' => 'Predis\Command\SetIsMember',
            'SINTER' => 'Predis\Command\SetIntersection',
            'SINTERSTORE' => 'Predis\Command\SetIntersectionStore',
            'SUNION' => 'Predis\Command\SetUnion',
            'SUNIONSTORE' => 'Predis\Command\SetUnionStore',
            'SDIFF' => 'Predis\Command\SetDifference',
            'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore',
            'SMEMBERS' => 'Predis\Command\SetMembers',
            'SRANDMEMBER' => 'Predis\Command\SetRandomMember',

            /* commands operating on sorted sets */
            'ZADD' => 'Predis\Command\ZSetAdd',
            'ZINCRBY' => 'Predis\Command\ZSetIncrementBy',
            'ZREM' => 'Predis\Command\ZSetRemove',
            'ZRANGE' => 'Predis\Command\ZSetRange',
            'ZREVRANGE' => 'Predis\Command\ZSetReverseRange',
            'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore',
            'ZCARD' => 'Predis\Command\ZSetCardinality',
            'ZSCORE' => 'Predis\Command\ZSetScore',
            'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore',

            /* connection related commands */
            'PING' => 'Predis\Command\ConnectionPing',
            'AUTH' => 'Predis\Command\ConnectionAuth',
            'SELECT' => 'Predis\Command\ConnectionSelect',
            'ECHO' => 'Predis\Command\ConnectionEcho',
            'QUIT' => 'Predis\Command\ConnectionQuit',

            /* remote server control commands */
            'INFO' => 'Predis\Command\ServerInfoV26x',
            'SLAVEOF' => 'Predis\Command\ServerSlaveOf',
            'MONITOR' => 'Predis\Command\ServerMonitor',
            'DBSIZE' => 'Predis\Command\ServerDatabaseSize',
            'FLUSHDB' => 'Predis\Command\ServerFlushDatabase',
            'FLUSHALL' => 'Predis\Command\ServerFlushAll',
            'SAVE' => 'Predis\Command\ServerSave',
            'BGSAVE' => 'Predis\Command\ServerBackgroundSave',
            'LASTSAVE' => 'Predis\Command\ServerLastSave',
            'SHUTDOWN' => 'Predis\Command\ServerShutdown',
            'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF',

            /* ---------------- Redis 2.0 ---------------- */

            /* commands operating on string values */
            'SETEX' => 'Predis\Command\StringSetExpire',
            'APPEND' => 'Predis\Command\StringAppend',
            'SUBSTR' => 'Predis\Command\StringSubstr',

            /* commands operating on lists */
            'BLPOP' => 'Predis\Command\ListPopFirstBlocking',
            'BRPOP' => 'Predis\Command\ListPopLastBlocking',

            /* commands operating on sorted sets */
            'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore',
            'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore',
            'ZCOUNT' => 'Predis\Command\ZSetCount',
            'ZRANK' => 'Predis\Command\ZSetRank',
            'ZREVRANK' => 'Predis\Command\ZSetReverseRank',
            'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank',

            /* commands operating on hashes */
            'HSET' => 'Predis\Command\HashSet',
            'HSETNX' => 'Predis\Command\HashSetPreserve',
            'HMSET' => 'Predis\Command\HashSetMultiple',
            'HINCRBY' => 'Predis\Command\HashIncrementBy',
            'HGET' => 'Predis\Command\HashGet',
            'HMGET' => 'Predis\Command\HashGetMultiple',
            'HDEL' => 'Predis\Command\HashDelete',
            'HEXISTS' => 'Predis\Command\HashExists',
            'HLEN' => 'Predis\Command\HashLength',
            'HKEYS' => 'Predis\Command\HashKeys',
            'HVALS' => 'Predis\Command\HashValues',
            'HGETALL' => 'Predis\Command\HashGetAll',

            /* transactions */
            'MULTI' => 'Predis\Command\TransactionMulti',
            'EXEC' => 'Predis\Command\TransactionExec',
            'DISCARD' => 'Predis\Command\TransactionDiscard',

            /* publish - subscribe */
            'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe',
            'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe',
            'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern',
            'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern',
            'PUBLISH' => 'Predis\Command\PubSubPublish',

            /* remote server control commands */
            'CONFIG' => 'Predis\Command\ServerConfig',

            /* ---------------- Redis 2.2 ---------------- */

            /* commands operating on the key space */
            'PERSIST' => 'Predis\Command\KeyPersist',

            /* commands operating on string values */
            'STRLEN' => 'Predis\Command\StringStrlen',
            'SETRANGE' => 'Predis\Command\StringSetRange',
            'GETRANGE' => 'Predis\Command\StringGetRange',
            'SETBIT' => 'Predis\Command\StringSetBit',
            'GETBIT' => 'Predis\Command\StringGetBit',

            /* commands operating on lists */
            'RPUSHX' => 'Predis\Command\ListPushTailX',
            'LPUSHX' => 'Predis\Command\ListPushHeadX',
            'LINSERT' => 'Predis\Command\ListInsert',
            'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking',

            /* commands operating on sorted sets */
            'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore',

            /* transactions */
            'WATCH' => 'Predis\Command\TransactionWatch',
            'UNWATCH' => 'Predis\Command\TransactionUnwatch',

            /* remote server control commands */
            'OBJECT' => 'Predis\Command\ServerObject',
            'SLOWLOG' => 'Predis\Command\ServerSlowlog',

            /* ---------------- Redis 2.4 ---------------- */

            /* remote server control commands */
            'CLIENT' => 'Predis\Command\ServerClient',

            /* ---------------- Redis 2.6 ---------------- */

            /* commands operating on the key space */
            'PTTL' => 'Predis\Command\KeyPreciseTimeToLive',
            'PEXPIRE' => 'Predis\Command\KeyPreciseExpire',
            'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt',
            'MIGRATE' => 'Predis\Command\KeyMigrate',

            /* commands operating on string values */
            'PSETEX' => 'Predis\Command\StringPreciseSetExpire',
            'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat',
            'BITOP' => 'Predis\Command\StringBitOp',
            'BITCOUNT' => 'Predis\Command\StringBitCount',

            /* commands operating on hashes */
            'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat',

            /* scripting */
            'EVAL' => 'Predis\Command\ServerEval',
            'EVALSHA' => 'Predis\Command\ServerEvalSHA',
            'SCRIPT' => 'Predis\Command\ServerScript',

            /* remote server control commands */
            'TIME' => 'Predis\Command\ServerTime',
            'SENTINEL' => 'Predis\Command\ServerSentinel',

            /* ---------------- Redis 2.8 ---------------- */

            /* commands operating on the key space */
            'SCAN' => 'Predis\Command\KeyScan',

            /* commands operating on string values */
            'BITPOS' => 'Predis\Command\StringBitPos',

            /* commands operating on sets */
            'SSCAN' => 'Predis\Command\SetScan',

            /* commands operating on sorted sets */
            'ZSCAN' => 'Predis\Command\ZSetScan',
            'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount',
            'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex',
            'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex',
            'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex',

            /* commands operating on hashes */
            'HSCAN' => 'Predis\Command\HashScan',

            /* publish - subscribe */
            'PUBSUB' => 'Predis\Command\PubSubPubsub',

            /* commands operating on HyperLogLog */
            'PFADD' => 'Predis\Command\HyperLogLogAdd',
            'PFCOUNT' => 'Predis\Command\HyperLogLogCount',
            'PFMERGE' => 'Predis\Command\HyperLogLogMerge',

            /* remote server control commands */
            'COMMAND' => 'Predis\Command\ServerCommand',

            /* ---------------- Redis 3.2 ---------------- */

            /* commands operating on hashes */
            'HSTRLEN' => 'Predis\Command\HashStringLength',
            'BITFIELD' => 'Predis\Command\StringBitField',

            /* commands performing geospatial operations */
            'GEOADD' => 'Predis\Command\GeospatialGeoAdd',
            'GEOHASH' => 'Predis\Command\GeospatialGeoHash',
            'GEOPOS' => 'Predis\Command\GeospatialGeoPos',
            'GEODIST' => 'Predis\Command\GeospatialGeoDist',
            'GEORADIUS' => 'Predis\Command\GeospatialGeoRadius',
            'GEORADIUSBYMEMBER' => 'Predis\Command\GeospatialGeoRadiusByMember',
        );
    }
}


code:
<?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\Protocol;

use Predis\CommunicationException;

/**
 * Exception used to indentify errors encountered while parsing the Redis wire
 * protocol.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class ProtocolException extends CommunicationException
{
}


code:
<?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\Protocol;

use Predis\Command\CommandInterface;
use Predis\Connection\CompositeConnectionInterface;

/**
 * Defines a pluggable protocol processor capable of serializing commands and
 * deserializing responses into PHP objects directly from a connection.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
interface ProtocolProcessorInterface
{
    /**
     * Writes a request over a connection to Redis.
     *
     * @param CompositeConnectionInterface $connection Redis connection.
     * @param CommandInterface             $command    Command instance.
     */
    public function write(CompositeConnectionInterface $connection, CommandInterface $command);

    /**
     * Reads a response from a connection to Redis.
     *
     * @param CompositeConnectionInterface $connection Redis connection.
     *
     * @return mixed
     */
    public function read(CompositeConnectionInterface $connection);
}


code:
<?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\Protocol;

use Predis\Command\CommandInterface;

/**
 * Defines a pluggable serializer for Redis commands.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
interface RequestSerializerInterface
{
    /**
     * Serializes a Redis command.
     *
     * @param CommandInterface $command Redis command.
     *
     * @return string
     */
    public function serialize(CommandInterface $command);
}


code:
<?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\Protocol;

use Predis\Connection\CompositeConnectionInterface;

/**
 * Defines a pluggable reader capable of parsing responses returned by Redis and
 * deserializing them to PHP objects.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
interface ResponseReaderInterface
{
    /**
     * Reads a response from a connection to Redis.
     *
     * @param CompositeConnectionInterface $connection Redis connection.
     *
     * @return mixed
     */
    public function read(CompositeConnectionInterface $connection);
}


code:
<?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\Protocol\Text;

use Predis\Command\CommandInterface;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolProcessorInterface;
use Predis\Protocol\RequestSerializerInterface;
use Predis\Protocol\ResponseReaderInterface;

/**
 * Composite protocol processor for the standard Redis wire protocol using
 * pluggable handlers to serialize requests and deserialize responses.
 *
 * @link http://redis.io/topics/protocol
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class CompositeProtocolProcessor implements ProtocolProcessorInterface
{
    /*
     * @var RequestSerializerInterface
     */
    protected $serializer;

    /*
     * @var ResponseReaderInterface
     */
    protected $reader;

    /**
     * @param RequestSerializerInterface $serializer Request serializer.
     * @param ResponseReaderInterface    $reader     Response reader.
     */
    public function __construct(
        RequestSerializerInterface $serializer = null,
        ResponseReaderInterface $reader = null
    ) {
        $this->setRequestSerializer($serializer ?: new RequestSerializer());
        $this->setResponseReader($reader ?: new ResponseReader());
    }

    /**
     * {@inheritdoc}
     */
    public function write(CompositeConnectionInterface $connection, CommandInterface $command)
    {
        $connection->writeBuffer($this->serializer->serialize($command));
    }

    /**
     * {@inheritdoc}
     */
    public function read(CompositeConnectionInterface $connection)
    {
        return $this->reader->read($connection);
    }

    /**
     * Sets the request serializer used by the protocol processor.
     *
     * @param RequestSerializerInterface $serializer Request serializer.
     */
    public function setRequestSerializer(RequestSerializerInterface $serializer)
    {
        $this->serializer = $serializer;
    }

    /**
     * Returns the request serializer used by the protocol processor.
     *
     * @return RequestSerializerInterface
     */
    public function getRequestSerializer()
    {
        return $this->serializer;
    }

    /**
     * Sets the response reader used by the protocol processor.
     *
     * @param ResponseReaderInterface $reader Response reader.
     */
    public function setResponseReader(ResponseReaderInterface $reader)
    {
        $this->reader = $reader;
    }

    /**
     * Returns the Response reader used by the protocol processor.
     *
     * @return ResponseReaderInterface
     */
    public function getResponseReader()
    {
        return $this->reader;
    }
}


code:
<?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\Protocol\Text\Handler;

use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;

/**
 * Handler for the bulk response type in the standard Redis wire protocol.
 * It translates the payload to a string or a NULL.
 *
 * @link http://redis.io/topics/protocol
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class BulkResponse implements ResponseHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public function handle(CompositeConnectionInterface $connection, $payload)
    {
        $length = (int) $payload;

        if ("$length" !== $payload) {
            CommunicationException::handle(new ProtocolException(
                $connection, "Cannot parse '$payload' as a valid length for a bulk response."
            ));
        }

        if ($length >= 0) {
            return substr($connection->readBuffer($length + 2), 0, -2);
        }

        if ($length == -1) {
            return;
        }

        CommunicationException::handle(new ProtocolException(
            $connection, "Value '$payload' is not a valid length for a bulk response."
        ));

        return;
    }
}


code:
<?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\Protocol\Text\Handler;

use Predis\Connection\CompositeConnectionInterface;
use Predis\Response\Error;

/**
 * Handler for the error response type in the standard Redis wire protocol.
 * It translates the payload to a complex response object for Predis.
 *
 * @link http://redis.io/topics/protocol
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class ErrorResponse implements ResponseHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public function handle(CompositeConnectionInterface $connection, $payload)
    {
        return new Error($payload);
    }
}


code:
<?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\Protocol\Text\Handler;

use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;

/**
 * Handler for the integer response type in the standard Redis wire protocol.
 * It translates the payload an integer or NULL.
 *
 * @link http://redis.io/topics/protocol
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class IntegerResponse implements ResponseHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public function handle(CompositeConnectionInterface $connection, $payload)
    {
        if (is_numeric($payload)) {
            $integer = (int) $payload;
            return $integer == $payload ? $integer : $payload;
        }

        if ($payload !== 'nil') {
            CommunicationException::handle(new ProtocolException(
                $connection, "Cannot parse '$payload' as a valid numeric response."
            ));
        }

        return;
    }
}


code:
<?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\Protocol\Text\Handler;

use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;

/**
 * Handler for the multibulk response type in the standard Redis wire protocol.
 * It returns multibulk responses as PHP arrays.
 *
 * @link http://redis.io/topics/protocol
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class MultiBulkResponse implements ResponseHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public function handle(CompositeConnectionInterface $connection, $payload)
    {
        $length = (int) $payload;

        if ("$length" !== $payload) {
            CommunicationException::handle(new ProtocolException(
                $connection, "Cannot parse '$payload' as a valid length of a multi-bulk response."
            ));
        }

        if ($length === -1) {
            return;
        }

        $list = array();

        if ($length > 0) {
            $handlersCache = array();
            $reader = $connection->getProtocol()->getResponseReader();

            for ($i = 0; $i < $length; ++$i) {
                $header = $connection->readLine();
                $prefix = $header[0];

                if (isset($handlersCache[$prefix])) {
                    $handler = $handlersCache[$prefix];
                } else {
                    $handler = $reader->getHandler($prefix);
                    $handlersCache[$prefix] = $handler;
                }

                $list[$i] = $handler->handle($connection, substr($header, 1));
            }
        }

        return $list;
    }
}


code:
<?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\Protocol\Text\Handler;

use Predis\Connection\CompositeConnectionInterface;

/**
 * Defines a pluggable handler used to parse a particular type of response.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
interface ResponseHandlerInterface
{
    /**
     * Deserializes a response returned by Redis and reads more data from the
     * connection if needed.
     *
     * @param CompositeConnectionInterface $connection Redis connection.
     * @param string                       $payload    String payload.
     *
     * @return mixed
     */
    public function handle(CompositeConnectionInterface $connection, $payload);
}


code:
<?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\Protocol\Text\Handler;

use Predis\Connection\CompositeConnectionInterface;
use Predis\Response\Status;

/**
 * Handler for the status response type in the standard Redis wire protocol. It
 * translates certain classes of status response to PHP objects or just returns
 * the payload as a string.
 *
 * @link http://redis.io/topics/protocol
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class StatusResponse implements ResponseHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public function handle(CompositeConnectionInterface $connection, $payload)
    {
        return Status::get($payload);
    }
}


code:
<?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\Protocol\Text\Handler;

use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;
use Predis\Response\Iterator\MultiBulk as MultiBulkIterator;

/**
 * Handler for the multibulk response type in the standard Redis wire protocol.
 * It returns multibulk responses as iterators that can stream bulk elements.
 *
 * Streamable multibulk responses are not globally supported by the abstractions
 * built-in into Predis, such as transactions or pipelines. Use them with care!
 *
 * @link http://redis.io/topics/protocol
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class StreamableMultiBulkResponse implements ResponseHandlerInterface
{
    /**
     * {@inheritdoc}
     */
    public function handle(CompositeConnectionInterface $connection, $payload)
    {
        $length = (int) $payload;

        if ("$length" != $payload) {
            CommunicationException::handle(new ProtocolException(
                $connection, "Cannot parse '$payload' as a valid length for a multi-bulk response."
            ));
        }

        return new MultiBulkIterator($connection, $length);
    }
}


code:
<?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\Protocol\Text;

use Predis\Command\CommandInterface;
use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;
use Predis\Protocol\ProtocolProcessorInterface;
use Predis\Response\Error as ErrorResponse;
use Predis\Response\Iterator\MultiBulk as MultiBulkIterator;
use Predis\Response\Status as StatusResponse;

/**
 * Protocol processor for the standard Redis wire protocol.
 *
 * @link http://redis.io/topics/protocol
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class ProtocolProcessor implements ProtocolProcessorInterface
{
    protected $mbiterable;
    protected $serializer;

    /**
     *
     */
    public function __construct()
    {
        $this->mbiterable = false;
        $this->serializer = new RequestSerializer();
    }

    /**
     * {@inheritdoc}
     */
    public function write(CompositeConnectionInterface $connection, CommandInterface $command)
    {
        $request = $this->serializer->serialize($command);
        $connection->writeBuffer($request);
    }

    /**
     * {@inheritdoc}
     */
    public function read(CompositeConnectionInterface $connection)
    {
        $chunk = $connection->readLine();
        $prefix = $chunk[0];
        $payload = substr($chunk, 1);

        switch ($prefix) {
            case '+':
                return new StatusResponse($payload);

            case '$':
                $size = (int) $payload;
                if ($size === -1) {
                    return;
                }

                return substr($connection->readBuffer($size + 2), 0, -2);

            case '*':
                $count = (int) $payload;

                if ($count === -1) {
                    return;
                }
                if ($this->mbiterable) {
                    return new MultiBulkIterator($connection, $count);
                }

                $multibulk = array();

                for ($i = 0; $i < $count; ++$i) {
                    $multibulk[$i] = $this->read($connection);
                }

                return $multibulk;

            case ':':
                $integer = (int) $payload;
                return $integer == $payload ? $integer : $payload;

            case '-':
                return new ErrorResponse($payload);

            default:
                CommunicationException::handle(new ProtocolException(
                    $connection, "Unknown response prefix: '$prefix'."
                ));

                return;
        }
    }

    /**
     * Enables or disables returning multibulk responses as specialized PHP
     * iterators used to stream bulk elements of a multibulk response instead
     * returning a plain array.
     *
     * Streamable multibulk responses are not globally supported by the
     * abstractions built-in into Predis, such as transactions or pipelines.
     * Use them with care!
     *
     * @param bool $value Enable or disable streamable multibulk responses.
     */
    public function useIterableMultibulk($value)
    {
        $this->mbiterable = (bool) $value;
    }
}


code:
<?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\Protocol\Text;

use Predis\Command\CommandInterface;
use Predis\Protocol\RequestSerializerInterface;

/**
 * Request serializer for the standard Redis wire protocol.
 *
 * @link http://redis.io/topics/protocol
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class RequestSerializer implements RequestSerializerInterface
{
    /**
     * {@inheritdoc}
     */
    public function serialize(CommandInterface $command)
    {
        $commandID = $command->getId();
        $arguments = $command->getArguments();

        $cmdlen = strlen($commandID);
        $reqlen = count($arguments) + 1;

        $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n";

        foreach ($arguments as $argument) {
            $arglen = strlen($argument);
            $buffer .= "\${$arglen}\r\n{$argument}\r\n";
        }

        return $buffer;
    }
}


code:
<?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\Protocol\Text;

use Predis\CommunicationException;
use Predis\Connection\CompositeConnectionInterface;
use Predis\Protocol\ProtocolException;
use Predis\Protocol\ResponseReaderInterface;

/**
 * Response reader for the standard Redis wire protocol.
 *
 * @link http://redis.io/topics/protocol
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class ResponseReader implements ResponseReaderInterface
{
    protected $handlers;

    /**
     *
     */
    public function __construct()
    {
        $this->handlers = $this->getDefaultHandlers();
    }

    /**
     * Returns the default handlers for the supported type of responses.
     *
     * @return array
     */
    protected function getDefaultHandlers()
    {
        return array(
            '+' => new Handler\StatusResponse(),
            '-' => new Handler\ErrorResponse(),
            ':' => new Handler\IntegerResponse(),
            '$' => new Handler\BulkResponse(),
            '*' => new Handler\MultiBulkResponse(),
        );
    }

    /**
     * Sets the handler for the specified prefix identifying the response type.
     *
     * @param string                           $prefix  Identifier of the type of response.
     * @param Handler\ResponseHandlerInterface $handler Response handler.
     */
    public function setHandler($prefix, Handler\ResponseHandlerInterface $handler)
    {
        $this->handlers[$prefix] = $handler;
    }

    /**
     * Returns the response handler associated to a certain type of response.
     *
     * @param string $prefix Identifier of the type of response.
     *
     * @return Handler\ResponseHandlerInterface
     */
    public function getHandler($prefix)
    {
        if (isset($this->handlers[$prefix])) {
            return $this->handlers[$prefix];
        }

        return;
    }

    /**
     * {@inheritdoc}
     */
    public function read(CompositeConnectionInterface $connection)
    {
        $header = $connection->readLine();

        if ($header === '') {
            $this->onProtocolError($connection, 'Unexpected empty reponse header.');
        }

        $prefix = $header[0];

        if (!isset($this->handlers[$prefix])) {
            $this->onProtocolError($connection, "Unknown response prefix: '$prefix'.");
        }

        $payload = $this->handlers[$prefix]->handle($connection, substr($header, 1));

        return $payload;
    }

    /**
     * Handles protocol errors generated while reading responses from a
     * connection.
     *
     * @param CompositeConnectionInterface $connection Redis connection that generated the error.
     * @param string                       $message    Error message.
     */
    protected function onProtocolError(CompositeConnectionInterface $connection, $message)
    {
        CommunicationException::handle(
            new ProtocolException($connection, $message)
        );
    }
}


code:
<?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\PubSub;

/**
 * Base implementation of a PUB/SUB consumer abstraction based on PHP iterators.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
abstract class AbstractConsumer implements \Iterator
{
    const SUBSCRIBE = 'subscribe';
    const UNSUBSCRIBE = 'unsubscribe';
    const PSUBSCRIBE = 'psubscribe';
    const PUNSUBSCRIBE = 'punsubscribe';
    const MESSAGE = 'message';
    const PMESSAGE = 'pmessage';
    const PONG = 'pong';

    const STATUS_VALID = 1;       // 0b0001
    const STATUS_SUBSCRIBED = 2;  // 0b0010
    const STATUS_PSUBSCRIBED = 4; // 0b0100

    private $position = null;
    private $statusFlags = self::STATUS_VALID;

    /**
     * Automatically stops the consumer when the garbage collector kicks in.
     */
    public function __destruct()
    {
        $this->stop(true);
    }

    /**
     * Checks if the specified flag is valid based on the state of the consumer.
     *
     * @param int $value Flag.
     *
     * @return bool
     */
    protected function isFlagSet($value)
    {
        return ($this->statusFlags & $value) === $value;
    }

    /**
     * Subscribes to the specified channels.
     *
     * @param mixed $channel,... One or more channel names.
     */
    public function subscribe($channel /*, ... */)
    {
        $this->writeRequest(self::SUBSCRIBE, func_get_args());
        $this->statusFlags |= self::STATUS_SUBSCRIBED;
    }

    /**
     * Unsubscribes from the specified channels.
     *
     * @param string ... One or more channel names.
     */
    public function unsubscribe(/* ... */)
    {
        $this->writeRequest(self::UNSUBSCRIBE, func_get_args());
    }

    /**
     * Subscribes to the specified channels using a pattern.
     *
     * @param mixed $pattern,... One or more channel name patterns.
     */
    public function psubscribe($pattern /* ... */)
    {
        $this->writeRequest(self::PSUBSCRIBE, func_get_args());
        $this->statusFlags |= self::STATUS_PSUBSCRIBED;
    }

    /**
     * Unsubscribes from the specified channels using a pattern.
     *
     * @param string ... One or more channel name patterns.
     */
    public function punsubscribe(/* ... */)
    {
        $this->writeRequest(self::PUNSUBSCRIBE, func_get_args());
    }

    /**
     * PING the server with an optional payload that will be echoed as a
     * PONG message in the pub/sub loop.
     *
     * @param string $payload Optional PING payload.
     */
    public function ping($payload = null)
    {
        $this->writeRequest('PING', array($payload));
    }

    /**
     * Closes the context by unsubscribing from all the subscribed channels. The
     * context can be forcefully closed by dropping the underlying connection.
     *
     * @param bool $drop Indicates if the context should be closed by dropping the connection.
     *
     * @return bool Returns false when there are no pending messages.
     */
    public function stop($drop = false)
    {
        if (!$this->valid()) {
            return false;
        }

        if ($drop) {
            $this->invalidate();
            $this->disconnect();
        } else {
            if ($this->isFlagSet(self::STATUS_SUBSCRIBED)) {
                $this->unsubscribe();
            }
            if ($this->isFlagSet(self::STATUS_PSUBSCRIBED)) {
                $this->punsubscribe();
            }
        }

        return !$drop;
    }

    /**
     * Closes the underlying connection when forcing a disconnection.
     */
    abstract protected function disconnect();

    /**
     * Writes a Redis command on the underlying connection.
     *
     * @param string $method    Command ID.
     * @param array  $arguments Arguments for the command.
     */
    abstract protected function writeRequest($method, $arguments);

    /**
     * {@inheritdoc}
     */
    public function rewind()
    {
        // NOOP
    }

    /**
     * Returns the last message payload retrieved from the server and generated
     * by one of the active subscriptions.
     *
     * @return array
     */
    public function current()
    {
        return $this->getValue();
    }

    /**
     * {@inheritdoc}
     */
    public function key()
    {
        return $this->position;
    }

    /**
     * {@inheritdoc}
     */
    public function next()
    {
        if ($this->valid()) {
            ++$this->position;
        }

        return $this->position;
    }

    /**
     * Checks if the the consumer is still in a valid state to continue.
     *
     * @return bool
     */
    public function valid()
    {
        $isValid = $this->isFlagSet(self::STATUS_VALID);
        $subscriptionFlags = self::STATUS_SUBSCRIBED | self::STATUS_PSUBSCRIBED;
        $hasSubscriptions = ($this->statusFlags & $subscriptionFlags) > 0;

        return $isValid && $hasSubscriptions;
    }

    /**
     * Resets the state of the consumer.
     */
    protected function invalidate()
    {
        $this->statusFlags = 0;    // 0b0000;
    }

    /**
     * Waits for a new message from the server generated by one of the active
     * subscriptions and returns it when available.
     *
     * @return array
     */
    abstract protected function getValue();
}


code:
<?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\PubSub;

use Predis\ClientException;
use Predis\ClientInterface;
use Predis\Command\Command;
use Predis\Connection\AggregateConnectionInterface;
use Predis\NotSupportedException;

/**
 * PUB/SUB consumer abstraction.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class Consumer extends AbstractConsumer
{
    private $client;
    private $options;

    /**
     * @param ClientInterface $client  Client instance used by the consumer.
     * @param array           $options Options for the consumer initialization.
     */
    public function __construct(ClientInterface $client, array $options = null)
    {
        $this->checkCapabilities($client);

        $this->options = $options ?: array();
        $this->client = $client;

        $this->genericSubscribeInit('subscribe');
        $this->genericSubscribeInit('psubscribe');
    }

    /**
     * Returns the underlying client instance used by the pub/sub iterator.
     *
     * @return ClientInterface
     */
    public function getClient()
    {
        return $this->client;
    }

    /**
     * Checks if the client instance satisfies the required conditions needed to
     * initialize a PUB/SUB consumer.
     *
     * @param ClientInterface $client Client instance used by the consumer.
     *
     * @throws NotSupportedException
     */
    private function checkCapabilities(ClientInterface $client)
    {
        if ($client->getConnection() instanceof AggregateConnectionInterface) {
            throw new NotSupportedException(
                'Cannot initialize a PUB/SUB consumer over aggregate connections.'
            );
        }

        $commands = array('publish', 'subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe');

        if ($client->getProfile()->supportsCommands($commands) === false) {
            throw new NotSupportedException(
                'The current profile does not support PUB/SUB related commands.'
            );
        }
    }

    /**
     * This method shares the logic to handle both SUBSCRIBE and PSUBSCRIBE.
     *
     * @param string $subscribeAction Type of subscription.
     */
    private function genericSubscribeInit($subscribeAction)
    {
        if (isset($this->options[$subscribeAction])) {
            $this->$subscribeAction($this->options[$subscribeAction]);
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function writeRequest($method, $arguments)
    {
        $this->client->getConnection()->writeRequest(
            $this->client->createCommand($method,
                Command::normalizeArguments($arguments)
            )
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function disconnect()
    {
        $this->client->disconnect();
    }

    /**
     * {@inheritdoc}
     */
    protected function getValue()
    {
        $response = $this->client->getConnection()->read();

        switch ($response[0]) {
            case self::SUBSCRIBE:
            case self::UNSUBSCRIBE:
            case self::PSUBSCRIBE:
            case self::PUNSUBSCRIBE:
                if ($response[2] === 0) {
                    $this->invalidate();
                }
                // The missing break here is intentional as we must process
                // subscriptions and unsubscriptions as standard messages.
                // no break

            case self::MESSAGE:
                return (object) array(
                    'kind' => $response[0],
                    'channel' => $response[1],
                    'payload' => $response[2],
                );

            case self::PMESSAGE:
                return (object) array(
                    'kind' => $response[0],
                    'pattern' => $response[1],
                    'channel' => $response[2],
                    'payload' => $response[3],
                );

            case self::PONG:
                return (object) array(
                    'kind' => $response[0],
                    'payload' => $response[1],
                );

            default:
                throw new ClientException(
                    "Unknown message type '{$response[0]}' received in the PUB/SUB context."
                );
        }
    }
}


code:
<?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\PubSub;

/**
 * Method-dispatcher loop built around the client-side abstraction of a Redis
 * PUB / SUB context.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class DispatcherLoop
{
    private $pubsub;

    protected $callbacks;
    protected $defaultCallback;
    protected $subscriptionCallback;

    /**
     * @param Consumer $pubsub PubSub consumer instance used by the loop.
     */
    public function __construct(Consumer $pubsub)
    {
        $this->callbacks = array();
        $this->pubsub = $pubsub;
    }

    /**
     * Checks if the passed argument is a valid callback.
     *
     * @param mixed $callable A callback.
     *
     * @throws \InvalidArgumentException
     */
    protected function assertCallback($callable)
    {
        if (!is_callable($callable)) {
            throw new \InvalidArgumentException('The given argument must be a callable object.');
        }
    }

    /**
     * Returns the underlying PUB / SUB context.
     *
     * @return Consumer
     */
    public function getPubSubConsumer()
    {
        return $this->pubsub;
    }

    /**
     * Sets a callback that gets invoked upon new subscriptions.
     *
     * @param mixed $callable A callback.
     */
    public function subscriptionCallback($callable = null)
    {
        if (isset($callable)) {
            $this->assertCallback($callable);
        }

        $this->subscriptionCallback = $callable;
    }

    /**
     * Sets a callback that gets invoked when a message is received on a
     * channel that does not have an associated callback.
     *
     * @param mixed $callable A callback.
     */
    public function defaultCallback($callable = null)
    {
        if (isset($callable)) {
            $this->assertCallback($callable);
        }

        $this->subscriptionCallback = $callable;
    }

    /**
     * Binds a callback to a channel.
     *
     * @param string   $channel  Channel name.
     * @param callable $callback A callback.
     */
    public function attachCallback($channel, $callback)
    {
        $callbackName = $this->getPrefixKeys().$channel;

        $this->assertCallback($callback);
        $this->callbacks[$callbackName] = $callback;
        $this->pubsub->subscribe($channel);
    }

    /**
     * Stops listening to a channel and removes the associated callback.
     *
     * @param string $channel Redis channel.
     */
    public function detachCallback($channel)
    {
        $callbackName = $this->getPrefixKeys().$channel;

        if (isset($this->callbacks[$callbackName])) {
            unset($this->callbacks[$callbackName]);
            $this->pubsub->unsubscribe($channel);
        }
    }

    /**
     * Starts the dispatcher loop.
     */
    public function run()
    {
        foreach ($this->pubsub as $message) {
            $kind = $message->kind;

            if ($kind !== Consumer::MESSAGE && $kind !== Consumer::PMESSAGE) {
                if (isset($this->subscriptionCallback)) {
                    $callback = $this->subscriptionCallback;
                    call_user_func($callback, $message);
                }

                continue;
            }

            if (isset($this->callbacks[$message->channel])) {
                $callback = $this->callbacks[$message->channel];
                call_user_func($callback, $message->payload);
            } elseif (isset($this->defaultCallback)) {
                $callback = $this->defaultCallback;
                call_user_func($callback, $message);
            }
        }
    }

    /**
     * Terminates the dispatcher loop.
     */
    public function stop()
    {
        $this->pubsub->stop();
    }

    /**
     * Return the prefix used for keys.
     *
     * @return string
     */
    protected function getPrefixKeys()
    {
        $options = $this->pubsub->getClient()->getOptions();

        if (isset($options->prefix)) {
            return $options->prefix->getPrefix();
        }

        return '';
    }
}


code:
<?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\Replication;

use Predis\ClientException;

/**
 * Exception class that identifies when master is missing in a replication setup.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class MissingMasterException extends ClientException
{
}


code:
<?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\Replication;

use Predis\Command\CommandInterface;
use Predis\NotSupportedException;

/**
 * Defines a strategy for master/slave replication.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class ReplicationStrategy
{
    protected $disallowed;
    protected $readonly;
    protected $readonlySHA1;

    /**
     *
     */
    public function __construct()
    {
        $this->disallowed = $this->getDisallowedOperations();
        $this->readonly = $this->getReadOnlyOperations();
        $this->readonlySHA1 = array();
    }

    /**
     * Returns if the specified command will perform a read-only operation
     * on Redis or not.
     *
     * @param CommandInterface $command Command instance.
     *
     * @throws NotSupportedException
     *
     * @return bool
     */
    public function isReadOperation(CommandInterface $command)
    {
        if (isset($this->disallowed[$id = $command->getId()])) {
            throw new NotSupportedException(
                "The command '$id' is not allowed in replication mode."
            );
        }

        if (isset($this->readonly[$id])) {
            if (true === $readonly = $this->readonly[$id]) {
                return true;
            }

            return call_user_func($readonly, $command);
        }

        if (($eval = $id === 'EVAL') || $id === 'EVALSHA') {
            $sha1 = $eval ? sha1($command->getArgument(0)) : $command->getArgument(0);

            if (isset($this->readonlySHA1[$sha1])) {
                if (true === $readonly = $this->readonlySHA1[$sha1]) {
                    return true;
                }

                return call_user_func($readonly, $command);
            }
        }

        return false;
    }

    /**
     * Returns if the specified command is not allowed for execution in a master
     * / slave replication context.
     *
     * @param CommandInterface $command Command instance.
     *
     * @return bool
     */
    public function isDisallowedOperation(CommandInterface $command)
    {
        return isset($this->disallowed[$command->getId()]);
    }

    /**
     * Checks if a SORT command is a readable operation by parsing the arguments
     * array of the specified commad instance.
     *
     * @param CommandInterface $command Command instance.
     *
     * @return bool
     */
    protected function isSortReadOnly(CommandInterface $command)
    {
        $arguments = $command->getArguments();
        $argc = count($arguments);

        if ($argc > 1) {
            for ($i = 1; $i < $argc; ++$i) {
                $argument = strtoupper($arguments[$i]);
                if ($argument === 'STORE') {
                    return false;
                }
            }
        }

        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;
    }

    /**
     * Checks if a GEORADIUS command is a readable operation by parsing the
     * arguments array of the specified commad instance.
     *
     * @param CommandInterface $command Command instance.
     *
     * @return bool
     */
    protected function isGeoradiusReadOnly(CommandInterface $command)
    {
        $arguments = $command->getArguments();
        $argc = count($arguments);
        $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;

        if ($argc > $startIndex) {
            for ($i = $startIndex; $i < $argc; ++$i) {
                $argument = strtoupper($arguments[$i]);
                if ($argument === 'STORE' || $argument === 'STOREDIST') {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Marks a command as a read-only operation.
     *
     * When the behavior of a command can be decided only at runtime depending
     * on its arguments, a callable object can be provided to dynamically check
     * if the specified command performs a read or a write operation.
     *
     * @param string $commandID Command ID.
     * @param mixed  $readonly  A boolean value or a callable object.
     */
    public function setCommandReadOnly($commandID, $readonly = true)
    {
        $commandID = strtoupper($commandID);

        if ($readonly) {
            $this->readonly[$commandID] = $readonly;
        } else {
            unset($this->readonly[$commandID]);
        }
    }

    /**
     * Marks a Lua script for EVAL and EVALSHA as a read-only operation. When
     * the behaviour of a script can be decided only at runtime depending on
     * its arguments, a callable object can be provided to dynamically check
     * if the passed instance of EVAL or EVALSHA performs write operations or
     * not.
     *
     * @param string $script   Body of the Lua script.
     * @param mixed  $readonly A boolean value or a callable object.
     */
    public function setScriptReadOnly($script, $readonly = true)
    {
        $sha1 = sha1($script);

        if ($readonly) {
            $this->readonlySHA1[$sha1] = $readonly;
        } else {
            unset($this->readonlySHA1[$sha1]);
        }
    }

    /**
     * Returns the default list of disallowed commands.
     *
     * @return array
     */
    protected function getDisallowedOperations()
    {
        return array(
            'SHUTDOWN' => true,
            'INFO' => true,
            'DBSIZE' => true,
            'LASTSAVE' => true,
            'CONFIG' => true,
            'MONITOR' => true,
            'SLAVEOF' => true,
            'SAVE' => true,
            'BGSAVE' => true,
            'BGREWRITEAOF' => true,
            'SLOWLOG' => true,
        );
    }

    /**
     * Returns the default list of commands performing read-only operations.
     *
     * @return array
     */
    protected function getReadOnlyOperations()
    {
        return array(
            'EXISTS' => true,
            'TYPE' => true,
            'KEYS' => true,
            'SCAN' => true,
            'RANDOMKEY' => true,
            'TTL' => true,
            'GET' => true,
            'MGET' => true,
            'SUBSTR' => true,
            'STRLEN' => true,
            'GETRANGE' => true,
            'GETBIT' => true,
            'LLEN' => true,
            'LRANGE' => true,
            'LINDEX' => true,
            'SCARD' => true,
            'SISMEMBER' => true,
            'SINTER' => true,
            'SUNION' => true,
            'SDIFF' => true,
            'SMEMBERS' => true,
            'SSCAN' => true,
            'SRANDMEMBER' => true,
            'ZRANGE' => true,
            'ZREVRANGE' => true,
            'ZRANGEBYSCORE' => true,
            'ZREVRANGEBYSCORE' => true,
            'ZCARD' => true,
            'ZSCORE' => true,
            'ZCOUNT' => true,
            'ZRANK' => true,
            'ZREVRANK' => true,
            'ZSCAN' => true,
            'ZLEXCOUNT' => true,
            'ZRANGEBYLEX' => true,
            'ZREVRANGEBYLEX' => true,
            'HGET' => true,
            'HMGET' => true,
            'HEXISTS' => true,
            'HLEN' => true,
            'HKEYS' => true,
            'HVALS' => true,
            'HGETALL' => true,
            'HSCAN' => true,
            'HSTRLEN' => true,
            'PING' => true,
            'AUTH' => true,
            'SELECT' => true,
            'ECHO' => true,
            'QUIT' => true,
            'OBJECT' => true,
            'BITCOUNT' => true,
            'BITPOS' => true,
            'TIME' => true,
            'PFCOUNT' => true,
            'SORT' => array($this, 'isSortReadOnly'),
            'BITFIELD' => array($this, 'isBitfieldReadOnly'),
            'GEOHASH' => true,
            'GEOPOS' => true,
            'GEODIST' => true,
            'GEORADIUS' => array($this, 'isGeoradiusReadOnly'),
            'GEORADIUSBYMEMBER' => array($this, 'isGeoradiusReadOnly'),
        );
    }
}


code:
<?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\Replication;

use Predis\CommunicationException;

/**
 * Exception class that identifies a role mismatch when connecting to node
 * managed by redis-sentinel.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class RoleException extends CommunicationException
{
}


code:
<?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\Response;

/**
 * Represents an error returned by Redis (-ERR responses) during the execution
 * of a command on the server.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class Error implements ErrorInterface
{
    private $message;

    /**
     * @param string $message Error message returned by Redis
     */
    public function __construct($message)
    {
        $this->message = $message;
    }

    /**
     * {@inheritdoc}
     */
    public function getMessage()
    {
        return $this->message;
    }

    /**
     * {@inheritdoc}
     */
    public function getErrorType()
    {
        list($errorType) = explode(' ', $this->getMessage(), 2);

        return $errorType;
    }

    /**
     * Converts the object to its string representation.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->getMessage();
    }
}


code:
<?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\Response;

/**
 * Represents an error returned by Redis (responses identified by "-" in the
 * Redis protocol) during the execution of an operation on the server.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
interface ErrorInterface extends ResponseInterface
{
    /**
     * Returns the error message.
     *
     * @return string
     */
    public function getMessage();

    /**
     * Returns the error type (e.g. ERR, ASK, MOVED).
     *
     * @return string
     */
    public function getErrorType();
}


code:
<?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\Response\Iterator;

use Predis\Connection\NodeConnectionInterface;

/**
 * Streamable multibulk response.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class MultiBulk extends MultiBulkIterator
{
    private $connection;

    /**
     * @param NodeConnectionInterface $connection Connection to Redis.
     * @param int                     $size       Number of elements of the multibulk response.
     */
    public function __construct(NodeConnectionInterface $connection, $size)
    {
        $this->connection = $connection;
        $this->size = $size;
        $this->position = 0;
        $this->current = $size > 0 ? $this->getValue() : null;
    }

    /**
     * Handles the synchronization of the client with the Redis protocol when
     * the garbage collector kicks in (e.g. when the iterator goes out of the
     * scope of a foreach or it is unset).
     */
    public function __destruct()
    {
        $this->drop(true);
    }

    /**
     * Drop queued elements that have not been read from the connection either
     * by consuming the rest of the multibulk response or quickly by closing the
     * underlying connection.
     *
     * @param bool $disconnect Consume the iterator or drop the connection.
     */
    public function drop($disconnect = false)
    {
        if ($disconnect) {
            if ($this->valid()) {
                $this->position = $this->size;
                $this->connection->disconnect();
            }
        } else {
            while ($this->valid()) {
                $this->next();
            }
        }
    }

    /**
     * Reads the next item of the multibulk response from the connection.
     *
     * @return mixed
     */
    protected function getValue()
    {
        return $this->connection->read();
    }
}


code:
<?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\Response\Iterator;

use Predis\Response\ResponseInterface;

/**
 * Iterator that abstracts the access to multibulk responses allowing them to be
 * consumed in a streamable fashion without keeping the whole payload in memory.
 *
 * This iterator does not support rewinding which means that the iteration, once
 * consumed, cannot be restarted.
 *
 * Always make sure that the whole iteration is consumed (or dropped) to prevent
 * protocol desynchronization issues.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInterface
{
    protected $current;
    protected $position;
    protected $size;

    /**
     * {@inheritdoc}
     */
    public function rewind()
    {
        // NOOP
    }

    /**
     * {@inheritdoc}
     */
    public function current()
    {
        return $this->current;
    }

    /**
     * {@inheritdoc}
     */
    public function key()
    {
        return $this->position;
    }

    /**
     * {@inheritdoc}
     */
    public function next()
    {
        if (++$this->position < $this->size) {
            $this->current = $this->getValue();
        }
    }

    /**
     * {@inheritdoc}
     */
    public function valid()
    {
        return $this->position < $this->size;
    }

    /**
     * Returns the number of items comprising the whole multibulk response.
     *
     * This method should be used instead of iterator_count() to get the size of
     * the current multibulk response since the former consumes the iteration to
     * count the number of elements, but our iterators do not support rewinding.
     *
     * @return int
     */
    public function count()
    {
        return $this->size;
    }

    /**
     * Returns the current position of the iterator.
     *
     * @return int
     */
    public function getPosition()
    {
        return $this->position;
    }

    /**
     * {@inheritdoc}
     */
    abstract protected function getValue();
}


code:
<?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\Response\Iterator;

/**
 * Outer iterator consuming streamable multibulk responses by yielding tuples of
 * keys and values.
 *
 * This wrapper is useful for responses to commands such as `HGETALL` that can
 * be iterater as $key => $value pairs.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
class MultiBulkTuple extends MultiBulk implements \OuterIterator
{
    private $iterator;

    /**
     * @param MultiBulk $iterator Inner multibulk response iterator.
     */
    public function __construct(MultiBulk $iterator)
    {
        $this->checkPreconditions($iterator);

        $this->size = count($iterator) / 2;
        $this->iterator = $iterator;
        $this->position = $iterator->getPosition();
        $this->current = $this->size > 0 ? $this->getValue() : null;
    }

    /**
     * Checks for valid preconditions.
     *
     * @param MultiBulk $iterator Inner multibulk response iterator.
     *
     * @throws \InvalidArgumentException
     * @throws \UnexpectedValueException
     */
    protected function checkPreconditions(MultiBulk $iterator)
    {
        if ($iterator->getPosition() !== 0) {
            throw new \InvalidArgumentException(
                'Cannot initialize a tuple iterator using an already initiated iterator.'
            );
        }

        if (($size = count($iterator)) % 2 !== 0) {
            throw new \UnexpectedValueException('Invalid response size for a tuple iterator.');
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getInnerIterator()
    {
        return $this->iterator;
    }

    /**
     * {@inheritdoc}
     */
    public function __destruct()
    {
        $this->iterator->drop(true);
    }

    /**
     * {@inheritdoc}
     */
    protected function getValue()
    {
        $k = $this->iterator->current();
        $this->iterator->next();

        $v = $this->iterator->current();
        $this->iterator->next();

        return array($k, $v);
    }
}


code:
<?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\Response;

/**
 * Represents a complex response object from Redis.
 *
 * @author Daniele Alessandri <suppakilla@gmail.com>
 */
interface ResponseInterface
{
}