mirror of
https://github.com/predis/predis.git
synced 2026-08-20 20:11:53 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 239864d9a0 | |||
| b006d2306c | |||
| 34e394cc10 | |||
| 79539c3bcf | |||
| 74817b5596 | |||
| f967ecd66b | |||
| 261d0b824f | |||
| b77da8476c | |||
| 3fbd29d00b | |||
| 8373663486 | |||
| 18853d1dbb | |||
| 1da827c0ae | |||
| aa8fd5cea4 | |||
| 8d353cc223 | |||
| 0a580c9d1b | |||
| 58d1cc12dc | |||
| a8862f0fdd | |||
| e148dc831a | |||
| c4b592298c | |||
| 0e3b105959 | |||
| 749bbfe0e3 | |||
| c2ae1c612e | |||
| a85f89e2c7 | |||
| dbb802f0f1 | |||
| 64b79511bd | |||
| 5a9e747b01 | |||
| cb0fbd01b4 | |||
| 83df2ef11c | |||
| c94e5d3a10 | |||
| c90fbfc0ff | |||
| 21d6f0e7c6 | |||
| f4a950b275 | |||
| 5ec51c899f | |||
| e1d700dd0c | |||
| 809f76a7b3 | |||
| ab6cbe7e10 | |||
| 80bcb9f475 | |||
| c8e671a42a | |||
| 838e6af403 | |||
| ba2ffb612a | |||
| fdaf73987b | |||
| 50f8922df2 | |||
| 60733eb5c9 | |||
| a13dd4e180 | |||
| 1678e486b1 |
+11
-1
@@ -3,6 +3,8 @@ php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- hhvm
|
||||
branches:
|
||||
except:
|
||||
- v0.5
|
||||
@@ -10,4 +12,12 @@ branches:
|
||||
- php5.2_backport
|
||||
- documentation
|
||||
services: redis-server
|
||||
script: phpunit -c phpunit.xml.travisci
|
||||
before_script:
|
||||
- composer self-update
|
||||
- composer install --no-interaction --prefer-source --dev
|
||||
script:
|
||||
- vendor/bin/phpunit -c phpunit.xml.travisci
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
fast_finish: true
|
||||
|
||||
@@ -1,3 +1,38 @@
|
||||
v0.8.6 (2014-07-15)
|
||||
================================================================================
|
||||
|
||||
- Redis 2.8 is not the default server profile as there are no changes that would
|
||||
break compatibility with previous releases.
|
||||
|
||||
- Added `PFADD`, `PFCOUNT`, `PFMERGE` to the server profile for Redis 2.8 for
|
||||
handling the HyperLogLog data structure introduced in Redis 2.8.9.
|
||||
|
||||
- Added `ZLEXCOUNT`, `ZRANGEBYLEX`, `ZREMRANGEBYLEX` to the server profile for
|
||||
Redis 2.8 for handling lexicographic operations on members of sorted sets.
|
||||
|
||||
- Added support for key hash tags when using redis-cluster (Redis 3.0.0b1).
|
||||
|
||||
- __FIX__: minor tweaks to make Predis compatible with HHVM >= 2.4.0.
|
||||
|
||||
- __FIX__: responses to `INFO` are now properly parsed and will not break when
|
||||
redis sentinel is being used (ISSUE #154).
|
||||
|
||||
- __FIX__: added missing support for `INCRBYFLOAT` in cluster and replication
|
||||
configurations (ISSUE #159).
|
||||
|
||||
- __FIX__: fix parsing of the output of `CLUSTER NODES` to fetch the slots map
|
||||
from a node when redis-cluster has slaves in its configuration (ISSUE #165).
|
||||
|
||||
- __FIX__: prevent a stack overflow when iterating over large Redis collections
|
||||
using our abstraction for cursor-based iterators (ISSUE #182).
|
||||
|
||||
- __FIX__: properly discards transactions when the server immediately returns an
|
||||
error response (e.g. -OOM or -ERR on invalid arguments for a command) instead
|
||||
of a +QUEUED response (ISSUE #187).
|
||||
|
||||
- Upgraded to PHPUnit 4.* for the test suite.
|
||||
|
||||
|
||||
v0.8.5 (2014-01-16)
|
||||
================================================================================
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ By default Predis does not require any additional C extension, but it can be opt
|
||||
Redis protocol. An asynchronous implementation of the client, albeit experimental, is also available
|
||||
through [Predis\Async](https://github.com/nrk/predis-async).
|
||||
|
||||
Predis can be used with [HHVM](http://www.hhvm.com) >= 2.4.0, but there are no guarantees you will
|
||||
not run into unexpected issues (especially with the JIT compiler enabled via `Eval.Jit = true`) due
|
||||
to HHVM being still under heavy development, thus unstable and not yet 100% compatible with PHP.
|
||||
|
||||
More details about the project can be found in our [frequently asked questions](FAQ.md) section or
|
||||
on the online [wiki](https://github.com/nrk/predis/wiki).
|
||||
|
||||
@@ -294,11 +298,11 @@ Predis has a comprehensive test suite covering every aspect of the library. This
|
||||
integration tests against a running instance of Redis (>= 2.4.0 is required) to verify the correct
|
||||
behaviour of the implementation of each command and automatically skips commands not defined in the
|
||||
specified Redis profile. If you do not have Redis up and running, integration tests can be disabled.
|
||||
By default the test suite is configured to execute integration tests using the profile for Redis 2.6
|
||||
but can optionally target a Redis instance built from `2.8` or `unstable` branches by modifying the
|
||||
`phpunit.xml` file and setting `REDIS_SERVER_VERSION` respectively to `2.8` or `dev` so that the
|
||||
correct server profile will be used. You can refer to [the tests README](tests/README.md) for more
|
||||
detailed information about testing Predis.
|
||||
By default the test suite is configured to execute integration tests using the profile for Redis 2.8
|
||||
(which is the current stable version of Redis) but can optionally target a Redis instance built from
|
||||
the `unstable` branch by modifying `phpunit.xml` and setting `REDIS_SERVER_VERSION` to `dev` so that
|
||||
the development server profile will be used. You can refer to [the tests README](tests/README.md)
|
||||
for more detailed information about testing Predis.
|
||||
|
||||
Predis uses Travis CI for continuous integration and the history for past and current builds can be
|
||||
found [on its project page](http://travis-ci.org/nrk/predis).
|
||||
|
||||
+172
-4
@@ -12,7 +12,11 @@
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
// In order to be able to execute this script to create a PEAR package of Predis
|
||||
// both `onion` and `pear` must be available and executable in your $PATH.
|
||||
// the `pear` binary must be available and executable in your $PATH.
|
||||
// The parts used to parse author and version strings are taken from Onion (used
|
||||
// by this library in the past) just to keep on relying on the package.ini file
|
||||
// to simplify things. We might consider to switch to using the PEAR classes
|
||||
// directly in the future.
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
function executeWithBackup($file, $callback)
|
||||
@@ -36,18 +40,182 @@ function executeWithBackup($file, $callback)
|
||||
}
|
||||
}
|
||||
|
||||
function buildPackage()
|
||||
function parseAuthor($string)
|
||||
{
|
||||
passthru('onion build && pear -q package && rm package.xml');
|
||||
$author = array();
|
||||
|
||||
if (preg_match('/^\s*(.+?)\s*(?:"(\S+)"\s*)?<(\S+)>\s*$/x', $string , $regs)) {
|
||||
if (count($regs) == 4) {
|
||||
list($orig,$name,$user,$email) = $regs;
|
||||
$author['name'] = $name;
|
||||
$author['user'] = $user;
|
||||
$author['email'] = $email;
|
||||
} elseif (count($regs) == 3) {
|
||||
list($orig,$name,$email) = $regs;
|
||||
$author['name'] = $name;
|
||||
$author['email'] = $email;
|
||||
}
|
||||
} else {
|
||||
$author['name'] = $string;
|
||||
}
|
||||
|
||||
return $author;
|
||||
}
|
||||
|
||||
executeWithBackup(__DIR__.'/../phpunit.xml.dist', function ($file) {
|
||||
function parseVersion($string)
|
||||
{
|
||||
$version_pattern = '([0-9.]+)';
|
||||
|
||||
if (preg_match("/^\s*$version_pattern\s*\$/x", $string, $regs)) {
|
||||
return array('min' => $regs[1] ?: '0.0.0');
|
||||
} elseif (preg_match("/^\s*[>=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
|
||||
return array('min' => $regs[1] ?: '0.0.0');
|
||||
} elseif (preg_match("/^\s*[<=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
|
||||
return array('max' => $regs[1]);
|
||||
} elseif (preg_match("/^\s*$version_pattern\s*<=>\s*$version_pattern\s*\$/x", $string, $regs)) {
|
||||
return array(
|
||||
'min' => $regs[1] ?: '0.0.0',
|
||||
'max' => $regs[2],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function addRolePath($pkg, $path, $role)
|
||||
{
|
||||
if (is_dir($path)) {
|
||||
$dirRoot = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$dirTree = new RecursiveIteratorIterator($dirRoot, RecursiveIteratorIterator::CHILD_FIRST);
|
||||
|
||||
foreach ($dirTree as $fileinfo) {
|
||||
if ($fileinfo->isFile()) {
|
||||
addPackageFile($pkg, $fileinfo, $role, $path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach (glob($path) as $filename) {
|
||||
addPackageFile($pkg, new SplFileInfo($filename), $role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addPackageFile($pkg, $fileinfo, $role, $baseDir = '')
|
||||
{
|
||||
$fileNode = $pkg->contents->dir->addChild('file');
|
||||
$fileNode->addAttribute('name', $filepath = $fileinfo->getPathname());
|
||||
$fileNode->addAttribute('role', $role);
|
||||
$fileNode->addAttribute('md5sum', md5_file($filepath));
|
||||
|
||||
$installNode = $pkg->phprelease->filelist->addChild('install');
|
||||
$installNode->addAttribute('name', $filepath);
|
||||
$installNode->addAttribute('as', !$baseDir ? basename($filepath) : substr($filepath, strlen($baseDir) + 1));
|
||||
}
|
||||
|
||||
function generatePackageXml($packageINI)
|
||||
{
|
||||
$XML = <<<XML
|
||||
<?xml version="1.0"?>
|
||||
<package packagerversion="1.4.10" version="2.0"
|
||||
xmlns="http://pear.php.net/dtd/package-2.0"
|
||||
xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
|
||||
http://pear.php.net/dtd/tasks-1.0.xsd
|
||||
http://pear.php.net/dtd/package-2.0
|
||||
http://pear.php.net/dtd/package-2.0.xsd" />
|
||||
XML;
|
||||
|
||||
$cfg = parse_ini_file($packageINI, true);
|
||||
$pkg = new SimpleXMLElement($XML);
|
||||
|
||||
$pkg->name = $cfg['package']['name'];
|
||||
$pkg->channel = $cfg['package']['channel'];
|
||||
$pkg->summary = $cfg['package']['desc'];
|
||||
$pkg->description = $cfg['package']['desc'];
|
||||
|
||||
$author = parseAuthor($cfg['package']['author']);
|
||||
$pkg->addChild('lead');
|
||||
$pkg->lead->name = $author['name'];
|
||||
$pkg->lead->user = $author['user'];
|
||||
$pkg->lead->email = $author['email'];
|
||||
$pkg->lead->active = 'yes';
|
||||
|
||||
$datetime = new DateTime('now');
|
||||
$pkg->date = $datetime->format('Y-m-d');
|
||||
$pkg->time = $datetime->format('H:i:s');
|
||||
|
||||
$pkg->addChild('version');
|
||||
$pkg->version->release = $cfg['package']['version'];
|
||||
$pkg->version->api = $cfg['package']['version'];
|
||||
|
||||
$pkg->addChild('stability');
|
||||
$pkg->stability->release = $cfg['package']['stability'];
|
||||
$pkg->stability->api = $cfg['package']['stability'];
|
||||
|
||||
$pkg->license = $cfg['package']['license'];
|
||||
$pkg->notes = '-';
|
||||
|
||||
$pkg->addChild('contents')->addChild('dir')->addAttribute('name', '/');
|
||||
|
||||
$pkg->addChild('dependencies')->addChild('required');
|
||||
foreach ($cfg['require'] as $required => $version) {
|
||||
$version = parseVersion($version);
|
||||
$pkg->dependencies->required->addChild($required);
|
||||
|
||||
if (isset($version['min'])) {
|
||||
$pkg->dependencies->required->$required->min = $version['min'];
|
||||
}
|
||||
if (isset($version['max'])) {
|
||||
$pkg->dependencies->required->$required->min = $version['max'];
|
||||
}
|
||||
}
|
||||
|
||||
$pkg->addChild('phprelease')->addChild('filelist');
|
||||
|
||||
$pathToRole = array(
|
||||
'doc' => 'doc', 'docs' => 'doc', 'examples' => 'doc',
|
||||
'lib' => 'php', 'src' => 'php',
|
||||
'test' => 'test', 'tests' => 'test',
|
||||
);
|
||||
|
||||
foreach (array_merge($pathToRole, $cfg['roles'] ?: array()) as $path => $role) {
|
||||
addRolePath($pkg, $path, $role);
|
||||
}
|
||||
|
||||
return $pkg;
|
||||
}
|
||||
|
||||
function savePackageXml($xml)
|
||||
{
|
||||
$dom = new DOMDocument("1.0");
|
||||
$dom->preserveWhiteSpace = false;
|
||||
$dom->formatOutput = true;
|
||||
$dom->loadXML($xml->asXML());
|
||||
|
||||
file_put_contents('package.xml', $dom->saveXML());
|
||||
}
|
||||
|
||||
function buildPackage()
|
||||
{
|
||||
passthru('pear -q package && rm package.xml');
|
||||
}
|
||||
|
||||
function modifyPhpunitXml($file)
|
||||
{
|
||||
$cfg = new SimpleXMLElement($file, null, true);
|
||||
|
||||
$cfg[0]['bootstrap'] = str_replace('tests/', '', $cfg[0]['bootstrap']);
|
||||
$cfg->testsuites->testsuite->directory = str_replace('tests/', '', $cfg->testsuites->testsuite->directory);
|
||||
|
||||
$cfg->saveXml($file);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
executeWithBackup(__DIR__.'/../phpunit.xml.dist', function ($file) {
|
||||
modifyPhpunitXml($file);
|
||||
|
||||
$pkg = generatePackageXml('package.ini');
|
||||
savePackageXml($pkg);
|
||||
|
||||
buildPackage();
|
||||
});
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
"require": {
|
||||
"php": ">=5.3.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
|
||||
"ext-curl": "Allows access to Webdis when paired with phpiredis"
|
||||
|
||||
@@ -30,7 +30,7 @@ use Predis\Transaction\MultiExecContext;
|
||||
*/
|
||||
class Client implements ClientInterface
|
||||
{
|
||||
const VERSION = '0.8.5';
|
||||
const VERSION = '0.8.6';
|
||||
|
||||
private $options;
|
||||
private $profile;
|
||||
|
||||
@@ -42,117 +42,126 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
|
||||
*/
|
||||
protected function getDefaultCommands()
|
||||
{
|
||||
$keyIsFirstArgument = array($this, 'getKeyFromFirstArgument');
|
||||
$keysAreAllArguments = array($this, 'getKeyFromAllArguments');
|
||||
$getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument');
|
||||
$getKeyFromAllArguments = array($this, 'getKeyFromAllArguments');
|
||||
|
||||
return array(
|
||||
/* commands operating on the key space */
|
||||
'EXISTS' => $keyIsFirstArgument,
|
||||
'DEL' => $keysAreAllArguments,
|
||||
'TYPE' => $keyIsFirstArgument,
|
||||
'EXPIRE' => $keyIsFirstArgument,
|
||||
'EXPIREAT' => $keyIsFirstArgument,
|
||||
'PERSIST' => $keyIsFirstArgument,
|
||||
'PEXPIRE' => $keyIsFirstArgument,
|
||||
'PEXPIREAT' => $keyIsFirstArgument,
|
||||
'TTL' => $keyIsFirstArgument,
|
||||
'PTTL' => $keyIsFirstArgument,
|
||||
'SORT' => $keyIsFirstArgument, // TODO
|
||||
'DUMP' => $keyIsFirstArgument,
|
||||
'RESTORE' => $keyIsFirstArgument,
|
||||
'EXISTS' => $getKeyFromFirstArgument,
|
||||
'DEL' => $getKeyFromAllArguments,
|
||||
'TYPE' => $getKeyFromFirstArgument,
|
||||
'EXPIRE' => $getKeyFromFirstArgument,
|
||||
'EXPIREAT' => $getKeyFromFirstArgument,
|
||||
'PERSIST' => $getKeyFromFirstArgument,
|
||||
'PEXPIRE' => $getKeyFromFirstArgument,
|
||||
'PEXPIREAT' => $getKeyFromFirstArgument,
|
||||
'TTL' => $getKeyFromFirstArgument,
|
||||
'PTTL' => $getKeyFromFirstArgument,
|
||||
'SORT' => $getKeyFromFirstArgument, // TODO
|
||||
'DUMP' => $getKeyFromFirstArgument,
|
||||
'RESTORE' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on string values */
|
||||
'APPEND' => $keyIsFirstArgument,
|
||||
'DECR' => $keyIsFirstArgument,
|
||||
'DECRBY' => $keyIsFirstArgument,
|
||||
'GET' => $keyIsFirstArgument,
|
||||
'GETBIT' => $keyIsFirstArgument,
|
||||
'MGET' => $keysAreAllArguments,
|
||||
'SET' => $keyIsFirstArgument,
|
||||
'GETRANGE' => $keyIsFirstArgument,
|
||||
'GETSET' => $keyIsFirstArgument,
|
||||
'INCR' => $keyIsFirstArgument,
|
||||
'INCRBY' => $keyIsFirstArgument,
|
||||
'SETBIT' => $keyIsFirstArgument,
|
||||
'SETEX' => $keyIsFirstArgument,
|
||||
'APPEND' => $getKeyFromFirstArgument,
|
||||
'DECR' => $getKeyFromFirstArgument,
|
||||
'DECRBY' => $getKeyFromFirstArgument,
|
||||
'GET' => $getKeyFromFirstArgument,
|
||||
'GETBIT' => $getKeyFromFirstArgument,
|
||||
'MGET' => $getKeyFromAllArguments,
|
||||
'SET' => $getKeyFromFirstArgument,
|
||||
'GETRANGE' => $getKeyFromFirstArgument,
|
||||
'GETSET' => $getKeyFromFirstArgument,
|
||||
'INCR' => $getKeyFromFirstArgument,
|
||||
'INCRBY' => $getKeyFromFirstArgument,
|
||||
'INCRBYFLOAT' => $getKeyFromFirstArgument,
|
||||
'SETBIT' => $getKeyFromFirstArgument,
|
||||
'SETEX' => $getKeyFromFirstArgument,
|
||||
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
|
||||
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
|
||||
'SETNX' => $keyIsFirstArgument,
|
||||
'SETRANGE' => $keyIsFirstArgument,
|
||||
'STRLEN' => $keyIsFirstArgument,
|
||||
'SUBSTR' => $keyIsFirstArgument,
|
||||
'SETNX' => $getKeyFromFirstArgument,
|
||||
'SETRANGE' => $getKeyFromFirstArgument,
|
||||
'STRLEN' => $getKeyFromFirstArgument,
|
||||
'SUBSTR' => $getKeyFromFirstArgument,
|
||||
'BITOP' => array($this, 'getKeyFromBitOp'),
|
||||
'BITCOUNT' => $keyIsFirstArgument,
|
||||
'BITCOUNT' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on lists */
|
||||
'LINSERT' => $keyIsFirstArgument,
|
||||
'LINDEX' => $keyIsFirstArgument,
|
||||
'LLEN' => $keyIsFirstArgument,
|
||||
'LPOP' => $keyIsFirstArgument,
|
||||
'RPOP' => $keyIsFirstArgument,
|
||||
'RPOPLPUSH' => $keysAreAllArguments,
|
||||
'LINSERT' => $getKeyFromFirstArgument,
|
||||
'LINDEX' => $getKeyFromFirstArgument,
|
||||
'LLEN' => $getKeyFromFirstArgument,
|
||||
'LPOP' => $getKeyFromFirstArgument,
|
||||
'RPOP' => $getKeyFromFirstArgument,
|
||||
'RPOPLPUSH' => $getKeyFromAllArguments,
|
||||
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
|
||||
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
|
||||
'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'),
|
||||
'LPUSH' => $keyIsFirstArgument,
|
||||
'LPUSHX' => $keyIsFirstArgument,
|
||||
'RPUSH' => $keyIsFirstArgument,
|
||||
'RPUSHX' => $keyIsFirstArgument,
|
||||
'LRANGE' => $keyIsFirstArgument,
|
||||
'LREM' => $keyIsFirstArgument,
|
||||
'LSET' => $keyIsFirstArgument,
|
||||
'LTRIM' => $keyIsFirstArgument,
|
||||
'LPUSH' => $getKeyFromFirstArgument,
|
||||
'LPUSHX' => $getKeyFromFirstArgument,
|
||||
'RPUSH' => $getKeyFromFirstArgument,
|
||||
'RPUSHX' => $getKeyFromFirstArgument,
|
||||
'LRANGE' => $getKeyFromFirstArgument,
|
||||
'LREM' => $getKeyFromFirstArgument,
|
||||
'LSET' => $getKeyFromFirstArgument,
|
||||
'LTRIM' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on sets */
|
||||
'SADD' => $keyIsFirstArgument,
|
||||
'SCARD' => $keyIsFirstArgument,
|
||||
'SDIFF' => $keysAreAllArguments,
|
||||
'SDIFFSTORE' => $keysAreAllArguments,
|
||||
'SINTER' => $keysAreAllArguments,
|
||||
'SINTERSTORE' => $keysAreAllArguments,
|
||||
'SUNION' => $keysAreAllArguments,
|
||||
'SUNIONSTORE' => $keysAreAllArguments,
|
||||
'SISMEMBER' => $keyIsFirstArgument,
|
||||
'SMEMBERS' => $keyIsFirstArgument,
|
||||
'SSCAN' => $keyIsFirstArgument,
|
||||
'SPOP' => $keyIsFirstArgument,
|
||||
'SRANDMEMBER' => $keyIsFirstArgument,
|
||||
'SREM' => $keyIsFirstArgument,
|
||||
'SADD' => $getKeyFromFirstArgument,
|
||||
'SCARD' => $getKeyFromFirstArgument,
|
||||
'SDIFF' => $getKeyFromAllArguments,
|
||||
'SDIFFSTORE' => $getKeyFromAllArguments,
|
||||
'SINTER' => $getKeyFromAllArguments,
|
||||
'SINTERSTORE' => $getKeyFromAllArguments,
|
||||
'SUNION' => $getKeyFromAllArguments,
|
||||
'SUNIONSTORE' => $getKeyFromAllArguments,
|
||||
'SISMEMBER' => $getKeyFromFirstArgument,
|
||||
'SMEMBERS' => $getKeyFromFirstArgument,
|
||||
'SSCAN' => $getKeyFromFirstArgument,
|
||||
'SPOP' => $getKeyFromFirstArgument,
|
||||
'SRANDMEMBER' => $getKeyFromFirstArgument,
|
||||
'SREM' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on sorted sets */
|
||||
'ZADD' => $keyIsFirstArgument,
|
||||
'ZCARD' => $keyIsFirstArgument,
|
||||
'ZCOUNT' => $keyIsFirstArgument,
|
||||
'ZINCRBY' => $keyIsFirstArgument,
|
||||
'ZADD' => $getKeyFromFirstArgument,
|
||||
'ZCARD' => $getKeyFromFirstArgument,
|
||||
'ZCOUNT' => $getKeyFromFirstArgument,
|
||||
'ZINCRBY' => $getKeyFromFirstArgument,
|
||||
'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
|
||||
'ZRANGE' => $keyIsFirstArgument,
|
||||
'ZRANGEBYSCORE' => $keyIsFirstArgument,
|
||||
'ZRANK' => $keyIsFirstArgument,
|
||||
'ZREM' => $keyIsFirstArgument,
|
||||
'ZREMRANGEBYRANK' => $keyIsFirstArgument,
|
||||
'ZREMRANGEBYSCORE' => $keyIsFirstArgument,
|
||||
'ZREVRANGE' => $keyIsFirstArgument,
|
||||
'ZREVRANGEBYSCORE' => $keyIsFirstArgument,
|
||||
'ZREVRANK' => $keyIsFirstArgument,
|
||||
'ZSCORE' => $keyIsFirstArgument,
|
||||
'ZRANGE' => $getKeyFromFirstArgument,
|
||||
'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZRANK' => $getKeyFromFirstArgument,
|
||||
'ZREM' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANK' => $getKeyFromFirstArgument,
|
||||
'ZSCORE' => $getKeyFromFirstArgument,
|
||||
'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
|
||||
'ZSCAN' => $keyIsFirstArgument,
|
||||
'ZSCAN' => $getKeyFromFirstArgument,
|
||||
'ZLEXCOUNT' => $getKeyFromFirstArgument,
|
||||
'ZRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on hashes */
|
||||
'HDEL' => $keyIsFirstArgument,
|
||||
'HEXISTS' => $keyIsFirstArgument,
|
||||
'HGET' => $keyIsFirstArgument,
|
||||
'HGETALL' => $keyIsFirstArgument,
|
||||
'HMGET' => $keyIsFirstArgument,
|
||||
'HMSET' => $keyIsFirstArgument,
|
||||
'HINCRBY' => $keyIsFirstArgument,
|
||||
'HINCRBYFLOAT' => $keyIsFirstArgument,
|
||||
'HKEYS' => $keyIsFirstArgument,
|
||||
'HLEN' => $keyIsFirstArgument,
|
||||
'HSET' => $keyIsFirstArgument,
|
||||
'HSETNX' => $keyIsFirstArgument,
|
||||
'HVALS' => $keyIsFirstArgument,
|
||||
'HSCAN' => $keyIsFirstArgument,
|
||||
'HDEL' => $getKeyFromFirstArgument,
|
||||
'HEXISTS' => $getKeyFromFirstArgument,
|
||||
'HGET' => $getKeyFromFirstArgument,
|
||||
'HGETALL' => $getKeyFromFirstArgument,
|
||||
'HMGET' => $getKeyFromFirstArgument,
|
||||
'HMSET' => $getKeyFromFirstArgument,
|
||||
'HINCRBY' => $getKeyFromFirstArgument,
|
||||
'HINCRBYFLOAT' => $getKeyFromFirstArgument,
|
||||
'HKEYS' => $getKeyFromFirstArgument,
|
||||
'HLEN' => $getKeyFromFirstArgument,
|
||||
'HSET' => $getKeyFromFirstArgument,
|
||||
'HSETNX' => $getKeyFromFirstArgument,
|
||||
'HVALS' => $getKeyFromFirstArgument,
|
||||
'HSCAN' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on HyperLogLog */
|
||||
'PFADD' => $getKeyFromFirstArgument,
|
||||
'PFCOUNT' => $getKeyFromAllArguments,
|
||||
'PFMERGE' => $getKeyFromAllArguments,
|
||||
|
||||
/* scripting */
|
||||
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
|
||||
|
||||
@@ -42,103 +42,113 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
|
||||
*/
|
||||
protected function getDefaultCommands()
|
||||
{
|
||||
$keyIsFirstArgument = array($this, 'getKeyFromFirstArgument');
|
||||
$getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument');
|
||||
$getKeyFromAllArguments = array($this, 'getKeyFromAllArguments');
|
||||
|
||||
return array(
|
||||
/* commands operating on the key space */
|
||||
'EXISTS' => $keyIsFirstArgument,
|
||||
'DEL' => array($this, 'getKeyFromAllArguments'),
|
||||
'TYPE' => $keyIsFirstArgument,
|
||||
'EXPIRE' => $keyIsFirstArgument,
|
||||
'EXPIREAT' => $keyIsFirstArgument,
|
||||
'PERSIST' => $keyIsFirstArgument,
|
||||
'PEXPIRE' => $keyIsFirstArgument,
|
||||
'PEXPIREAT' => $keyIsFirstArgument,
|
||||
'TTL' => $keyIsFirstArgument,
|
||||
'PTTL' => $keyIsFirstArgument,
|
||||
'SORT' => $keyIsFirstArgument, // TODO
|
||||
'EXISTS' => $getKeyFromFirstArgument,
|
||||
'DEL' => $getKeyFromAllArguments,
|
||||
'TYPE' => $getKeyFromFirstArgument,
|
||||
'EXPIRE' => $getKeyFromFirstArgument,
|
||||
'EXPIREAT' => $getKeyFromFirstArgument,
|
||||
'PERSIST' => $getKeyFromFirstArgument,
|
||||
'PEXPIRE' => $getKeyFromFirstArgument,
|
||||
'PEXPIREAT' => $getKeyFromFirstArgument,
|
||||
'TTL' => $getKeyFromFirstArgument,
|
||||
'PTTL' => $getKeyFromFirstArgument,
|
||||
'SORT' => $getKeyFromFirstArgument, // TODO
|
||||
|
||||
/* commands operating on string values */
|
||||
'APPEND' => $keyIsFirstArgument,
|
||||
'DECR' => $keyIsFirstArgument,
|
||||
'DECRBY' => $keyIsFirstArgument,
|
||||
'GET' => $keyIsFirstArgument,
|
||||
'GETBIT' => $keyIsFirstArgument,
|
||||
'MGET' => array($this, 'getKeyFromAllArguments'),
|
||||
'SET' => $keyIsFirstArgument,
|
||||
'GETRANGE' => $keyIsFirstArgument,
|
||||
'GETSET' => $keyIsFirstArgument,
|
||||
'INCR' => $keyIsFirstArgument,
|
||||
'INCRBY' => $keyIsFirstArgument,
|
||||
'SETBIT' => $keyIsFirstArgument,
|
||||
'SETEX' => $keyIsFirstArgument,
|
||||
'APPEND' => $getKeyFromFirstArgument,
|
||||
'DECR' => $getKeyFromFirstArgument,
|
||||
'DECRBY' => $getKeyFromFirstArgument,
|
||||
'GET' => $getKeyFromFirstArgument,
|
||||
'GETBIT' => $getKeyFromFirstArgument,
|
||||
'MGET' => $getKeyFromAllArguments,
|
||||
'SET' => $getKeyFromFirstArgument,
|
||||
'GETRANGE' => $getKeyFromFirstArgument,
|
||||
'GETSET' => $getKeyFromFirstArgument,
|
||||
'INCR' => $getKeyFromFirstArgument,
|
||||
'INCRBY' => $getKeyFromFirstArgument,
|
||||
'INCRBYFLOAT' => $getKeyFromFirstArgument,
|
||||
'SETBIT' => $getKeyFromFirstArgument,
|
||||
'SETEX' => $getKeyFromFirstArgument,
|
||||
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
|
||||
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
|
||||
'SETNX' => $keyIsFirstArgument,
|
||||
'SETRANGE' => $keyIsFirstArgument,
|
||||
'STRLEN' => $keyIsFirstArgument,
|
||||
'SUBSTR' => $keyIsFirstArgument,
|
||||
'BITCOUNT' => $keyIsFirstArgument,
|
||||
'SETNX' => $getKeyFromFirstArgument,
|
||||
'SETRANGE' => $getKeyFromFirstArgument,
|
||||
'STRLEN' => $getKeyFromFirstArgument,
|
||||
'SUBSTR' => $getKeyFromFirstArgument,
|
||||
'BITCOUNT' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on lists */
|
||||
'LINSERT' => $keyIsFirstArgument,
|
||||
'LINDEX' => $keyIsFirstArgument,
|
||||
'LLEN' => $keyIsFirstArgument,
|
||||
'LPOP' => $keyIsFirstArgument,
|
||||
'RPOP' => $keyIsFirstArgument,
|
||||
'LINSERT' => $getKeyFromFirstArgument,
|
||||
'LINDEX' => $getKeyFromFirstArgument,
|
||||
'LLEN' => $getKeyFromFirstArgument,
|
||||
'LPOP' => $getKeyFromFirstArgument,
|
||||
'RPOP' => $getKeyFromFirstArgument,
|
||||
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
|
||||
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
|
||||
'LPUSH' => $keyIsFirstArgument,
|
||||
'LPUSHX' => $keyIsFirstArgument,
|
||||
'RPUSH' => $keyIsFirstArgument,
|
||||
'RPUSHX' => $keyIsFirstArgument,
|
||||
'LRANGE' => $keyIsFirstArgument,
|
||||
'LREM' => $keyIsFirstArgument,
|
||||
'LSET' => $keyIsFirstArgument,
|
||||
'LTRIM' => $keyIsFirstArgument,
|
||||
'LPUSH' => $getKeyFromFirstArgument,
|
||||
'LPUSHX' => $getKeyFromFirstArgument,
|
||||
'RPUSH' => $getKeyFromFirstArgument,
|
||||
'RPUSHX' => $getKeyFromFirstArgument,
|
||||
'LRANGE' => $getKeyFromFirstArgument,
|
||||
'LREM' => $getKeyFromFirstArgument,
|
||||
'LSET' => $getKeyFromFirstArgument,
|
||||
'LTRIM' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on sets */
|
||||
'SADD' => $keyIsFirstArgument,
|
||||
'SCARD' => $keyIsFirstArgument,
|
||||
'SISMEMBER' => $keyIsFirstArgument,
|
||||
'SMEMBERS' => $keyIsFirstArgument,
|
||||
'SSCAN' => $keyIsFirstArgument,
|
||||
'SPOP' => $keyIsFirstArgument,
|
||||
'SRANDMEMBER' => $keyIsFirstArgument,
|
||||
'SREM' => $keyIsFirstArgument,
|
||||
'SADD' => $getKeyFromFirstArgument,
|
||||
'SCARD' => $getKeyFromFirstArgument,
|
||||
'SISMEMBER' => $getKeyFromFirstArgument,
|
||||
'SMEMBERS' => $getKeyFromFirstArgument,
|
||||
'SSCAN' => $getKeyFromFirstArgument,
|
||||
'SPOP' => $getKeyFromFirstArgument,
|
||||
'SRANDMEMBER' => $getKeyFromFirstArgument,
|
||||
'SREM' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on sorted sets */
|
||||
'ZADD' => $keyIsFirstArgument,
|
||||
'ZCARD' => $keyIsFirstArgument,
|
||||
'ZCOUNT' => $keyIsFirstArgument,
|
||||
'ZINCRBY' => $keyIsFirstArgument,
|
||||
'ZRANGE' => $keyIsFirstArgument,
|
||||
'ZRANGEBYSCORE' => $keyIsFirstArgument,
|
||||
'ZRANK' => $keyIsFirstArgument,
|
||||
'ZREM' => $keyIsFirstArgument,
|
||||
'ZREMRANGEBYRANK' => $keyIsFirstArgument,
|
||||
'ZREMRANGEBYSCORE' => $keyIsFirstArgument,
|
||||
'ZREVRANGE' => $keyIsFirstArgument,
|
||||
'ZREVRANGEBYSCORE' => $keyIsFirstArgument,
|
||||
'ZREVRANK' => $keyIsFirstArgument,
|
||||
'ZSCORE' => $keyIsFirstArgument,
|
||||
'ZSCAN' => $keyIsFirstArgument,
|
||||
'ZADD' => $getKeyFromFirstArgument,
|
||||
'ZCARD' => $getKeyFromFirstArgument,
|
||||
'ZCOUNT' => $getKeyFromFirstArgument,
|
||||
'ZINCRBY' => $getKeyFromFirstArgument,
|
||||
'ZRANGE' => $getKeyFromFirstArgument,
|
||||
'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZRANK' => $getKeyFromFirstArgument,
|
||||
'ZREM' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANK' => $getKeyFromFirstArgument,
|
||||
'ZSCORE' => $getKeyFromFirstArgument,
|
||||
'ZSCAN' => $getKeyFromFirstArgument,
|
||||
'ZLEXCOUNT' => $getKeyFromFirstArgument,
|
||||
'ZRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on hashes */
|
||||
'HDEL' => $keyIsFirstArgument,
|
||||
'HEXISTS' => $keyIsFirstArgument,
|
||||
'HGET' => $keyIsFirstArgument,
|
||||
'HGETALL' => $keyIsFirstArgument,
|
||||
'HMGET' => $keyIsFirstArgument,
|
||||
'HMSET' => $keyIsFirstArgument,
|
||||
'HINCRBY' => $keyIsFirstArgument,
|
||||
'HINCRBYFLOAT' => $keyIsFirstArgument,
|
||||
'HKEYS' => $keyIsFirstArgument,
|
||||
'HLEN' => $keyIsFirstArgument,
|
||||
'HSET' => $keyIsFirstArgument,
|
||||
'HSETNX' => $keyIsFirstArgument,
|
||||
'HVALS' => $keyIsFirstArgument,
|
||||
'HSCAN' => $keyIsFirstArgument,
|
||||
'HDEL' => $getKeyFromFirstArgument,
|
||||
'HEXISTS' => $getKeyFromFirstArgument,
|
||||
'HGET' => $getKeyFromFirstArgument,
|
||||
'HGETALL' => $getKeyFromFirstArgument,
|
||||
'HMGET' => $getKeyFromFirstArgument,
|
||||
'HMSET' => $getKeyFromFirstArgument,
|
||||
'HINCRBY' => $getKeyFromFirstArgument,
|
||||
'HINCRBYFLOAT' => $getKeyFromFirstArgument,
|
||||
'HKEYS' => $getKeyFromFirstArgument,
|
||||
'HLEN' => $getKeyFromFirstArgument,
|
||||
'HSET' => $getKeyFromFirstArgument,
|
||||
'HSETNX' => $getKeyFromFirstArgument,
|
||||
'HVALS' => $getKeyFromFirstArgument,
|
||||
'HSCAN' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on HyperLogLog */
|
||||
'PFADD' => $getKeyFromFirstArgument,
|
||||
'PFCOUNT' => $getKeyFromAllArguments,
|
||||
'PFMERGE' => $getKeyFromAllArguments,
|
||||
|
||||
/* scripting */
|
||||
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
|
||||
@@ -287,6 +297,27 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
|
||||
*/
|
||||
public function getKeyHash($key)
|
||||
{
|
||||
return $this->hashGenerator->hash($key);
|
||||
$key = $this->extractKeyTag($key);
|
||||
$hash = $this->hashGenerator->hash($key);
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the hashable part of a key (delimited by "{...}"), or the
|
||||
* whole key if a key tag is not found in the string.
|
||||
*
|
||||
* @param string $key A key.
|
||||
* @return string
|
||||
*/
|
||||
protected function extractKeyTag($key)
|
||||
{
|
||||
if (false !== $start = strpos($key, '{')) {
|
||||
if (false !== ($end = strpos($key, '}', $start)) && $end !== ++$start) {
|
||||
$key = substr($key, $start, $end - $start);
|
||||
}
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,16 +166,18 @@ abstract class CursorBasedIterator implements Iterator
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
if (!$this->elements && $this->fetchmore) {
|
||||
$this->fetch();
|
||||
}
|
||||
tryFetch: {
|
||||
if (!$this->elements && $this->fetchmore) {
|
||||
$this->fetch();
|
||||
}
|
||||
|
||||
if ($this->elements) {
|
||||
$this->extractNext();
|
||||
} elseif ($this->cursor) {
|
||||
$this->next();
|
||||
} else {
|
||||
$this->valid = false;
|
||||
if ($this->elements) {
|
||||
$this->extractNext();
|
||||
} elseif ($this->cursor) {
|
||||
goto tryFetch;
|
||||
} else {
|
||||
$this->valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pfadd
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HyperLogLogAdd extends PrefixableCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'PFADD';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
return self::normalizeVariadic($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pfcount
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HyperLogLogCount extends PrefixableCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'PFCOUNT';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
return self::normalizeArguments($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prefixKeys($prefix)
|
||||
{
|
||||
PrefixHelpers::all($this, $prefix);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pfmerge
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HyperLogLogMerge extends PrefixableCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'PFMERGE';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
return self::normalizeArguments($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prefixKeys($prefix)
|
||||
{
|
||||
PrefixHelpers::all($this, $prefix);
|
||||
}
|
||||
}
|
||||
@@ -34,27 +34,37 @@ class ServerInfo extends AbstractCommand
|
||||
$infoLines = preg_split('/\r?\n/', $data);
|
||||
|
||||
foreach ($infoLines as $row) {
|
||||
@list($k, $v) = explode(':', $row);
|
||||
|
||||
if ($row === '' || !isset($v)) {
|
||||
if (strpos($row, ':') === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!preg_match('/^db\d+$/', $k)) {
|
||||
if ($k === 'allocation_stats') {
|
||||
$info[$k] = $this->parseAllocationStats($v);
|
||||
continue;
|
||||
}
|
||||
|
||||
$info[$k] = $v;
|
||||
} else {
|
||||
$info[$k] = $this->parseDatabaseStats($v);
|
||||
}
|
||||
list($k, $v) = $this->parseRow($row);
|
||||
$info[$k] = $v;
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses single row of the reply buffer and returns the key-value pair.
|
||||
*
|
||||
* @param string $row Single row of the reply buffer.
|
||||
* @return array
|
||||
*/
|
||||
public function parseRow($row)
|
||||
{
|
||||
list($k, $v) = explode(':', $row, 2);
|
||||
|
||||
if (!preg_match('/^db\d+$/', $k)) {
|
||||
if ($k === 'allocation_stats') {
|
||||
$v = $this->parseAllocationStats($v);
|
||||
}
|
||||
} else {
|
||||
$v = $this->parseDatabaseStats($v);
|
||||
}
|
||||
return array($k, $v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the reply buffer and extracts the statistics of each logical DB.
|
||||
*
|
||||
|
||||
@@ -41,18 +41,8 @@ class ServerInfoV26x extends ServerInfo
|
||||
continue;
|
||||
}
|
||||
|
||||
list($k, $v) = explode(':', $row);
|
||||
|
||||
if (!preg_match('/^db\d+$/', $k)) {
|
||||
if ($k === 'allocation_stats') {
|
||||
$current[$k] = $this->parseAllocationStats($v);
|
||||
continue;
|
||||
}
|
||||
|
||||
$current[$k] = $v;
|
||||
} else {
|
||||
$current[$k] = $this->parseDatabaseStats($v);
|
||||
}
|
||||
list($k, $v) = $this->parseRow($row);
|
||||
$current[$k] = $v;
|
||||
}
|
||||
|
||||
return $info;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/zlexcount
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ZSetLexCount extends PrefixableCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'ZLEXCOUNT';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/zrangebylex
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ZSetRangeByLex extends ZSetRange
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'ZRANGEBYLEX';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareOptions($options)
|
||||
{
|
||||
$opts = array_change_key_case($options, CASE_UPPER);
|
||||
$finalizedOpts = array();
|
||||
|
||||
if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) {
|
||||
$limit = array_change_key_case($opts['LIMIT'], CASE_UPPER);
|
||||
|
||||
$finalizedOpts[] = 'LIMIT';
|
||||
$finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0];
|
||||
$finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1];
|
||||
}
|
||||
|
||||
return $finalizedOpts;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function withScores()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/zremrangebylex
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ZSetRemoveRangeByLex extends PrefixableCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'ZREMRANGEBYLEX';
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ class PhpiredisStreamConnection extends StreamConnection
|
||||
$reader = $this->reader;
|
||||
|
||||
while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) {
|
||||
$buffer = fread($socket, 4096);
|
||||
$buffer = stream_socket_recvfrom($socket, 4096);
|
||||
|
||||
if ($buffer === false || $buffer === '') {
|
||||
$this->onConnectionError('Error while reading bytes from the server');
|
||||
|
||||
@@ -186,6 +186,11 @@ class RedisCluster implements ClusterConnectionInterface, IteratorAggregate, Cou
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$node = explode(' ', $nodes[$i], 9);
|
||||
|
||||
if (false === strpos($node[2], 'master')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$slots = explode('-', $node[8], 2);
|
||||
|
||||
if ($node[1] === ':0') {
|
||||
|
||||
@@ -104,7 +104,7 @@ class StreamConnection extends AbstractConnection
|
||||
stream_set_timeout($resource, $timeoutSeconds, $timeoutUSeconds);
|
||||
}
|
||||
|
||||
if (isset($parameters->tcp_nodelay) && version_compare(PHP_VERSION, '5.4.0') >= 0) {
|
||||
if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) {
|
||||
$socket = socket_import_stream($resource);
|
||||
socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay);
|
||||
}
|
||||
@@ -183,7 +183,7 @@ class StreamConnection extends AbstractConnection
|
||||
$socket = $this->getResource();
|
||||
|
||||
while (($length = strlen($buffer)) > 0) {
|
||||
$written = fwrite($socket, $buffer);
|
||||
$written = @fwrite($socket, $buffer);
|
||||
|
||||
if ($length === $written) {
|
||||
return;
|
||||
|
||||
@@ -50,7 +50,7 @@ class ClientConnectionFactory extends AbstractOption
|
||||
return $factory;
|
||||
}
|
||||
|
||||
if (@class_exists($value)) {
|
||||
if (is_string($value) && @class_exists($value)) {
|
||||
$factory = new $value();
|
||||
|
||||
if (!$factory instanceof ConnectionFactoryInterface) {
|
||||
|
||||
@@ -78,7 +78,7 @@ abstract class ServerProfile implements ServerProfileInterface, CommandProcessin
|
||||
'2.4' => 'Predis\Profile\ServerVersion24',
|
||||
'2.6' => 'Predis\Profile\ServerVersion26',
|
||||
'2.8' => 'Predis\Profile\ServerVersion28',
|
||||
'default' => 'Predis\Profile\ServerVersion26',
|
||||
'default' => 'Predis\Profile\ServerVersion28',
|
||||
'dev' => 'Predis\Profile\ServerVersionNext',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -239,9 +239,17 @@ class ServerVersion28 extends ServerProfile
|
||||
|
||||
/* commands operating on sorted sets */
|
||||
'zscan' => 'Predis\Command\ZSetScan',
|
||||
'zlexcount' => 'Predis\Command\ZSetLexCount',
|
||||
'zrangebylex' => 'Predis\Command\ZSetRangeByLex',
|
||||
'zremrangebylex' => 'Predis\Command\ZSetRemoveRangeByLex',
|
||||
|
||||
/* commands operating on hashes */
|
||||
'hscan' => 'Predis\Command\HashScan',
|
||||
|
||||
/* commands operating on HyperLogLog */
|
||||
'pfadd' => 'Predis\Command\HyperLogLogAdd',
|
||||
'pfcount' => 'Predis\Command\HyperLogLogCount',
|
||||
'pfmerge' => 'Predis\Command\HyperLogLogMerge',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +201,8 @@ class ReplicationStrategy
|
||||
'ZRANK' => true,
|
||||
'ZREVRANK' => true,
|
||||
'ZSCAN' => true,
|
||||
'ZLEXCOUNT' => true,
|
||||
'ZRANGEBYLEX' => true,
|
||||
'HGET' => true,
|
||||
'HMGET' => true,
|
||||
'HEXISTS' => true,
|
||||
@@ -217,6 +219,7 @@ class ReplicationStrategy
|
||||
'OBJECT' => true,
|
||||
'BITCOUNT' => true,
|
||||
'TIME' => true,
|
||||
'PFCOUNT' => true,
|
||||
'SORT' => array($this, 'isSortReadOnly'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -206,17 +206,20 @@ class MultiExecContext implements BasicClientInterface, ExecutableContextInterfa
|
||||
public function executeCommand(CommandInterface $command)
|
||||
{
|
||||
$this->initialize();
|
||||
$response = $this->client->executeCommand($command);
|
||||
|
||||
if ($this->checkState(self::STATE_CAS)) {
|
||||
return $response;
|
||||
return $this->client->executeCommand($command);
|
||||
}
|
||||
|
||||
if (!$response instanceof ResponseQueued) {
|
||||
$this->onProtocolError('The server did not respond with a QUEUED status reply');
|
||||
}
|
||||
$response = $this->client->getConnection()->executeCommand($command);
|
||||
|
||||
$this->commands->enqueue($command);
|
||||
if ($response instanceof ResponseQueued) {
|
||||
$this->commands->enqueue($command);
|
||||
} else if ($response instanceof ResponseErrorInterface) {
|
||||
throw new AbortedMultiExecException($this, $response->getMessage());
|
||||
} else {
|
||||
$this->onProtocolError('The server did not return a +QUEUED status response.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
+1
-2
@@ -10,7 +10,7 @@ name = "Predis"
|
||||
desc = "Flexible and feature-complete PHP client library for Redis"
|
||||
homepage = "http://github.com/nrk/predis"
|
||||
license = "MIT"
|
||||
version = "0.8.5"
|
||||
version = "0.8.6"
|
||||
stability = "stable"
|
||||
channel = "pear.nrk.io"
|
||||
|
||||
@@ -24,7 +24,6 @@ pearinstaller = "1.4.1"
|
||||
*.xml.dist = test
|
||||
*.md = doc
|
||||
LICENSE = doc
|
||||
lib = php
|
||||
|
||||
[optional phpiredis]
|
||||
hint = "Add support for faster protocol handling with phpiredis"
|
||||
|
||||
@@ -20,6 +20,8 @@ use Predis\Profile\ServerProfileInterface;
|
||||
*/
|
||||
abstract class PredisTestCase extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected $redisServerVersion = null;
|
||||
|
||||
/**
|
||||
* Verifies that a Redis command is a valid Predis\Command\CommandInterface
|
||||
* instance with the specified ID and command arguments.
|
||||
@@ -164,13 +166,17 @@ abstract class PredisTestCase extends PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $expectedVersion Expected redis version.
|
||||
* @param string $operator Comparison operator.
|
||||
* @param callable $callback Callback for matching version.
|
||||
* @throws PHPUnit_Framework_SkippedTestError When expected redis version is not met.
|
||||
* Returns the server version of the Redis instance used by the test suite.
|
||||
*
|
||||
* @return string
|
||||
* @throws RuntimeException When the client cannot retrieve the current server version
|
||||
*/
|
||||
protected function executeOnRedisVersion($expectedVersion, $operator, $callback)
|
||||
protected function getRedisServerVersion()
|
||||
{
|
||||
if (isset($this->redisServerVersion)) {
|
||||
return $this->redisServerVersion;
|
||||
}
|
||||
|
||||
$client = $this->createClient(null, null, true);
|
||||
$info = array_change_key_case($client->info());
|
||||
|
||||
@@ -184,6 +190,20 @@ abstract class PredisTestCase extends PHPUnit_Framework_TestCase
|
||||
throw new RuntimeException('Unable to retrieve server info');
|
||||
}
|
||||
|
||||
$this->redisServerVersion = $version;
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $expectedVersion Expected redis version.
|
||||
* @param string $operator Comparison operator.
|
||||
* @param callable $callback Callback for matching version.
|
||||
* @throws PHPUnit_Framework_SkippedTestError When expected redis version is not met
|
||||
*/
|
||||
protected function executeOnRedisVersion($expectedVersion, $operator, $callback)
|
||||
{
|
||||
$version = $this->getRedisServerVersion();
|
||||
$comparation = version_compare($version, $expectedVersion);
|
||||
|
||||
if ($match = eval("return $comparation $operator 0;")) {
|
||||
@@ -211,23 +231,6 @@ abstract class PredisTestCase extends PHPUnit_Framework_TestCase
|
||||
return $match;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $expectedVersion Expected redis version.
|
||||
* @param string $message Optional message.
|
||||
* @param bool $remote Based on local profile or remote redis version.
|
||||
* @throws RuntimeException when unable to retrieve server info or redis version
|
||||
* @throws \PHPUnit_Framework_SkippedTestError when expected redis version is not met
|
||||
*/
|
||||
public function markTestSkippedOnRedisVersionBelow($expectedVersion, $message = '', $remote = true)
|
||||
{
|
||||
$callback = function ($test, $version) use ($message, $expectedVersion) {
|
||||
$test->markTestSkipped($message ?: "Test requires Redis $expectedVersion, current is $version.");
|
||||
};
|
||||
|
||||
$method = $remote ? 'executeOnRedisVersion' : 'executeOnProfileVersion';
|
||||
$this->$method($expectedVersion, '<', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep the test case with microseconds resolution.
|
||||
*
|
||||
@@ -237,4 +240,68 @@ abstract class PredisTestCase extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
usleep($seconds * 1000000);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected function setRequiredRedisVersionFromAnnotation()
|
||||
{
|
||||
$annotations = $this->getAnnotations();
|
||||
|
||||
if (isset($annotations['method']['requiresRedisVersion'], $annotations['method']['group']) &&
|
||||
!empty($annotations['method']['requiresRedisVersion']) &&
|
||||
in_array('connected', $annotations['method']['group'])
|
||||
) {
|
||||
$this->required['requiresRedisVersion'] = $annotations['method']['requiresRedisVersion'][0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected function checkRequiredRedisVersion()
|
||||
{
|
||||
if (!isset($this->required['requiresRedisVersion'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$srvVersion = $this->getRedisServerVersion();
|
||||
$expectation = explode(' ', $this->required['requiresRedisVersion'], 2);
|
||||
|
||||
if (count($expectation) === 1) {
|
||||
$expOperator = '>=';
|
||||
$expVersion = $expectation[0];
|
||||
} else {
|
||||
$expOperator = $expectation[0];
|
||||
$expVersion = $expectation[1];
|
||||
}
|
||||
|
||||
$comparation = version_compare($srvVersion, $expVersion);
|
||||
|
||||
if (!$match = eval("return $comparation $expOperator 0;")) {
|
||||
$this->markTestSkipped(
|
||||
"This test requires Redis $expOperator $expVersion but the current version is $srvVersion."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setRequirementsFromAnnotation()
|
||||
{
|
||||
parent::setRequirementsFromAnnotation();
|
||||
|
||||
$this->setRequiredRedisVersionFromAnnotation();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkRequirements()
|
||||
{
|
||||
parent::checkRequirements();
|
||||
|
||||
$this->checkRequiredRedisVersion();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +277,7 @@ class PredisClusterHashStrategyTest extends PredisTestCase
|
||||
'GETSET' => 'keys-first',
|
||||
'INCR' => 'keys-first',
|
||||
'INCRBY' => 'keys-first',
|
||||
'INCRBYFLOAT' => 'keys-first',
|
||||
'SETBIT' => 'keys-first',
|
||||
'SETEX' => 'keys-first',
|
||||
'MSET' => 'keys-interleaved',
|
||||
@@ -341,6 +342,9 @@ class PredisClusterHashStrategyTest extends PredisTestCase
|
||||
'ZSCORE' => 'keys-first',
|
||||
'ZUNIONSTORE' => 'keys-zaggregated',
|
||||
'ZSCAN' => 'keys-first',
|
||||
'ZLEXCOUNT' => 'keys-first',
|
||||
'ZRANGEBYLEX' => 'keys-first',
|
||||
'ZREMRANGEBYLEX' => 'keys-first',
|
||||
|
||||
/* commands operating on hashes */
|
||||
'HDEL' => 'keys-first',
|
||||
@@ -358,6 +362,11 @@ class PredisClusterHashStrategyTest extends PredisTestCase
|
||||
'HVALS' => 'keys-first',
|
||||
'HSCAN' => 'keys-first',
|
||||
|
||||
/* commands operating on HyperLogLog */
|
||||
'PFADD' => 'keys-first',
|
||||
'PFCOUNT' => 'keys-all',
|
||||
'PFMERGE' => 'keys-all',
|
||||
|
||||
/* scripting */
|
||||
'EVAL' => 'keys-script',
|
||||
'EVALSHA' => 'keys-script',
|
||||
|
||||
@@ -22,14 +22,21 @@ class RedisClusterHashStrategyTest extends PredisTestCase
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testDoesNotSupportKeyTags()
|
||||
public function testSupportsKeyTags()
|
||||
{
|
||||
$strategy = $this->getHashStrategy();
|
||||
|
||||
$this->assertSame(35910, $strategy->getKeyHash('{foo}'));
|
||||
$this->assertSame(60032, $strategy->getKeyHash('{foo}:bar'));
|
||||
$this->assertSame(27528, $strategy->getKeyHash('{foo}:baz'));
|
||||
$this->assertSame(34064, $strategy->getKeyHash('bar:{foo}:bar'));
|
||||
$this->assertSame(44950, $strategy->getKeyHash('{foo}'));
|
||||
$this->assertSame(44950, $strategy->getKeyHash('{foo}:bar'));
|
||||
$this->assertSame(44950, $strategy->getKeyHash('{foo}:baz'));
|
||||
$this->assertSame(44950, $strategy->getKeyHash('bar:{foo}:baz'));
|
||||
$this->assertSame(44950, $strategy->getKeyHash('bar:{foo}:{baz}'));
|
||||
|
||||
$this->assertSame(44950, $strategy->getKeyHash('bar:{foo}:baz{}'));
|
||||
$this->assertSame(9415, $strategy->getKeyHash('{}bar:{foo}:baz'));
|
||||
|
||||
$this->assertSame(0, $strategy->getKeyHash(''));
|
||||
$this->assertSame(31641, $strategy->getKeyHash('{}'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,6 +288,7 @@ class RedisClusterHashStrategyTest extends PredisTestCase
|
||||
'GETSET' => 'keys-first',
|
||||
'INCR' => 'keys-first',
|
||||
'INCRBY' => 'keys-first',
|
||||
'INCRBYFLOAT' => 'keys-first',
|
||||
'SETBIT' => 'keys-first',
|
||||
'SETEX' => 'keys-first',
|
||||
'MSET' => 'keys-interleaved',
|
||||
@@ -334,6 +342,9 @@ class RedisClusterHashStrategyTest extends PredisTestCase
|
||||
'ZREVRANK' => 'keys-first',
|
||||
'ZSCORE' => 'keys-first',
|
||||
'ZSCAN' => 'keys-first',
|
||||
'ZLEXCOUNT' => 'keys-first',
|
||||
'ZRANGEBYLEX' => 'keys-first',
|
||||
'ZREMRANGEBYLEX' => 'keys-first',
|
||||
|
||||
/* commands operating on hashes */
|
||||
'HDEL' => 'keys-first',
|
||||
@@ -351,6 +362,11 @@ class RedisClusterHashStrategyTest extends PredisTestCase
|
||||
'HVALS' => 'keys-first',
|
||||
'HSCAN' => 'keys-first',
|
||||
|
||||
/* commands operating on HyperLogLog */
|
||||
'PFADD' => 'keys-first',
|
||||
'PFCOUNT' => 'keys-all',
|
||||
'PFMERGE' => 'keys-all',
|
||||
|
||||
/* scripting */
|
||||
'EVAL' => 'keys-script',
|
||||
'EVALSHA' => 'keys-script',
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-hyperloglog
|
||||
* @todo Add integration tests depending on the minor redis version
|
||||
*/
|
||||
class HyperLogLogAddTest extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand()
|
||||
{
|
||||
return 'Predis\Command\HyperLogLogAdd';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId()
|
||||
{
|
||||
return 'PFADD';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments()
|
||||
{
|
||||
$arguments = array('key', 'a', 'b', 'c');
|
||||
$expected = array('key', 'a', 'b', 'c');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArgumentsFieldsAsSingleArray()
|
||||
{
|
||||
$arguments = array('key', array('a', 'b', 'c'));
|
||||
$expected = array('key', 'a', 'b', 'c');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse()
|
||||
{
|
||||
$command = $this->getCommand();
|
||||
|
||||
$this->assertSame(false, $command->parseResponse(0));
|
||||
$this->assertSame(true, $command->parseResponse(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
* @expectedException Predis\ServerException
|
||||
* @expectedExceptionMessage Operation against a key holding the wrong kind of value
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->lpush('metavars', 'foo', 'hoge');
|
||||
$redis->pfadd('metavars', 'foofoo');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-hyperloglog
|
||||
* @todo Add integration tests depending on the minor redis version
|
||||
*/
|
||||
class HyperLogLogCountTest extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand()
|
||||
{
|
||||
return 'Predis\Command\HyperLogLogCount';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId()
|
||||
{
|
||||
return 'PFCOUNT';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments()
|
||||
{
|
||||
$arguments = array('key:1', 'key:2');
|
||||
$expected = array('key:1', 'key:2');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArgumentsFieldsAsSingleArray()
|
||||
{
|
||||
$arguments = array(array('key:1', 'key:2'));
|
||||
$expected = array('key:1', 'key:2');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse()
|
||||
{
|
||||
$command = $this->getCommand();
|
||||
|
||||
$this->assertSame(0, $command->parseResponse(0));
|
||||
$this->assertSame(1, $command->parseResponse(1));
|
||||
$this->assertSame(10, $command->parseResponse(10));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
* @expectedException Predis\ServerException
|
||||
* @expectedExceptionMessage Operation against a key holding the wrong kind of value
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->lpush('metavars', 'foo', 'hoge');
|
||||
$redis->pfcount('metavars');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
* @expectedException Predis\ServerException
|
||||
* @expectedExceptionMessage Operation against a key holding the wrong kind of value
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongTypeOfAtLeastOneKey()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->pfadd('metavars:1', 'foo', 'hoge');
|
||||
$redis->lpush('metavars:2', 'foofoo');
|
||||
$redis->pfcount('metavars:1', 'metavars:2');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-hyperloglog
|
||||
* @todo Add integration tests depending on the minor redis version
|
||||
*/
|
||||
class HyperLogLogMergeTest extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand()
|
||||
{
|
||||
return 'Predis\Command\HyperLogLogMerge';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId()
|
||||
{
|
||||
return 'PFMERGE';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments()
|
||||
{
|
||||
$arguments = array('key:1', 'key:2', 'key:3');
|
||||
$expected = array('key:1', 'key:2', 'key:3');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArgumentsFieldsAsSingleArray()
|
||||
{
|
||||
$arguments = array(array('key:1', 'key:2', 'key:3'));
|
||||
$expected = array('key:1', 'key:2', 'key:3');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse()
|
||||
{
|
||||
$this->assertSame('OK', $this->getCommand()->parseResponse('OK'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
* @expectedException Predis\ServerException
|
||||
* @expectedExceptionMessage Operation against a key holding the wrong kind of value
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->pfadd('metavars:1', 'foo', 'hoge');
|
||||
$redis->lpush('metavars:2', 'foofoo');
|
||||
$redis->pfmerge('metavars:1', 'metavars:2');
|
||||
}
|
||||
}
|
||||
@@ -69,9 +69,12 @@ class RawCommandTest extends PredisTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* The signature of RawCommand::create() requires one argument which is the
|
||||
* ID of the command (other arguments are fetched dinamically). If the first
|
||||
* argument is missing, PHP emits an E_WARNING.
|
||||
*
|
||||
* @group disconnected
|
||||
* @expectedException PHPUnit_Framework_Error_Warning
|
||||
* @expectedExceptionMessage Missing argument 1 for Predis\Command\RawCommand::create()
|
||||
*/
|
||||
public function testPHPWarningOnMissingCommandIDWithStaticCreate()
|
||||
{
|
||||
|
||||
@@ -143,11 +143,10 @@ BUFFER;
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.6.9
|
||||
*/
|
||||
public function testGetsNameOfConnection()
|
||||
{
|
||||
$this->markTestSkippedOnRedisVersionBelow('2.6.9');
|
||||
|
||||
$redis = $this->getClient();
|
||||
$clientName = $redis->client('GETNAME');
|
||||
$this->assertNull($clientName);
|
||||
@@ -159,11 +158,10 @@ BUFFER;
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.6.9
|
||||
*/
|
||||
public function testSetsNameOfConnection()
|
||||
{
|
||||
$this->markTestSkippedOnRedisVersionBelow('2.6.9');
|
||||
|
||||
$redis = $this->getClient();
|
||||
|
||||
$expectedConnectionName = 'foo-baz';
|
||||
@@ -185,13 +183,12 @@ BUFFER;
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @expectedException Predis\ServerException
|
||||
* @requiresRedisVersion >= 2.6.9
|
||||
* @dataProvider invalidConnectionNameProvider
|
||||
* @expectedException Predis\ServerException
|
||||
*/
|
||||
public function testInvalidSetNameOfConnection($invalidConnectionName)
|
||||
{
|
||||
$this->markTestSkippedOnRedisVersionBelow('2.6.9');
|
||||
|
||||
$redis = $this->getClient();
|
||||
$redis->client('SETNAME', $invalidConnectionName);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-zset
|
||||
*/
|
||||
class ZSetLexCountTest extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand()
|
||||
{
|
||||
return 'Predis\Command\ZSetLexCount';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId()
|
||||
{
|
||||
return 'ZLEXCOUNT';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments()
|
||||
{
|
||||
$arguments = array('key', '+', '-');
|
||||
$expected = array('key', '+', '-');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse()
|
||||
{
|
||||
$this->assertSame(1, $this->getCommand()->parseResponse(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeys()
|
||||
{
|
||||
$arguments = array('key', '+', '-');
|
||||
$expected = array('prefix:key', '+', '-');
|
||||
|
||||
$command = $this->getCommandWithArgumentsArray($arguments);
|
||||
$command->prefixKeys('prefix:');
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeysIgnoredOnEmptyArguments()
|
||||
{
|
||||
$command = $this->getCommand();
|
||||
$command->prefixKeys('prefix:');
|
||||
|
||||
$this->assertSame(array(), $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testExclusiveIntervalRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(3, $redis->zlexcount('letters', '(b', '(f'));
|
||||
$this->assertSame(5, $redis->zlexcount('letters', '(b', '(z'));
|
||||
$this->assertSame(4, $redis->zlexcount('letters', '(0', '(e'));
|
||||
$this->assertSame(0, $redis->zlexcount('letters', '(f', '(b'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testInclusiveIntervalRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(5, $redis->zlexcount('letters', '[b', '[f'));
|
||||
$this->assertSame(6, $redis->zlexcount('letters', '[b', '[z'));
|
||||
$this->assertSame(5, $redis->zlexcount('letters', '[0', '[e'));
|
||||
$this->assertSame(0, $redis->zlexcount('letters', '[f', '[b'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testWholeRangeInterval()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(7, $redis->zlexcount('letters', '-', '+'));
|
||||
$this->assertSame(0, $redis->zlexcount('letters', '+', '-'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
* @expectedException Predis\ServerException
|
||||
* @expectedExceptionMessage min or max not valid string range item
|
||||
*/
|
||||
public function testThrowsExceptionOnInvalidRangeFormat()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
$redis->zlexcount('letters', 'b', 'f');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
* @expectedException Predis\ServerException
|
||||
* @expectedExceptionMessage Operation against a key holding the wrong kind of value
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
$redis->zlexcount('foo', '+', '-');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-zset
|
||||
*/
|
||||
class ZSetRangeByLexTest extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand()
|
||||
{
|
||||
return 'Predis\Command\ZSetRangeByLex';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId()
|
||||
{
|
||||
return 'ZRANGEBYLEX';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments()
|
||||
{
|
||||
$modifiers = array(
|
||||
'limit' => array(0, 100),
|
||||
);
|
||||
|
||||
$arguments = array('zset', '[a', '[z', $modifiers);
|
||||
$expected = array('zset', '[a', '[z', 'LIMIT', 0, 100);
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArgumentsWithNamedLimit()
|
||||
{
|
||||
$arguments = array('zset', '[a', '[z', array('limit' => array('offset' => 1, 'count' => 2)));
|
||||
$expected = array('zset', '[a', '[z', 'LIMIT', 1, 2);
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse()
|
||||
{
|
||||
$raw = array('a', 'b', 'c');
|
||||
$expected = array('a', 'b', 'c');
|
||||
|
||||
$command = $this->getCommand();
|
||||
|
||||
$this->assertSame($expected, $command->parseResponse($raw));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeys()
|
||||
{
|
||||
$modifiers = array(
|
||||
'limit' => array(0, 100),
|
||||
);
|
||||
|
||||
$arguments = array('zset', '[a', '[z', $modifiers);
|
||||
$expected = array('prefix:zset', '[a', '[z', 'LIMIT', 0, 100);
|
||||
|
||||
$command = $this->getCommandWithArgumentsArray($arguments);
|
||||
$command->prefixKeys('prefix:');
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeysIgnoredOnEmptyArguments()
|
||||
{
|
||||
$command = $this->getCommand();
|
||||
$command->prefixKeys('prefix:');
|
||||
|
||||
$this->assertSame(array(), $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testReturnsElementsInWholeRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(array('a', 'b', 'c', 'd', 'e', 'f', 'g'), $redis->zrangebylex('letters', '-', '+'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('letters', '+', '-'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('unknown', '-', '+'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('unknown', '+', '-'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testReturnsElementsInInclusiveRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(array('a'), $redis->zrangebylex('letters', '[a', '[a'));
|
||||
$this->assertSame(array('c', 'd', 'e', 'f'), $redis->zrangebylex('letters', '[c', '[f'));
|
||||
$this->assertSame(array('a', 'b', 'c'), $redis->zrangebylex('letters', '-', '[c'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('letters', '+', '[c'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('letters', '[x', '[z'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('unknown', '[0', '[1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testReturnsElementsInExclusiveRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(array(), $redis->zrangebylex('letters', '(a', '(a'));
|
||||
$this->assertSame(array('d', 'e'), $redis->zrangebylex('letters', '(c', '(f'));
|
||||
$this->assertSame(array('a', 'b'), $redis->zrangebylex('letters', '-', '(c'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('letters', '+', '(c'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('letters', '(x', '(z'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('unknown', '(0', '(1'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testReturnsElementsInMixedRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(array(), $redis->zrangebylex('letters', '[a', '(a'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('letters', '(a', '[a'));
|
||||
$this->assertSame(array('c', 'd', 'e'), $redis->zrangebylex('letters', '[c', '(f'));
|
||||
$this->assertSame(array('d', 'e', 'f'), $redis->zrangebylex('letters', '(c', '[f'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('unknown', '[0', '(5'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testRangeWithLimitModifier()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(array('c', 'd', 'e'), $redis->zrangebylex('letters', '-', '+', 'LIMIT', '2', '3'));
|
||||
$this->assertSame(array('c', 'd', 'e'), $redis->zrangebylex('letters', '-', '+', array('limit' => array(2, 3))));
|
||||
$this->assertSame(array('c', 'd', 'e'), $redis->zrangebylex('letters', '-', '+', array('limit' => array('offset' => 2, 'count' => 3))));
|
||||
$this->assertSame(array(), $redis->zrangebylex('letters', '[a', '[f', 'LIMIT', '2', '0'));
|
||||
$this->assertSame(array(), $redis->zrangebylex('letters', '[a', '[f', 'LIMIT', '-4', '2'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
* @expectedException Predis\ServerException
|
||||
* @expectedExceptionMessage min or max not valid string range item
|
||||
*/
|
||||
public function testThrowsExceptionOnInvalidRangeFormat()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
$redis->zrangebylex('letters', 'b', 'f');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
* @expectedException Predis\ServerException
|
||||
* @expectedExceptionMessage Operation against a key holding the wrong kind of value
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
$redis->zrangebylex('foo', '-', '+');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-zset
|
||||
*/
|
||||
class ZSetRemoveRangeByLexTest extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand()
|
||||
{
|
||||
return 'Predis\Command\ZSetRemoveRangeByLex';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId()
|
||||
{
|
||||
return 'ZREMRANGEBYLEX';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments()
|
||||
{
|
||||
$arguments = array('key', '[a', '[b');
|
||||
$expected = array('key', '[a', '[b');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse()
|
||||
{
|
||||
$this->assertSame(1, $this->getCommand()->parseResponse(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeys()
|
||||
{
|
||||
$arguments = array('key', '[a', '[b');
|
||||
$expected = array('prefix:key', '[a', '[b');
|
||||
|
||||
$command = $this->getCommandWithArgumentsArray($arguments);
|
||||
$command->prefixKeys('prefix:');
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeysIgnoredOnEmptyArguments()
|
||||
{
|
||||
$command = $this->getCommand();
|
||||
$command->prefixKeys('prefix:');
|
||||
|
||||
$this->assertSame(array(), $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testRemovesRangeByLexWithWholeRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(0, $redis->zremrangebylex('letters', '+', '-'));
|
||||
$this->assertSame(7, $redis->zremrangebylex('letters', '-', '+'));
|
||||
|
||||
$this->assertSame(array(), $redis->zrange('letters', 0, -1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testRemovesRangeByLexWithInclusiveRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(3, $redis->zremrangebylex('letters', '[b', '[d'));
|
||||
$this->assertSame(array('a', 'e', 'f', 'g'), $redis->zrange('letters', 0, -1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testRemovesRangeByLexWithExclusiveRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(3, $redis->zremrangebylex('letters', '(a', '(e'));
|
||||
$this->assertSame(array('a', 'e', 'f', 'g'), $redis->zrange('letters', 0, -1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
*/
|
||||
public function testRemovesRangeByLexWithMixedRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
|
||||
$this->assertSame(3, $redis->zremrangebylex('letters', '[b', '(e'));
|
||||
$this->assertSame(array('a', 'e', 'f', 'g'), $redis->zrange('letters', 0, -1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
* @expectedException Predis\ServerException
|
||||
* @expectedExceptionMessage min or max not valid string range item
|
||||
*/
|
||||
public function testThrowsExceptionOnInvalidRangeFormat()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->zadd('letters', 0, 'a', 0, 'b', 0, 'c', 0, 'd', 0, 'e', 0, 'f', 0, 'g');
|
||||
$redis->zremrangebylex('letters', 'b', 'f');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.9
|
||||
* @expectedException Predis\ServerException
|
||||
* @expectedExceptionMessage Operation against a key holding the wrong kind of value
|
||||
*/
|
||||
public function testThrowsExceptionOnWrongType()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
$redis->zremrangebylex('foo', '[a', '[b');
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ namespace Predis\Connection;
|
||||
|
||||
use PredisTestCase;
|
||||
use Predis\ResponseError;
|
||||
use Predis\Command\RawCommand;
|
||||
use Predis\Profile\ServerProfile;
|
||||
|
||||
/**
|
||||
@@ -600,6 +601,64 @@ class RedisClusterTest extends PredisTestCase
|
||||
$this->assertSame(3, count($cluster));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFetchSlotsMapFromClusterWithClusterNodesCommand()
|
||||
{
|
||||
$output =<<<EOS
|
||||
1284a54dc3245d305ae5e3f5507579f9a3e4bd80 10.1.0.51:6387 master - 0 1400000953094 20 connected 12288-13311
|
||||
1fb644aa255ef92db789a93cc834f597e9a3800b 10.1.0.52:6392 master - 0 1400000955139 15 connected 3072-4095
|
||||
fe814c66d4171db548b6dce537a88c3994cd7197 10.1.0.51:6391 slave 7ca7ed3780c405ee50757550e19ade5921097d25 0 1400000954111 5 connected
|
||||
b3a3b7564c75c096bdc72fc314500cdd666adf87 :0 myself,master - 0 0 18 connected 6144-7167
|
||||
67b25b19a07b56a155e25145957dce82a6605f26 10.1.0.51:6388 master - 0 1400000954111 21 connected 14336-15359
|
||||
ba6e596ae5382ad05f1df5abaaa1489a0e0b2449 10.1.0.52:6398 master - 0 1400000953094 13 connected 15360-16383
|
||||
7ca7ed3780c405ee50757550e19ade5921097d25 10.1.0.52:6391 master - 0 1400000954111 5 connected 1024-2047
|
||||
4d90ab772120a49b177eb7bd4799b592f5048e16 10.1.0.51:6394 slave fa9b9fae41e3d53d2a901dbfe6fd1527c874768c 0 1400000955139 26 connected
|
||||
5731adc383f5f7afbfcca2ee75d17609e436b146 10.1.0.51:6398 slave ba6e596ae5382ad05f1df5abaaa1489a0e0b2449 0 1400000953094 21 connected
|
||||
fd0bd0cd8461bd44b759fefaabaf541788ec65cf 10.1.0.52:6396 master - 0 1400000955139 7 connected 11264-12287
|
||||
d012ddf044d0ef5d72d171f8af1e700488b79241 10.1.0.52:6393 master - 0 1400000953604 10 connected 5120-6143
|
||||
462ab20db112a007f3ffc4e7e105b0c963aa8981 10.1.0.52:6382 slave a1948d989b092bc96ff91019d9d3b74361edcdfa 0 1400000954111 18 connected
|
||||
f468a7018ff5a0701a1b614b8ed1b400cddcd578 10.1.0.52:6385 slave 2a1845f27d0f24904d67e2f2cd3e7380949e9d3a 0 1400000953604 25 connected
|
||||
a48e023927edcc0d001be0535130f41d8e93adde 10.1.0.52:6383 slave a547c6de8acfbf4a97a1c341bddc2efffea83032 0 1400000955139 12 connected
|
||||
2530af9ee9b8d4e359cefedd7492ba777dd1e5aa 10.1.0.51:6392 slave 1fb644aa255ef92db789a93cc834f597e9a3800b 0 1400000953603 17 connected
|
||||
2d3934ef56abbe25949063474d2e408ae2fb41af 10.1.0.51:6395 slave 0f70284073a6613343fd9a2da82a0549c1efaaae 0 1400000954627 27 connected
|
||||
6a0430bf8e1beed8911aa69b002ac7a00ad60428 10.1.0.52:6387 slave 1284a54dc3245d305ae5e3f5507579f9a3e4bd80 0 1400000954111 20 connected
|
||||
578ad5ff1970a22419a6afca0cc97bfb729f6b47 10.1.0.51:6397 slave fd637fc700d31395d1f324348b49ad38248ba8d4 0 1400000955139 18 connected
|
||||
c3083761437050f1cd80a5f95b3ff5aabba3bb97 10.1.0.51:6381 master - 0 1400000955139 11 connected 0-1023
|
||||
846d3410ce8e7cea565617686f4685a35d376706 10.1.0.52:6384 slave b3a3b7564c75c096bdc72fc314500cdd666adf87 0 1400000953094 18 connected
|
||||
fd637fc700d31395d1f324348b49ad38248ba8d4 10.1.0.52:6397 master - 0 1400000954627 16 connected 13312-14335
|
||||
a547c6de8acfbf4a97a1c341bddc2efffea83032 10.1.0.51:6383 master - 0 1400000954627 8 connected 4096-5119
|
||||
0f70284073a6613343fd9a2da82a0549c1efaaae 10.1.0.52:6395 master - 0 1400000954627 27 connected 9216-10239
|
||||
2a1845f27d0f24904d67e2f2cd3e7380949e9d3a 10.1.0.51:6385 master - 0 1400000953603 25 connected 8192-9215
|
||||
ba018d6d154205a47b83de33efbbe846cb6f4b1f 10.1.0.52:6381 slave c3083761437050f1cd80a5f95b3ff5aabba3bb97 0 1400000955139 11 connected
|
||||
5a830c9ef0d09eb25b521395466e803052ddbef4 10.1.0.51:6386 master - 0 1400000954627 9 connected 10240-11263
|
||||
91d9840437794ab1ff77b59f831ce80ffaaa4c5a 10.1.0.51:6393 slave d012ddf044d0ef5d72d171f8af1e700488b79241 0 1400000954627 14 connected
|
||||
d6bdbb495fcb66ba3001c2a07c067d5aa354cf55 10.1.0.52:6388 slave 67b25b19a07b56a155e25145957dce82a6605f26 0 1400000954111 21 connected
|
||||
2176380db50d687d37e37c3daeeea6e7f8feed0c 10.1.0.51:6396 slave fd0bd0cd8461bd44b759fefaabaf541788ec65cf 0 1400000953603 22 connected
|
||||
c93ffb4a2ff815f9029156383b00f106234b9546 10.1.0.52:6386 slave 5a830c9ef0d09eb25b521395466e803052ddbef4 0 1400000953604 9 connected
|
||||
a1948d989b092bc96ff91019d9d3b74361edcdfa 10.1.0.51:6382 master - 0 1400000953094 17 connected 2048-3071
|
||||
fa9b9fae41e3d53d2a901dbfe6fd1527c874768c 10.1.0.52:6394 master - 0 1400000953094 1 connected 7168-8191
|
||||
EOS;
|
||||
|
||||
$command = RawCommand::create('CLUSTER', 'NODES');
|
||||
|
||||
$connection1 = $this->getMockConnection('tcp://10.1.0.51:6384');
|
||||
$connection1->expects($this->once())
|
||||
->method('executeCommand')
|
||||
->with($command)
|
||||
->will($this->returnValue($output));
|
||||
|
||||
$factory = $this->getMock('Predis\Connection\ConnectionFactoryInterface');
|
||||
|
||||
$cluster = new RedisCluster($factory);
|
||||
$cluster->add($connection1);
|
||||
|
||||
$cluster->askClusterNodes();
|
||||
|
||||
$this->assertSame($cluster->getConnectionBySlot('6144'), $connection1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @expectedException Predis\NotSupportedException
|
||||
|
||||
@@ -19,7 +19,7 @@ use Predis\Command\Processor\ProcessorChain;
|
||||
*/
|
||||
class ServerProfileTest extends PredisTestCase
|
||||
{
|
||||
const DEFAULT_PROFILE_VERSION = '2.6';
|
||||
const DEFAULT_PROFILE_VERSION = '2.8';
|
||||
const DEVELOPMENT_PROFILE_VERSION = '3.0';
|
||||
|
||||
/**
|
||||
|
||||
@@ -176,7 +176,13 @@ class ServerVersion28Test extends PredisProfileTestCase
|
||||
135 => 'scan',
|
||||
136 => 'sscan',
|
||||
137 => 'zscan',
|
||||
138 => 'hscan',
|
||||
138 => 'zlexcount',
|
||||
139 => 'zrangebylex',
|
||||
140 => 'zremrangebylex',
|
||||
141 => 'hscan',
|
||||
142 => 'pfadd',
|
||||
143 => 'pfcount',
|
||||
144 => 'pfmerge',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,13 @@ class ServerVersionNextTest extends PredisProfileTestCase
|
||||
135 => 'scan',
|
||||
136 => 'sscan',
|
||||
137 => 'zscan',
|
||||
138 => 'hscan',
|
||||
138 => 'zlexcount',
|
||||
139 => 'zrangebylex',
|
||||
140 => 'zremrangebylex',
|
||||
141 => 'hscan',
|
||||
142 => 'pfadd',
|
||||
143 => 'pfcount',
|
||||
144 => 'pfmerge',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +286,7 @@ class ReplicationStrategyTest extends PredisTestCase
|
||||
'GETSET' => 'write',
|
||||
'INCR' => 'write',
|
||||
'INCRBY' => 'write',
|
||||
'INCRBYFLOAT' => 'write',
|
||||
'SETBIT' => 'write',
|
||||
'SETEX' => 'write',
|
||||
'MSET' => 'write',
|
||||
@@ -341,6 +342,9 @@ class ReplicationStrategyTest extends PredisTestCase
|
||||
'ZREVRANK' => 'read',
|
||||
'ZSCORE' => 'read',
|
||||
'ZSCAN' => 'read',
|
||||
'ZLEXCOUNT' => 'read',
|
||||
'ZRANGEBYLEX' => 'read',
|
||||
'ZREMRANGEBYLEX' => 'write',
|
||||
|
||||
/* commands operating on hashes */
|
||||
'HDEL' => 'write',
|
||||
@@ -357,6 +361,11 @@ class ReplicationStrategyTest extends PredisTestCase
|
||||
'HVALS' => 'read',
|
||||
'HSCAN' => 'read',
|
||||
|
||||
/* commands operating on HyperLogLog */
|
||||
'PFADD' => 'write',
|
||||
'PFMERGE' => 'write',
|
||||
'PFCOUNT' => 'read',
|
||||
|
||||
/* scripting */
|
||||
'EVAL' => 'write',
|
||||
'EVALSHA' => 'write',
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Predis\Transaction;
|
||||
use PredisTestCase;
|
||||
use Predis\Client;
|
||||
use Predis\ResponseQueued;
|
||||
use Predis\ResponseError;
|
||||
use Predis\ServerException;
|
||||
use Predis\Command\CommandInterface;
|
||||
|
||||
@@ -473,6 +474,50 @@ class MultiExecContextTest extends PredisTestCase
|
||||
$this->assertSame(array('MULTI', 'SET', 'ECHO', 'DISCARD'), self::commandsToIDs($commands));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testProperlyDiscardsTransactionAfterServerExceptionInBlock()
|
||||
{
|
||||
$connection = $this->getMockedConnection(function (CommandInterface $command) {
|
||||
switch ($command->getId()) {
|
||||
case 'MULTI':
|
||||
return true;
|
||||
|
||||
case 'ECHO':
|
||||
return new ResponseError('ERR simulated failure on ECHO');
|
||||
|
||||
case 'EXEC':
|
||||
return new ResponseError('EXECABORT Transaction discarded because of previous errors.');
|
||||
|
||||
default:
|
||||
return new ResponseQueued();
|
||||
}
|
||||
});
|
||||
|
||||
$client = new Client($connection);
|
||||
|
||||
// First attempt
|
||||
$tx = new MultiExecContext($client);
|
||||
|
||||
try {
|
||||
$tx->multi()->set('foo', 'bar')->echo('simulated failure')->exec();
|
||||
} catch (\Exception $exception) {
|
||||
$this->assertInstanceOf('Predis\Transaction\AbortedMultiExecException', $exception);
|
||||
$this->assertSame('ERR simulated failure on ECHO', $exception->getMessage());
|
||||
}
|
||||
|
||||
// Second attempt
|
||||
$tx = new MultiExecContext($client);
|
||||
|
||||
try {
|
||||
$tx->multi()->set('foo', 'bar')->echo('simulated failure')->exec();
|
||||
} catch (\Exception $exception) {
|
||||
$this->assertInstanceOf('Predis\Transaction\AbortedMultiExecException', $exception);
|
||||
$this->assertSame('ERR simulated failure on ECHO', $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ******************************************************************** //
|
||||
// ---- INTEGRATION TESTS --------------------------------------------- //
|
||||
// ******************************************************************** //
|
||||
|
||||
Reference in New Issue
Block a user