Merge nicolasff:b9a16b5ad5 in, fixing for Win32

Now we should be up to master with upstream for an easier merge.
This commit is contained in:
vostok4
2014-04-09 11:14:45 +02:00
parent 978fbcf6fc
commit 9c12c40a66
14 changed files with 1352 additions and 86 deletions
+1
View File
@@ -2,3 +2,4 @@ Redis client extension for PHP
Alfonso Jimenez (yo@alfonsojimenez.com)
Nasreddine Bouafif (n.bouafif@owlient.eu)
Nicolas Favre-Felix (n.favre-felix@owlient.eu)
Michael Grunder (michael.grunder@gmail.com)
+172 -5
View File
@@ -3,7 +3,7 @@
The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt).
This code has been developed and maintained by Owlient from November 2009 to March 2011.
You can send comments, patches, questions [here on github](https://github.com/nicolasff/phpredis/issues) or to n.favrefelix@gmail.com ([@yowgi](http://twitter.com/yowgi)).
You can send comments, patches, questions [here on github](https://github.com/nicolasff/phpredis/issues), to n.favrefelix@gmail.com ([@yowgi](http://twitter.com/yowgi)), or to michael.grunder@gmail.com ([@grumi78](http://twitter.com/grumi78)).
# Table of contents
@@ -69,6 +69,10 @@ Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tag
See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports).
You can install install it using Homebrew:
- [Get homebrew-php](https://github.com/josegonzalez/homebrew-php)
- `brew install php55-redis` (or php53-redis, php54-redis)
## PHP Session handler
@@ -268,6 +272,15 @@ $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // use built-in
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // use igBinary serialize/unserialize
$redis->setOption(Redis::OPT_PREFIX, 'myAppName:'); // use custom prefix on all keys
/* Options for the SCAN family of commands, indicating whether to abstract
empty results from the user. If set to SCAN_NORETRY (the default), phpredis
will just issue one SCAN command at a time, sometimes returning an empty
array of results. If set to SCAN_RETRY, phpredis will retry the scan command
until keys come back OR Redis returns an iterator of zero
*/
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
~~~
@@ -607,6 +620,7 @@ $redis->slowlog('len');
* [expire, setTimeout, pexpire](#expire-settimeout-pexpire) - Set a key's time to live in seconds
* [expireAt, pexpireAt](#expireat-pexpireat) - Set the expiration for a key as a UNIX timestamp
* [keys, getKeys](#keys-getkeys) - Find all keys matching the given pattern
* [scan](#scan) - Scan for keys in the keyspace (Redis >= 2.8.0)
* [migrate](#migrate) - Atomically transfer a key from a Redis instance to another one
* [move](#move) - Move a key to another database
* [object](#object) - Inspect the internals of Redis objects
@@ -658,10 +672,10 @@ $redis->set('key', 'value');
$redis->set('key','value', 10);
// Will set the key, if it doesn't exist, with a ttl of 10 seconds
$redis->set('key', 'value', Array('nx', 'ex'=>10);
$redis->set('key', 'value', Array('nx', 'ex'=>10));
// Will set a key, if it does exist, with a ttl of 1000 miliseconds
$redis->set('key', 'value', Array('xx', 'px'=>1000);
$redis->set('key', 'value', Array('xx', 'px'=>1000));
~~~
@@ -780,7 +794,7 @@ $redis->incrByFloat('key1', 1.5); /* key1 didn't exist, so it will now be 1.5 */
$redis->incrByFloat('key1', 1.5); /* 3 */
$redis->incrByFloat('key1', -1.5); /* 1.5 */
$redis->incrByFloat('key1', 2.5); /* 3.5 */
$redis->incrByFloat('key1', 2.5); /* 4 */
~~~
### decr, decrBy
@@ -953,7 +967,29 @@ $allKeys = $redis->keys('*'); // all keys will match this.
$keyWithUserPrefix = $redis->keys('user*');
~~~
### scan
-----
_**Description**_: Scan the keyspace for keys
##### *Parameters*
*LONG (reference)*: Iterator, initialized to NULL
*STRING, Optional*: Pattern to match
*LONG, Optional*: Count of keys per iteration (only a suggestion to Redis)
##### *Return value*
*Array, boolean*: This function will return an array of keys or FALSE if there are no more keys
##### *Example*
~~~
$it = NULL; /* Initialize our iterator to NULL */
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* retry when we get no keys back */
while($arr_keys = $redis->scan($it)) {
foreach($arr_keys as $str_key) {
echo "Here is a key: $str_key\n";
}
echo "No more keys to scan!\n";
}
~~~
### object
-----
@@ -1261,9 +1297,13 @@ _**Description**_: Migrates a key to a different Redis instance.
*key* string. The key to migrate.
*destination-db* integer. The target DB.
*timeout* integer. The maximum amount of time given to this transfer.
*copy* boolean, optional. Should we send the COPY flag to redis
*replace* boolean, optional. Should we send the REPLACE flag to redis
##### *Examples*
~~~
$redis->migrate('backup', 6379, 'foo', 0, 3600);
$redis->migrate('backup', 6379, 'foo', 0, 3600, true, true); /* copy and replace */
$redis->migrate('backup', 6379, 'foo', 0, 3600, false, true); /* just REPLACE flag */
~~~
@@ -1283,6 +1323,7 @@ $redis->migrate('backup', 6379, 'foo', 0, 3600);
* [hSet](#hset) - Set the string value of a hash field
* [hSetNx](#hsetnx) - Set the value of a hash field, only if the field does not exist
* [hVals](#hvals) - Get all the values in a hash
* [hScan](#hscan) - Scan a hash key for members
### hSet
-----
@@ -1542,7 +1583,28 @@ $redis->hSet('h', 'field2', 'value2');
$redis->hmGet('h', array('field1', 'field2')); /* returns array('field1' => 'value1', 'field2' => 'value2') */
~~~
### hScan
-----
_**Description**_: Scan a HASH value for members, with an optional pattern and count
##### *Parameters*
*key*: String
*iterator*: Long (reference)
*pattern*: Optional pattern to match against
*count*: How many keys to return in a go (only a sugestion to Redis)
##### *Return value*
*Array* An array of members that match our pattern
##### *Examples*
~~~
$it = NULL;
/* Don't ever return an empty array until we're done iterating */
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
while($arr_keys = $redis->hscan('hash', $it)) {
foreach($arr_keys as $str_field => $str_value) {
echo "$str_field => $str_value\n"; /* Print the hash member and value */
}
}
~~~
## Lists
@@ -1981,6 +2043,7 @@ $redis->lSize('key1');/* 2 */
* [sRem, sRemove](#srem-sremove) - Remove one or more members from a set
* [sUnion](#sunion) - Add multiple sets
* [sUnionStore](#sunionstore) - Add multiple sets and store the resulting set in a key
* [sScan](#sscan) - Scan a set for members
### sAdd
-----
@@ -2380,6 +2443,41 @@ array(4) {
}
~~~
### sScan
-----
_**Description**_: Scan a set for members
##### *Parameters*
*Key*: The set to search
*iterator*: LONG (reference) to the iterator as we go
*pattern*: String, optional pattern to match against
*count*: How many members to return at a time (Redis might return a different amount)
##### *Return value*
*Array, boolean*: PHPRedis will return an array of keys or FALSE when we're done iterating
##### *Example*
~~~
$it = NULL;
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* don't return empty results until we're done */
while($arr_mems = $redis->sscan('set', $it, "*pattern*")) {
foreach($arr_mems as $str_mem) {
echo "Member: $str_mem\n";
}
}
$it = NULL;
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); /* return after each iteration, even if empty */
while(($arr_mems = $redis->sscan('set', $it, "*pattern*"))!==FALSE) {
if(count($arr_mems) > 0) {
foreach($arr_mems as $str_mem) {
echo "Member found: $str_mem\n";
}
} else {
echo "No members in this iteration, iterator value: $it\n";
}
}
~~~
## Sorted sets
@@ -2397,6 +2495,7 @@ array(4) {
* [zRevRange](#zrevrange) - Return a range of members in a sorted set, by index, with scores ordered from high to low
* [zScore](#zscore) - Get the score associated with the given member in a sorted set
* [zUnion](#zunion) - Add multiple sorted sets and store the resulting sorted set in a new key
* [zScan](#zscan) - Scan a sorted set for members
### zAdd
-----
@@ -2736,11 +2835,36 @@ $redis->zUnion('ko2', array('k1', 'k2'), array(1, 1)); /* 4, 'ko2' => array('val
$redis->zUnion('ko3', array('k1', 'k2'), array(5, 1)); /* 4, 'ko3' => array('val0', 'val2', 'val3', 'val1') */
~~~
### zScan
-----
_**Description**_: Scan a sorted set for members, with optional pattern and count
##### *Parameters*
*key*: String, the set to scan
*iterator*: Long (reference), initialized to NULL
*pattern*: String (optional), the pattern to match
*count*: How many keys to return per iteration (Redis might return a different number)
##### *Return value*
*Array, boolean* PHPRedis will return matching keys from Redis, or FALSE when iteration is complete
##### *Example*
~~~
$it = NULL;
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
while($arr_matches = $redis->zscan('zset', $it, '*pattern*')) {
foreach($arr_matches as $str_mem => $f_score) {
echo "Key: $str_mem, Score: $f_score\n";
}
}
~~~
## Pub/sub
* [psubscribe](#psubscribe) - Subscribe to channels by pattern
* [publish](#publish) - Post a message to a channel
* [subscribe](#subscribe) - Subscribe to channels
* [pubsub](#pubsub) - Introspection into the pub/sub subsystem
### psubscribe
-----
@@ -2801,6 +2925,26 @@ function f($redis, $chan, $msg) {
$redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans
~~~
### pubsub
-----
_**Description**_: A command allowing you to get information on the Redis pub/sub system.
##### *Parameters*
*keyword*: String, which can be: "channels", "numsub", or "numpat"
*argument*: Optional, variant. For the "channels" subcommand, you can pass a string pattern. For "numsub" an array of channel names.
##### *Return value*
*CHANNELS*: Returns an array where the members are the matching channels.
*NUMSUB*: Returns a key/value array where the keys are channel names and values are their counts.
*NUMPAT*: Integer return containing the number active pattern subscriptions
##### *Example*
~~~
$redis->pubsub("channels"); /*All channels */
$redis->pubsub("channels", "*pattern*"); /* Just channels matching your pattern */
$redis->pubsub("numsub", Array("chan1", "chan2")); /*Get subscriber counts for 'chan1' and 'chan2'*/
$redsi->pubsub("numpat"); /* Get the number of pattern subscribers */
```
## Transactions
@@ -2867,6 +3011,7 @@ $ret = FALSE if x has been modified between the call to WATCH and the call to EX
* [clearLastError](#) - Clear the last error message
* [_prefix](#) - A utility method to prefix the value with the prefix setting for phpredis
* [_unserialize](#) - A utility method to unserialize data with whatever serializer is set up
* [_serialize](#) - A utility method to serialize data with whatever serializer is set up
### eval
-----
@@ -3016,6 +3161,28 @@ $redis->setOption(Redis::OPT_PREFIX, 'my-prefix:');
$redis->_prefix('my-value'); // Will return 'my-prefix:my-value'
~~~
### _serialize
-----
_**Description**_: A utility method to serialize values manually.
This method allows you to serialize a value with whatever serializer is configured, manually.
This can be useful for serialization/unserialization of data going in and out of EVAL commands
as phpredis can't automatically do this itself. Note that if no serializer is set, phpredis
will change Array values to 'Array', and Objects to 'Object'.
##### *Parameters*
*value*: Mixed. The value to be serialized
##### *Examples*
~~~
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
$redis->_serialize("foo"); // returns "foo"
$redis->_serialize(Array()); // Returns "Array"
$redis->_serialize(new stdClass()); // Returns "Object"
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
$redis->_serialize("foo"); // Returns 's:3:"foo";'
### _unserialize
-----
_**Description**_: A utility method to unserialize data with whatever serializer is set up.
@@ -3080,7 +3247,7 @@ None
### GetTimeout
-----
_**Description**_: Get the (write) timeout in use for phpreids
_**Description**_: Get the (write) timeout in use for phpredis
##### *Parameters*
None
+28
View File
@@ -37,22 +37,48 @@ typedef enum _REDIS_REPLY_TYPE {
TYPE_MULTIBULK = '*'
} REDIS_REPLY_TYPE;
/* SCAN variants */
typedef enum _REDIS_SCAN_TYPE {
TYPE_SCAN,
TYPE_SSCAN,
TYPE_HSCAN,
TYPE_ZSCAN
} REDIS_SCAN_TYPE;
/* PUBSUB subcommands */
typedef enum _PUBSUB_TYPE {
PUBSUB_CHANNELS,
PUBSUB_NUMSUB,
PUBSUB_NUMPAT
} PUBSUB_TYPE;
/* options */
#define REDIS_OPT_SERIALIZER 1
#define REDIS_OPT_PREFIX 2
#define REDIS_OPT_READ_TIMEOUT 3
#define REDIS_OPT_SCAN 4
/* serializers */
#define REDIS_SERIALIZER_NONE 0
#define REDIS_SERIALIZER_PHP 1
#define REDIS_SERIALIZER_IGBINARY 2
/* SCAN options */
#define REDIS_SCAN_NORETRY 0
#define REDIS_SCAN_RETRY 1
/* GETBIT/SETBIT offset range limits */
#define BITOP_MIN_OFFSET 0
#define BITOP_MAX_OFFSET 4294967295
#define IF_MULTI() if(redis_sock->mode == MULTI)
#define IF_MULTI_OR_ATOMIC() if(redis_sock->mode == MULTI || redis_sock->mode == ATOMIC)\
#define IF_MULTI_OR_PIPELINE() if(redis_sock->mode == MULTI || redis_sock->mode == PIPELINE)
#define IF_PIPELINE() if(redis_sock->mode == PIPELINE)
#define IF_NOT_MULTI() if(redis_sock->mode != MULTI)
#define IF_NOT_ATOMIC() if(redis_sock->mode != ATOMIC)
#define IF_ATOMIC() if(redis_sock->mode == ATOMIC)
#define ELSE_IF_MULTI() else if(redis_sock->mode == MULTI) { \
if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\
@@ -197,6 +223,8 @@ typedef struct {
char *err;
int err_len;
zend_bool lazy_connect;
int scan;
} RedisSock;
/* }}} */
+53 -1
View File
@@ -106,6 +106,54 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
return 0;
}
PHP_REDIS_API int
redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
REDIS_SCAN_TYPE type, long *iter)
{
REDIS_REPLY_TYPE reply_type;
int reply_info;
char *p_iter;
/* Our response should have two multibulk replies */
if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0
|| reply_type != TYPE_MULTIBULK || reply_info != 2)
{
return -1;
}
/* The BULK response iterator */
if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0
|| reply_type != TYPE_BULK)
{
return -1;
}
/* Attempt to read the iterator */
if(!(p_iter = redis_sock_read_bulk_reply(redis_sock, reply_info TSRMLS_CC))) {
return -1;
}
/* Push the iterator out to the caller */
*iter = atol(p_iter);
efree(p_iter);
/* Read our actual keys/members/etc differently depending on what kind of
scan command this is. They all come back in slightly different ways */
switch(type) {
case TYPE_SCAN:
return redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
case TYPE_SSCAN:
return redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
case TYPE_ZSCAN:
return redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
case TYPE_HSCAN:
return redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
default:
return -1;
}
}
PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
char inbuf[1024];
int numElems;
@@ -1074,6 +1122,8 @@ PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned sh
redis_sock->err = NULL;
redis_sock->err_len = 0;
redis_sock->scan = REDIS_SCAN_NORETRY;
return redis_sock;
}
@@ -1498,8 +1548,10 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, int *val_len TSRMLS_
#endif
smart_str sstr = {0};
zval *z_copy;
#ifdef HAVE_REDIS_IGBINARY
size_t sz;
uint8_t *val8;
#endif
switch(redis_sock->serializer) {
case REDIS_SERIALIZER_NONE:
@@ -1834,7 +1886,7 @@ redis_read_variant_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv
default:
/* Protocol error */
zend_throw_exception_ex(redis_exception_ce, 0 TSRMLS_CC, "protocol error, got '%c' as reply-type byte\n", reply_type);
break;
return FAILURE;
}
IF_MULTI_OR_PIPELINE() {
+2
View File
@@ -35,6 +35,8 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMET
PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
PHP_REDIS_API int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
PHP_REDIS_API int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter);
PHP_REDIS_API int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC);
PHP_REDIS_API void redis_stream_close(RedisSock *redis_sock TSRMLS_DC);
PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock TSRMLS_DC);
+87 -5
View File
@@ -21,10 +21,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
<email>michael.grunder@gmail.com</email>
<active>yes</active>
</lead>
<date>2013-09-01</date>
<date>2014-03-15</date>
<version>
<release>2.2.4</release>
<api>2.2.4</api>
<release>2.2.5</release>
<api>2.2.5</api>
</version>
<stability>
<release>stable</release>
@@ -32,7 +32,25 @@ http://pear.php.net/dtd/package-2.0.xsd">
</stability>
<license uri="http://www.php.net/license">PHP</license>
<notes>
First public release
phpredis 2.2.5
This is a minor release with several bug fixes as well as additions to support
new commands that have been introduced to Redis since our last release.
A special thanks to everyone who helps the project by commenting on issues and
submitting pull requests! :)
[NEW] Support for the BITPOS command
[NEW] Connection timeout option for RedisArray (@MikeToString)
[NEW] A _serialize method, to complement our existing _unserialize method
[NEW] Support for the PUBSUB command
[NEW] Support for SCAN, SSCAN, HSCAN, and ZSCAN
[NEW] Support for the WAIT command
[FIX] Handle the COPY and REPLACE arguments for the MIGRATE command
[DOC] Fix syntax error in documentation for the SET command (@mithunsatheesh)
[DOC] Homebrew documentation instructions (@mathias)
</notes>
<contents>
<dir name="/">
@@ -53,6 +71,13 @@ http://pear.php.net/dtd/package-2.0.xsd">
<file role='src' name='redis.c'/>
<file role='src' name='redis_session.c'/>
<file role='src' name='redis_session.h'/>
<dir name='tests'>
<file role='test' name='array-tests.php' />
<file role='test' name='memory.php' />
<file role='test' name='mkring.sh' />
<file role='test' name='test.php' />
<file role='test' name='TestRedis.php' />
</dir> <!-- tests -->
</dir> <!-- / -->
</contents>
<dependencies>
@@ -70,12 +95,69 @@ http://pear.php.net/dtd/package-2.0.xsd">
<providesextension>redis</providesextension>
<extsrcrelease/>
<changelog>
<release>
<stability><release>stable</release><api>stable</api></stability>
<version><release>2.2.5</release><api>2.2.5</api></version>
<date>2014-03-15</date>
<notes>
phpredis 2.2.5
This is a minor release with several bug fixes as well as additions to support
new commands that have been introduced to Redis since our last release.
A special thanks to everyone who helps the project by commenting on issues and
submitting pull requests! :)
[NEW] Support for the BITPOS command
[NEW] Connection timeout option for RedisArray (@MikeToString)
[NEW] A _serialize method, to complement our existing _unserialize method
[NEW] Support for the PUBSUB command
[NEW] Support for SCAN, SSCAN, HSCAN, and ZSCAN
[NEW] Support for the WAIT command
[FIX] Handle the COPY and REPLACE arguments for the MIGRATE command
[DOC] Fix syntax error in documentation for the SET command (@mithunsatheesh)
[DOC] Homebrew documentation instructions (@mathias)
</notes>
</release>
<release>
<stability><release>stable</release><api>stable</api></stability>
<version><release>2.2.4</release><api>2.2.4</api></version>
<date>2013-09-01</date>
<notes>
See GitHub for release notes
**
** Features / Improvements
**
* Randomized reconnect delay for RedisArray @mobli
This feature adds an optional parameter when constructing a RedisArray object
such that a random delay will be introduced if reconnections are made,
mitigating any &apos;thundering herd&apos; type problems.
* Lazy connections to RedisArray servers @mobli
By default, RedisArray will attempt to connect to each server you pass in
the ring on construction. This feature lets you specify that you would
rather have RedisArray only attempt a connection when it needs to get data
from a particular node (throughput/performance improvement).
* Allow LONG and STRING keys in MGET/MSET
* Extended SET options for Redis &gt;= 2.6.12
* Persistent connections and UNIX SOCKET support for RedisArray
* Allow aggregates for ZUNION/ZINTER without weights @mheijkoop
* Support for SLOWLOG command
* Reworked MGET algorithm to run in linear time regardless of key count.
* Reworked ZINTERSTORE/ZUNIONSTORE algorithm to run in linear time
**
** Bug fixes
**
* C99 Compliance (or rather lack thereof) fix @mobli
* Added ZEND_ACC_CTOR and ZEND_ACC_DTOR @euskadi31
* Stop throwing and clearing an exception on connect failure @matmoi
* Fix a false positive unit test failure having to do with TTL returns
</notes>
</release>
<release>
+13 -1
View File
@@ -14,6 +14,7 @@
+----------------------------------------------------------------------+
| Original author: Alfonso Jimenez <yo@alfonsojimenez.com> |
| Maintainer: Nicolas Favre-Felix <n.favre-felix@owlient.eu> |
| Maintainer: Michael Grunder <michael.grunder@gmail.com> |
| Maintainer: Nasreddine Bouafif <n.bouafif@owlient.eu> |
+----------------------------------------------------------------------+
*/
@@ -128,6 +129,7 @@ PHP_METHOD(Redis, slaveof);
PHP_METHOD(Redis, object);
PHP_METHOD(Redis, bitop);
PHP_METHOD(Redis, bitcount);
PHP_METHOD(Redis, bitpos);
PHP_METHOD(Redis, eval);
PHP_METHOD(Redis, evalsha);
@@ -141,6 +143,7 @@ PHP_METHOD(Redis, time);
PHP_METHOD(Redis, getLastError);
PHP_METHOD(Redis, clearLastError);
PHP_METHOD(Redis, _prefix);
PHP_METHOD(Redis, _serialize);
PHP_METHOD(Redis, _unserialize);
PHP_METHOD(Redis, mset);
@@ -181,9 +184,18 @@ PHP_METHOD(Redis, setOption);
PHP_METHOD(Redis, config);
PHP_METHOD(Redis, slowlog);
PHP_METHOD(Redis, wait);
PHP_METHOD(Redis, pubsub);
PHP_METHOD(Redis, client);
/* SCAN and friends */
PHP_METHOD(Redis, scan);
PHP_METHOD(Redis, hscan);
PHP_METHOD(Redis, sscan);
PHP_METHOD(Redis, zscan);
/* Reflection */
PHP_METHOD(Redis, getHost);
PHP_METHOD(Redis, getPort);
PHP_METHOD(Redis, getDBNum);
@@ -257,7 +269,7 @@ extern zend_module_entry redis_module_entry;
#define phpext_redis_ptr redis_module_ptr
#define PHP_REDIS_VERSION "2.2.4"
#define PHP_REDIS_VERSION "2.2.5"
#endif
+567 -42
View File
@@ -69,6 +69,25 @@ PHP_INI_BEGIN()
PHP_INI_ENTRY("redis.arrays.autorehash", "", PHP_INI_ALL, NULL)
PHP_INI_END()
/**
* Argument info for the SCAN proper
*/
ZEND_BEGIN_ARG_INFO_EX(arginfo_scan, 0, 0, 1)
ZEND_ARG_INFO(1, i_iterator)
ZEND_ARG_INFO(0, str_pattern)
ZEND_ARG_INFO(0, i_count)
ZEND_END_ARG_INFO();
/**
* Argument info for key scanning
*/
ZEND_BEGIN_ARG_INFO_EX(arginfo_kscan, 0, 0, 2)
ZEND_ARG_INFO(0, str_key)
ZEND_ARG_INFO(1, i_iterator)
ZEND_ARG_INFO(0, str_pattern)
ZEND_ARG_INFO(0, i_count)
ZEND_END_ARG_INFO();
#ifdef ZTS
ZEND_DECLARE_MODULE_GLOBALS(redis)
#endif
@@ -160,6 +179,7 @@ static zend_function_entry redis_functions[] = {
PHP_ME(Redis, object, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, bitop, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, bitcount, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, bitpos, NULL, ZEND_ACC_PUBLIC)
/* 1.1 */
PHP_ME(Redis, mset, NULL, ZEND_ACC_PUBLIC)
@@ -228,10 +248,17 @@ static zend_function_entry redis_functions[] = {
PHP_ME(Redis, clearLastError, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, _prefix, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, _serialize, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC)
/* SCAN and friends */
PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC)
PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC)
PHP_ME(Redis, zscan, arginfo_kscan, ZEND_ACC_PUBLIC)
PHP_ME(Redis, sscan, arginfo_kscan, ZEND_ACC_PUBLIC)
/* options */
PHP_ME(Redis, getOption, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, setOption, NULL, ZEND_ACC_PUBLIC)
@@ -252,6 +279,9 @@ static zend_function_entry redis_functions[] = {
PHP_ME(Redis, getAuth, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, isConnected, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, wait, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Redis, pubsub, NULL, ZEND_ACC_PUBLIC)
/* aliases */
PHP_MALIAS(Redis, open, connect, NULL, ZEND_ACC_PUBLIC)
PHP_MALIAS(Redis, popen, pconnect, NULL, ZEND_ACC_PUBLIC)
@@ -488,6 +518,11 @@ PHP_MINIT_FUNCTION(redis)
/* serializer */
add_constant_long(redis_ce, "SERIALIZER_NONE", REDIS_SERIALIZER_NONE);
add_constant_long(redis_ce, "SERIALIZER_PHP", REDIS_SERIALIZER_PHP);
/* scan options*/
add_constant_long(redis_ce, "OPT_SCAN", REDIS_OPT_SCAN);
add_constant_long(redis_ce, "SCAN_RETRY", REDIS_SCAN_RETRY);
add_constant_long(redis_ce, "SCAN_NORETRY", REDIS_SCAN_NORETRY);
#ifdef HAVE_REDIS_IGBINARY
add_constant_long(redis_ce, "SERIALIZER_IGBINARY", REDIS_SERIALIZER_IGBINARY);
#endif
@@ -790,6 +825,59 @@ PHP_METHOD(Redis, bitcount)
}
/* }}} */
/* {{{ proto integer Redis::bitpos(string key, int bit, [int start], [int end]) */
PHP_METHOD(Redis, bitpos)
{
zval *object;
RedisSock *redis_sock;
char *key, *cmd;
int key_len, cmd_len, argc, key_free=0;
long bit, start, end;
argc = ZEND_NUM_ARGS();
if(zend_parse_method_parameters(argc TSRMLS_CC, getThis(), "Osl|ll",
&object, redis_ce, &key, &key_len, &bit,
&start, &end)==FAILURE)
{
RETURN_FALSE;
}
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// We can prevalidate the first argument
if(bit != 0 && bit != 1) {
RETURN_FALSE;
}
// Prefix our key
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
// Various command semantics
if(argc == 2) {
cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sd", key, key_len,
bit);
} else if(argc == 3) {
cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sdd", key, key_len,
bit, start);
} else {
cmd_len = redis_cmd_format_static(&cmd, "BITPOS", "sddd", key, key_len,
bit, start, end);
}
// Free our key if it was prefixed
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/* }}} */
/* {{{ proto boolean Redis::close()
*/
PHP_METHOD(Redis, close)
@@ -839,7 +927,9 @@ PHP_METHOD(Redis, set) {
/* Our optional argument can either be a long (to support legacy SETEX */
/* redirection), or an array with Redis >= 2.6.12 set options */
if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY) {
if(z_opts && Z_TYPE_P(z_opts) != IS_LONG && Z_TYPE_P(z_opts) != IS_ARRAY
&& Z_TYPE_P(z_opts) != IS_NULL)
{
RETURN_FALSE;
}
@@ -903,7 +993,7 @@ PHP_METHOD(Redis, set) {
/* Free our key or value if we prefixed/serialized */
if(key_free) efree(key);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
/* Kick off the command */
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -936,7 +1026,7 @@ PHP_REDIS_API void redis_generic_setex(INTERNAL_FUNCTION_PARAMETERS, char *keywo
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "sls", key, key_len, expire, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -985,7 +1075,7 @@ PHP_METHOD(Redis, setnx)
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SETNX", "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -1023,7 +1113,7 @@ PHP_METHOD(Redis, getSet)
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "GETSET", "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -1331,12 +1421,10 @@ PHP_METHOD(Redis, incrByFloat) {
RETURN_FALSE;
}
// Prefix our key, free it if we have
// Prefix key, format command, free old key if necissary
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
if(key_free) efree(key);
// Format our INCRBYFLOAT command
cmd_len = redis_cmd_format_static(&cmd, "INCRBYFLOAT", "sf", key, key_len, val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
@@ -1742,6 +1830,11 @@ PHP_METHOD(Redis, getBit)
RETURN_FALSE;
}
// GETBIT and SETBIT only work for 0 - 2^32-1
if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "GETBIT", "sd", key, key_len, (int)offset);
if(key_free) efree(key);
@@ -1771,6 +1864,11 @@ PHP_METHOD(Redis, setBit)
RETURN_FALSE;
}
// GETBIT and SETBIT only work for 0 - 2^32-1
if(offset < BITOP_MIN_OFFSET || offset > BITOP_MAX_OFFSET) {
RETURN_FALSE;
}
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SETBIT", "sdd", key, key_len, (int)offset, (int)val);
if(key_free) efree(key);
@@ -1833,7 +1931,7 @@ generic_push_function(INTERNAL_FUNCTION_PARAMETERS, char *keyword, int keyword_l
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -1909,9 +2007,9 @@ PHP_METHOD(Redis, lInsert)
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
pivot_free = redis_serialize(redis_sock, z_pivot, &pivot, &pivot_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "LINSERT", "ssss", key, key_len, position, position_len, pivot, pivot_len, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
if(pivot_free) efree(pivot);
if(pivot_free) STR_FREE(pivot);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
@@ -2083,7 +2181,7 @@ PHP_METHOD(Redis, lRemove)
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "LREM", "sds", key, key_len, count, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -2287,7 +2385,7 @@ PHP_METHOD(Redis, sMove)
src_free = redis_key_prefix(redis_sock, &src, &src_len TSRMLS_CC);
dst_free = redis_key_prefix(redis_sock, &dst, &dst_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SMOVE", "sss", src, src_len, dst, dst_len, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(src_free) efree(src);
if(dst_free) efree(dst);
@@ -2349,14 +2447,23 @@ PHP_METHOD(Redis, sRandMember)
// Process our command
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
// Process our reply
// Either bulk or multi-bulk depending on argument count
if(ZEND_NUM_ARGS() == 2) {
IF_ATOMIC() {
// This will be bulk or multi-bulk depending if we passed the optional [COUNT] argument
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL) < 0) {
if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock, NULL, NULL) < 0)
{
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply);
} else {
IF_ATOMIC() {
redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_string_response);
}
}
/* }}} */
@@ -2384,7 +2491,7 @@ PHP_METHOD(Redis, sContains)
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "SISMEMBER", "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -2612,7 +2719,7 @@ PHP_REDIS_API int generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAMETERS, char *
/* cleanup prefixed keys. */
for(i = 0; i < real_argc + (has_timeout?-1:0); ++i) {
if(keys_to_free[i])
efree(keys[i]);
STR_FREE(keys[i]);
}
if(single_array && has_timeout) { /* cleanup string created to contain timeout value */
efree(keys[real_argc-1]);
@@ -3258,7 +3365,7 @@ PHP_METHOD(Redis, lSet) {
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "LSET", "sds", key, key_len, index, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -3678,7 +3785,7 @@ generic_mset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI
memcpy(p, _NL, 2); p += 2;
}
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
}
}
@@ -3861,7 +3968,7 @@ PHP_METHOD(Redis, zAdd) {
smart_str_appendl(&buf, val, val_len);
smart_str_appendl(&buf, _NL, sizeof(_NL) - 1);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
}
/* end string */
@@ -4258,7 +4365,7 @@ PHP_METHOD(Redis, zScore)
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "ZSCORE", "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -4291,7 +4398,7 @@ PHP_REDIS_API void generic_rank_method(INTERNAL_FUNCTION_PARAMETERS, char *keywo
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "ss", key, key_len, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -4341,7 +4448,7 @@ PHP_REDIS_API void generic_incrby_method(INTERNAL_FUNCTION_PARAMETERS, char *key
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, keyword, "sfs", key, key_len, add, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -4556,7 +4663,7 @@ generic_hset(INTERNAL_FUNCTION_PARAMETERS, char *kw, void (*fun)(INTERNAL_FUNCTI
val_free = redis_serialize(redis_sock, z_value, &val, &val_len TSRMLS_CC);
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, kw, "sss", key, key_len, member, member_len, val, val_len);
if(val_free) efree(val);
if(val_free) STR_FREE(val);
if(key_free) efree(key);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
@@ -5076,7 +5183,7 @@ PHP_METHOD(Redis, hMset)
redis_cmd_append_sstr(&set_cmds, hkey, hkey_len - 1);
redis_cmd_append_sstr(&set_cmds, hval, hval_len);
if(hval_free) efree(hval);
if(hval_free) STR_FREE(hval);
}
// Now construct the entire command
@@ -5804,22 +5911,19 @@ PHP_METHOD(Redis, getOption) {
}
switch(option) {
case REDIS_OPT_SERIALIZER:
RETURN_LONG(redis_sock->serializer);
case REDIS_OPT_PREFIX:
if(redis_sock->prefix) {
RETURN_STRINGL(redis_sock->prefix, redis_sock->prefix_len, 1);
}
RETURN_NULL();
case REDIS_OPT_READ_TIMEOUT:
RETURN_DOUBLE(redis_sock->read_timeout);
case REDIS_OPT_SCAN:
RETURN_LONG(redis_sock->scan);
default:
RETURN_FALSE;
}
}
/* }}} */
@@ -5857,7 +5961,6 @@ PHP_METHOD(Redis, setOption) {
RETURN_FALSE;
}
break;
case REDIS_OPT_PREFIX:
if(redis_sock->prefix) {
efree(redis_sock->prefix);
@@ -5871,17 +5974,22 @@ PHP_METHOD(Redis, setOption) {
memcpy(redis_sock->prefix, val_str, val_len);
}
RETURN_TRUE;
case REDIS_OPT_READ_TIMEOUT:
redis_sock->read_timeout = atof(val_str);
if(redis_sock->stream) {
read_tv.tv_sec = (time_t)redis_sock->read_timeout;
read_tv.tv_usec = (int)((redis_sock->read_timeout - read_tv.tv_sec) * 1000000);
php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT,
0, &read_tv);
php_stream_set_option(redis_sock->stream, PHP_STREAM_OPTION_READ_TIMEOUT,0, &read_tv);
}
RETURN_TRUE;
case REDIS_OPT_SCAN:
val_long = atol(val_str);
if(val_long == REDIS_SCAN_NORETRY || val_long == REDIS_SCAN_RETRY) {
redis_sock->scan = val_long;
RETURN_TRUE;
}
RETURN_FALSE;
break;
default:
RETURN_FALSE;
}
@@ -5992,6 +6100,207 @@ PHP_METHOD(Redis, slowlog) {
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
}
/* {{{ proto Redis::wait(int num_slaves, int ms) }}}
*/
PHP_METHOD(Redis, wait) {
zval *object;
RedisSock *redis_sock;
long num_slaves, timeout;
char *cmd;
int cmd_len;
// Make sure arguments are valid
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll",
&object, redis_ce, &num_slaves, &timeout)
==FAILURE)
{
RETURN_FALSE;
}
// Don't even send this to Redis if our args are negative
if(num_slaves < 0 || timeout < 0) {
RETURN_FALSE;
}
// Grab our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) {
RETURN_FALSE;
}
// Construct the command
cmd_len = redis_cmd_format_static(&cmd, "WAIT", "ll", num_slaves, timeout);
// Kick it off
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
IF_ATOMIC() {
redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
}
REDIS_PROCESS_RESPONSE(redis_long_response);
}
/*
* Construct a PUBSUB command
*/
PHP_REDIS_API int
redis_build_pubsub_cmd(RedisSock *redis_sock, char **ret, PUBSUB_TYPE type,
zval *arg TSRMLS_DC)
{
HashTable *ht_chan;
HashPosition ptr;
zval **z_ele;
char *key;
int cmd_len, key_len, key_free;
smart_str cmd = {0};
if(type == PUBSUB_CHANNELS) {
if(arg) {
// Get string argument and length.
key = Z_STRVAL_P(arg);
key_len = Z_STRLEN_P(arg);
// Prefix if necissary
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
// With a pattern
cmd_len = redis_cmd_format_static(ret, "PUBSUB", "ss", "CHANNELS", sizeof("CHANNELS")-1,
key, key_len);
// Free the channel name if we prefixed it
if(key_free) efree(key);
// Return command length
return cmd_len;
} else {
// No pattern
return redis_cmd_format_static(ret, "PUBSUB", "s", "CHANNELS", sizeof("CHANNELS")-1);
}
} else if(type == PUBSUB_NUMSUB) {
ht_chan = Z_ARRVAL_P(arg);
// Add PUBSUB and NUMSUB bits
redis_cmd_init_sstr(&cmd, zend_hash_num_elements(ht_chan)+1, "PUBSUB", sizeof("PUBSUB")-1);
redis_cmd_append_sstr(&cmd, "NUMSUB", sizeof("NUMSUB")-1);
// Iterate our elements
for(zend_hash_internal_pointer_reset_ex(ht_chan, &ptr);
zend_hash_get_current_data_ex(ht_chan, (void**)&z_ele, &ptr)==SUCCESS;
zend_hash_move_forward_ex(ht_chan, &ptr))
{
char *key;
int key_len, key_free;
zval *z_tmp = NULL;
if(Z_TYPE_PP(z_ele) == IS_STRING) {
key = Z_STRVAL_PP(z_ele);
key_len = Z_STRLEN_PP(z_ele);
} else {
MAKE_STD_ZVAL(z_tmp);
*z_tmp = **z_ele;
zval_copy_ctor(z_tmp);
convert_to_string(z_tmp);
key = Z_STRVAL_P(z_tmp);
key_len = Z_STRLEN_P(z_tmp);
}
// Apply prefix if required
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
// Append this channel
redis_cmd_append_sstr(&cmd, key, key_len);
// Free key if prefixed
if(key_free) efree(key);
// Free our temp var if we converted from something other than a string
if(z_tmp) {
zval_dtor(z_tmp);
efree(z_tmp);
z_tmp = NULL;
}
}
// Set return
*ret = cmd.c;
return cmd.len;
} else if(type == PUBSUB_NUMPAT) {
return redis_cmd_format_static(ret, "PUBSUB", "s", "NUMPAT", sizeof("NUMPAT")-1);
}
// Shouldn't ever happen
return -1;
}
/*
* {{{ proto Redis::pubsub("channels", pattern);
* proto Redis::pubsub("numsub", Array channels);
* proto Redis::pubsub("numpat"); }}}
*/
PHP_METHOD(Redis, pubsub) {
zval *object;
RedisSock *redis_sock;
char *keyword, *cmd;
int kw_len, cmd_len;
PUBSUB_TYPE type;
zval *arg=NULL;
// Parse arguments
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z",
&object, redis_ce, &keyword, &kw_len, &arg)
==FAILURE)
{
RETURN_FALSE;
}
// Validate our sub command keyword, and that we've got proper arguments
if(!strncasecmp(keyword, "channels", sizeof("channels"))) {
// One (optional) string argument
if(arg && Z_TYPE_P(arg) != IS_STRING) {
RETURN_FALSE;
}
type = PUBSUB_CHANNELS;
} else if(!strncasecmp(keyword, "numsub", sizeof("numsub"))) {
// One array argument
if(ZEND_NUM_ARGS() < 2 || Z_TYPE_P(arg) != IS_ARRAY ||
zend_hash_num_elements(Z_ARRVAL_P(arg))==0)
{
RETURN_FALSE;
}
type = PUBSUB_NUMSUB;
} else if(!strncasecmp(keyword, "numpat", sizeof("numpat"))) {
type = PUBSUB_NUMPAT;
} else {
// Invalid keyword
RETURN_FALSE;
}
// Grab our socket context object
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0)<0) {
RETURN_FALSE;
}
// Construct our "PUBSUB" command
cmd_len = redis_build_pubsub_cmd(redis_sock, &cmd, type, arg TSRMLS_CC);
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
if(type == PUBSUB_NUMSUB) {
IF_ATOMIC() {
if(redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL)<0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply_zipped);
} else {
IF_ATOMIC() {
if(redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL)<0) {
RETURN_FALSE;
}
}
REDIS_PROCESS_RESPONSE(redis_read_variant_reply);
}
}
// Construct an EVAL or EVALSHA command, with option argument array and number of arguments that are keys parameter
PHP_REDIS_API int
redis_build_eval_cmd(RedisSock *redis_sock, char **ret, char *keyword, char *value, int val_len, zval *args, int keys_count TSRMLS_DC) {
@@ -6291,18 +6600,20 @@ PHP_METHOD(Redis, restore) {
}
/*
* {{{ proto Redis::migrate(host port key dest-db timeout)
* {{{ proto Redis::migrate(host port key dest-db timeout [bool copy, bool replace])
*/
PHP_METHOD(Redis, migrate) {
zval *object;
RedisSock *redis_sock;
char *cmd, *host, *key;
int cmd_len, host_len, key_len, key_free;
zend_bool copy=0, replace=0;
long port, dest_db, timeout;
// Parse arguments
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll", &object, redis_ce,
&host, &host_len, &port, &key, &key_len, &dest_db, &timeout) == FAILURE) {
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oslsll|bb", &object, redis_ce,
&host, &host_len, &port, &key, &key_len, &dest_db, &timeout,
&copy, &replace) == FAILURE) {
RETURN_FALSE;
}
@@ -6313,7 +6624,26 @@ PHP_METHOD(Redis, migrate) {
// Prefix our key if we need to, build our command
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port, key, key_len, dest_db, timeout);
// Construct our command
if(copy && replace) {
cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsddss", host, host_len, port,
key, key_len, dest_db, timeout, "COPY",
sizeof("COPY")-1, "REPLACE", sizeof("REPLACE")-1);
} else if(copy) {
cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port,
key, key_len, dest_db, timeout, "COPY",
sizeof("COPY")-1);
} else if(replace) {
cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdds", host, host_len, port,
key, key_len, dest_db, timeout, "REPLACE",
sizeof("REPLACE")-1);
} else {
cmd_len = redis_cmd_format_static(&cmd, "MIGRATE", "sdsdd", host, host_len, port,
key, key_len, dest_db, timeout);
}
// Free our key if we prefixed it
if(key_free) efree(key);
// Kick off our MIGRATE request
@@ -6352,6 +6682,36 @@ PHP_METHOD(Redis, _prefix) {
}
}
/*
* {{{ proto Redis::_serialize(value)
*/
PHP_METHOD(Redis, _serialize) {
zval *object;
RedisSock *redis_sock;
zval *z_val;
char *val;
int val_len;
// Parse arguments
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz",
&object, redis_ce, &z_val) == FAILURE)
{
RETURN_FALSE;
}
// Grab socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Serialize, which will return a value even if no serializer is set
redis_serialize(redis_sock, z_val, &val, &val_len TSRMLS_CC);
// Return serialized value. Tell PHP to make a copy as some can be interned.
RETVAL_STRINGL(val, val_len, 1);
STR_FREE(val);
}
/*
* {{{ proto Redis::_unserialize(value)
*/
@@ -6634,4 +6994,169 @@ PHP_METHOD(Redis, client) {
}
}
/**
* Helper to format any combination of SCAN arguments
*/
PHP_REDIS_API int
redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len,
int iter, char *pattern, int pattern_len, int count)
{
char *keyword;
int arg_count, cmd_len;
// Count our arguments +1 for key if it's got one, and + 2 for pattern
// or count given that they each carry keywords with them.
arg_count = 1 + (key_len>0) + (pattern_len>0?2:0) + (count>0?2:0);
// Turn our type into a keyword
switch(type) {
case TYPE_SCAN:
keyword = "SCAN";
break;
case TYPE_SSCAN:
keyword = "SSCAN";
break;
case TYPE_HSCAN:
keyword = "HSCAN";
break;
case TYPE_ZSCAN:
default:
keyword = "ZSCAN";
break;
}
// Start the command
cmd_len = redis_cmd_format_header(cmd, keyword, arg_count);
// Add the key in question if we have one
if(key_len) {
cmd_len = redis_cmd_append_str(cmd, cmd_len, key, key_len);
}
// Add our iterator
cmd_len = redis_cmd_append_int(cmd, cmd_len, iter);
// Append COUNT if we've got it
if(count) {
cmd_len = redis_cmd_append_str(cmd, cmd_len, "COUNT", sizeof("COUNT")-1);
cmd_len = redis_cmd_append_int(cmd, cmd_len, count);
}
// Append MATCH if we've got it
if(pattern_len) {
cmd_len = redis_cmd_append_str(cmd, cmd_len, "MATCH", sizeof("MATCH")-1);
cmd_len = redis_cmd_append_str(cmd, cmd_len, pattern, pattern_len);
}
// Return our command length
return cmd_len;
}
/**
* {{{ proto redis::scan(&$iterator, [pattern, [count]])
*/
PHP_REDIS_API void
generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) {
zval *object, *z_iter;
RedisSock *redis_sock;
HashTable *hash;
char *pattern=NULL, *cmd, *key=NULL;
int cmd_len, key_len=0, pattern_len=0, num_elements, key_free=0;
long count=0, iter;
// Different prototype depending on if this is a key based scan
if(type != TYPE_SCAN) {
// Requires a key
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz/|s!l",
&object, redis_ce, &key, &key_len, &z_iter,
&pattern, &pattern_len, &count)==FAILURE)
{
RETURN_FALSE;
}
} else {
// Doesn't require a key
if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz/|s!l",
&object, redis_ce, &z_iter, &pattern, &pattern_len,
&count) == FAILURE)
{
RETURN_FALSE;
}
}
// Grab our socket
if(redis_sock_get(object, &redis_sock TSRMLS_CC, 0) < 0) {
RETURN_FALSE;
}
// Calling this in a pipeline makes no sense
IF_NOT_ATOMIC() {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Can't call SCAN commands in multi or pipeline mode!");
RETURN_FALSE;
}
// The iterator should be passed in as NULL for the first iteration, but we can treat
// any NON LONG value as NULL for these purposes as we've seperated the variable anyway.
if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter)<0) {
// Convert to long
convert_to_long(z_iter);
iter = 0;
} else if(Z_LVAL_P(z_iter)!=0) {
// Update our iterator value for the next passthru
iter = Z_LVAL_P(z_iter);
} else {
// We're done, back to iterator zero
RETURN_FALSE;
}
// Prefix our key if we've got one and we have a prefix set
if(key_len) {
key_free = redis_key_prefix(redis_sock, &key, &key_len TSRMLS_CC);
}
/**
* Redis can return to us empty keys, especially in the case where there are a large
* number of keys to scan, and we're matching against a pattern. PHPRedis can be set
* up to abstract this from the user, by setting OPT_SCAN to REDIS_SCAN_RETRY. Otherwise
* we will return empty keys and the user will need to make subsequent calls with
* an updated iterator.
*/
do {
// Format our SCAN command
cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (int)iter,
pattern, pattern_len, count);
// Execute our command getting our new iterator value
REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len);
if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,
redis_sock,type,&iter)<0)
{
if(key_free) efree(key);
RETURN_FALSE;
}
// Get the number of elements
hash = Z_ARRVAL_P(return_value);
num_elements = zend_hash_num_elements(hash);
} while(redis_sock->scan == REDIS_SCAN_RETRY && iter != 0 && num_elements == 0);
// Free our key if it was prefixed
if(key_free) efree(key);
// Update our iterator reference
Z_LVAL_P(z_iter) = iter;
}
PHP_METHOD(Redis, scan) {
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SCAN);
}
PHP_METHOD(Redis, hscan) {
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_HSCAN);
}
PHP_METHOD(Redis, sscan) {
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_SSCAN);
}
PHP_METHOD(Redis, zscan) {
generic_scan_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, TYPE_ZSCAN);
}
/* vim: set tabstop=4 softtabstops=4 noexpandtab shiftwidth=4: */
+48 -24
View File
@@ -32,6 +32,12 @@
#include "redis_array.h"
#include "redis_array_impl.h"
/* Simple macro to detect failure in a RedisArray call */
#define RA_CALL_FAILED(rv, cmd) \
((Z_TYPE_P(rv) == IS_BOOL && Z_BVAL_P(rv) == 0) || \
(Z_TYPE_P(rv) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(rv)) == 0) || \
(Z_TYPE_P(rv) == IS_LONG && Z_LVAL_P(rv) == 0 && !strcasecmp(cmd, "TYPE"))) \
extern zend_class_entry *redis_ce;
zend_class_entry *redis_array_ce;
@@ -76,43 +82,50 @@ zend_function_entry redis_array_functions[] = {
{NULL, NULL, NULL}
};
int le_redis_array;
void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC)
{
static void redis_array_free(RedisArray *ra) {
int i;
RedisArray *ra = (RedisArray*)rsrc->ptr;
/* delete Redis objects */
for(i = 0; i < ra->count; ++i) {
// Redis objects
for(i=0;i<ra->count;i++) {
zval_dtor(ra->redis[i]);
efree(ra->redis[i]);
/* remove host too */
efree(ra->hosts[i]);
}
efree(ra->redis);
efree(ra->hosts);
/* delete function */
/* delete hash function */
if(ra->z_fun) {
zval_dtor(ra->z_fun);
efree(ra->z_fun);
}
/* delete distributor */
/* Distributor */
if(ra->z_dist) {
zval_dtor(ra->z_dist);
efree(ra->z_dist);
}
/* delete list of pure commands */
/* Delete pur commands */
zval_dtor(ra->z_pure_cmds);
efree(ra->z_pure_cmds);
/* free container */
// Free structure itself
efree(ra);
}
int le_redis_array;
void redis_destructor_redis_array(zend_rsrc_list_entry * rsrc TSRMLS_DC)
{
RedisArray *ra = (RedisArray*)rsrc->ptr;
/* Free previous ring if it's set */
if(ra->prev) redis_array_free(ra->prev);
/* Free parent array */
redis_array_free(ra);
}
/**
* redis_array_get
*/
@@ -199,6 +212,8 @@ PHP_METHOD(RedisArray, __construct)
long l_retry_interval = 0;
zend_bool b_lazy_connect = 0;
zval **z_retry_interval_pp;
double d_connect_timeout = 0;
zval **z_connect_timeout_pp;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) {
RETURN_FALSE;
@@ -261,6 +276,18 @@ PHP_METHOD(RedisArray, __construct)
if(FAILURE != zend_hash_find(hOpts, "lazy_connect", sizeof("lazy_connect"), (void**)&zpData) && Z_TYPE_PP(zpData) == IS_BOOL) {
b_lazy_connect = Z_BVAL_PP(zpData);
}
/* extract connect_timeout option */
if (FAILURE != zend_hash_find(hOpts, "connect_timeout", sizeof("connect_timeout"), (void**)&z_connect_timeout_pp)) {
if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE || Z_TYPE_PP(z_connect_timeout_pp) == IS_STRING) {
if (Z_TYPE_PP(z_connect_timeout_pp) == IS_DOUBLE) {
d_connect_timeout = Z_DVAL_PP(z_connect_timeout_pp);
}
else {
d_connect_timeout = atof(Z_STRVAL_PP(z_connect_timeout_pp));
}
}
}
}
/* extract either name of list of hosts from z0 */
@@ -270,7 +297,7 @@ PHP_METHOD(RedisArray, __construct)
break;
case IS_ARRAY:
ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
ra = ra_make_array(Z_ARRVAL_P(z0), z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC);
break;
default:
@@ -280,6 +307,8 @@ PHP_METHOD(RedisArray, __construct)
if(ra) {
ra->auto_rehash = b_autorehash;
ra->connect_timeout = d_connect_timeout;
if(ra->prev) ra->prev->auto_rehash = b_autorehash;
#if PHP_VERSION_ID >= 50400
id = zend_list_insert(ra, le_redis_array TSRMLS_CC);
#else
@@ -366,22 +395,14 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, i
} else { /* call directly through. */
call_user_function(&redis_ce->function_table, &redis_inst, &z_fun, return_value, argc, z_callargs TSRMLS_CC);
failed = 0;
if((Z_TYPE_P(return_value) == IS_BOOL && Z_BVAL_P(return_value) == 0) ||
(Z_TYPE_P(return_value) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(return_value)) == 0) ||
(Z_TYPE_P(return_value) == IS_LONG && Z_LVAL_P(return_value) == 0 && !strcasecmp(cmd, "TYPE")))
{
failed = 1;
}
/* check if we have an error. */
if(failed && ra->prev && !b_write_cmd) { /* there was an error reading, try with prev ring. */
if(RA_CALL_FAILED(return_value,cmd) && ra->prev && !b_write_cmd) { /* there was an error reading, try with prev ring. */
/* ERROR, FALLBACK TO PREVIOUS RING and forward a reference to the first redis instance we were looking at. */
ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra->prev, cmd, cmd_len, z_args, z_new_target?z_new_target:redis_inst);
}
if(!failed && !b_write_cmd && z_new_target && ra->auto_rehash) { /* move key from old ring to new ring */
/* Autorehash if the key was found on the previous node if this is a read command and auto rehashing is on */
if(!RA_CALL_FAILED(return_value,cmd) && !b_write_cmd && z_new_target && ra->auto_rehash) { /* move key from old ring to new ring */
ra_move_key(key, key_len, redis_inst, z_new_target TSRMLS_CC);
}
}
@@ -876,6 +897,9 @@ PHP_METHOD(RedisArray, mget)
/* calls */
for(n = 0; n < ra->count; ++n) { /* for each node */
/* We don't even need to make a call to this node if no keys go there */
if(!argc_each[n]) continue;
/* copy args for MGET call on node. */
MAKE_STD_ZVAL(z_argarray);
array_init(z_argarray);
+1
View File
@@ -50,6 +50,7 @@ typedef struct RedisArray_ {
zval *z_fun; /* key extractor, callable */
zval *z_dist; /* key distributor, callable */
zval *z_pure_cmds; /* hash table */
double connect_timeout; /* socket connect timeout */
struct RedisArray_ *prev;
} RedisArray;
+34 -6
View File
@@ -44,7 +44,9 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b
/* init connections */
for(i = 0; i < count; ++i) {
if(FAILURE == zend_hash_quick_find(hosts, NULL, 0, i, (void**)&zpData)) {
if(FAILURE == zend_hash_quick_find(hosts, NULL, 0, i, (void**)&zpData) ||
Z_TYPE_PP(zpData) != IS_STRING)
{
efree(ra);
return NULL;
}
@@ -70,7 +72,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b
call_user_function(&redis_ce->function_table, &ra->redis[i], &z_cons, &z_ret, 0, NULL TSRMLS_CC);
/* create socket */
redis_sock = redis_sock_create(host, host_len, port, 0, ra->pconnect, NULL, retry_interval, b_lazy_connect);
redis_sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect);
if (!b_lazy_connect)
{
@@ -166,12 +168,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
zval *z_params_autorehash;
zval *z_params_retry_interval;
zval *z_params_pconnect;
zval *z_params_connect_timeout;
zval *z_params_lazy_connect;
RedisArray *ra = NULL;
zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0;
long l_retry_interval = 0;
zend_bool b_lazy_connect = 0;
double d_connect_timeout = 0;
HashTable *hHosts = NULL, *hPrev = NULL;
/* find entry */
@@ -258,7 +262,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
b_pconnect = 1;
}
}
/* find retry interval option */
/* find lazy connect option */
MAKE_STD_ZVAL(z_params_lazy_connect);
array_init(z_params_lazy_connect);
sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.lazyconnect")), z_params_lazy_connect TSRMLS_CC);
@@ -268,9 +273,25 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
}
}
/* find connect timeout option */
MAKE_STD_ZVAL(z_params_connect_timeout);
array_init(z_params_connect_timeout);
sapi_module.treat_data(PARSE_STRING, estrdup(INI_STR("redis.arrays.connecttimeout")), z_params_connect_timeout TSRMLS_CC);
if (zend_hash_find(Z_ARRVAL_P(z_params_connect_timeout), name, strlen(name) + 1, (void **) &z_data_pp) != FAILURE) {
if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE || Z_TYPE_PP(z_data_pp) == IS_STRING) {
if (Z_TYPE_PP(z_data_pp) == IS_DOUBLE) {
d_connect_timeout = Z_DVAL_PP(z_data_pp);
}
else {
d_connect_timeout = atof(Z_STRVAL_PP(z_data_pp));
}
}
}
/* create RedisArray object */
ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect TSRMLS_CC);
ra = ra_make_array(hHosts, z_fun, z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC);
ra->auto_rehash = b_autorehash;
if(ra->prev) ra->prev->auto_rehash = b_autorehash;
/* cleanup */
zval_dtor(z_params_hosts);
@@ -287,6 +308,8 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
efree(z_params_retry_interval);
zval_dtor(z_params_pconnect);
efree(z_params_pconnect);
zval_dtor(z_params_connect_timeout);
efree(z_params_connect_timeout);
zval_dtor(z_params_lazy_connect);
efree(z_params_lazy_connect);
@@ -294,7 +317,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) {
}
RedisArray *
ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC) {
ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC) {
int count = zend_hash_num_elements(hosts);
@@ -308,6 +331,8 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
ra->z_multi_exec = NULL;
ra->index = b_index;
ra->auto_rehash = 0;
ra->pconnect = b_pconnect;
ra->connect_timeout = connect_timeout;
/* init array data structures */
ra_init_function_table(ra);
@@ -315,7 +340,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev
if(NULL == ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC)) {
return NULL;
}
ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect TSRMLS_CC) : NULL;
ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout TSRMLS_CC) : NULL;
/* copy function if provided */
if(z_fun) {
@@ -603,6 +628,7 @@ ra_index_key(const char *key, int key_len, zval *z_redis TSRMLS_DC) {
/* don't dtor z_ret, since we're returning z_redis */
efree(z_args[0]);
zval_dtor(z_args[1]);
efree(z_args[1]);
}
@@ -965,6 +991,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl
ZVAL_STRINGL(z_args[0], key, key_len, 0);
ZVAL_LONG(z_args[1], ttl);
ZVAL_STRINGL(z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */
zval_dtor(&z_ret); /* free memory from our previous call */
call_user_function(&redis_ce->function_table, &z_to, &z_fun_set, &z_ret, 3, z_args TSRMLS_CC);
/* cleanup */
efree(z_args[1]);
@@ -975,6 +1002,7 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl
ZVAL_STRINGL(&z_fun_set, "SET", 3, 0);
ZVAL_STRINGL(z_args[0], key, key_len, 0);
ZVAL_STRINGL(z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret), 1); /* copy z_ret to arg 1 */
zval_dtor(&z_ret); /* free memory from our previous return value */
call_user_function(&redis_ce->function_table, &z_to, &z_fun_set, &z_ret, 2, z_args TSRMLS_CC);
/* cleanup */
zval_dtor(z_args[1]);
+1 -1
View File
@@ -12,7 +12,7 @@
RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC);
RedisArray *ra_load_array(const char *name TSRMLS_DC);
RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC);
RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC);
zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC);
zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC);
void ra_init_function_table(RedisArray *ra);
+1 -1
View File
@@ -3,7 +3,7 @@
%global php_version %(php-config --version 2>/dev/null || echo 0)
Name: php-redis
Version: 2.2.4
Version: 2.2.5
Release: 1%{?dist}
Summary: The phpredis extension provides an API for communicating with the Redis key-value store.
+344
View File
@@ -72,6 +72,47 @@ class Redis_Test extends TestSuite
$this->assertTrue(is_array($ret) && count($ret) === 1 && $ret[0] >= 0);
}
// Run some simple tests against the PUBSUB command. This is problematic, as we
// can't be sure what's going on in the instance, but we can do some things.
public function testPubSub() {
// Only available since 2.8.0
if(version_compare($this->version, "2.8.0", "lt")) {
$this->markTestSkipped();
return;
}
// PUBSUB CHANNELS ...
$result = $this->redis->pubsub("channels", "*");
$this->assertTrue(is_array($result));
$result = $this->redis->pubsub("channels");
$this->assertTrue(is_array($result));
// PUBSUB NUMSUB
$c1 = uniqid() . '-' . rand(1,100);
$c2 = uniqid() . '-' . rand(1,100);
$result = $this->redis->pubsub("numsub", Array($c1, $c2));
// Should get an array back, with two elements
$this->assertTrue(is_array($result));
$this->assertEquals(count($result), 2);
// Make sure the elements are correct, and have zero counts
foreach(Array($c1,$c2) as $channel) {
$this->assertTrue(isset($result[$channel]));
$this->assertEquals($result[$channel], "0");
}
// PUBSUB NUMPAT
$result = $this->redis->pubsub("numpat");
$this->assertTrue(is_int($result));
// Invalid calls
$this->assertFalse($this->redis->pubsub("notacommand"));
$this->assertFalse($this->redis->pubsub("numsub", "not-an-array"));
}
public function testBitsets() {
$this->redis->delete('key');
@@ -110,6 +151,31 @@ class Redis_Test extends TestSuite
// values above 1 are changed to 1 but don't overflow on bits to the right.
$this->assertTrue(0 === $this->redis->setBit('key', 0, 0xff));
$this->assertTrue("\x9f" === $this->redis->get('key'));
// Verify valid offset ranges
$this->assertFalse($this->redis->getBit('key', -1));
$this->assertFalse($this->redis->getBit('key', 4294967296));
$this->assertFalse($this->redis->setBit('key', -1, 1));
$this->assertFalse($this->redis->setBit('key', 4294967296, 1));
}
public function testBitPos() {
if(version_compare($this->version, "2.8.7", "lt")) {
$this->MarkTestSkipped();
return;
}
$this->redis->del('bpkey');
$this->redis->set('bpkey', "\xff\xf0\x00");
$this->assertEquals($this->redis->bitpos('bpkey', 0), 12);
$this->redis->set('bpkey', "\x00\xff\xf0");
$this->assertEquals($this->redis->bitpos('bpkey', 1, 0), 8);
$this->assertEquals($this->redis->bitpos('bpkey', 1, 1), 8);
$this->redis->set('bpkey', "\x00\x00\x00");
$this->assertEquals($this->redis->bitpos('bpkey', 1), -1);
}
public function test1000() {
@@ -258,6 +324,12 @@ class Redis_Test extends TestSuite
$this->assertTrue($this->redis->set('foo','barbaz', Array('not-valid','nx','invalid','ex'=>200)));
$this->assertEquals($this->redis->ttl('foo'), 200);
$this->assertEquals($this->redis->get('foo'), 'barbaz');
/* Pass NULL as the optional arguments which should be ignored */
$this->redis->del('foo');
$this->redis->set('foo','bar', NULL);
$this->assertEquals($this->redis->get('foo'), 'bar');
$this->assertTrue($this->redis->ttl('foo')<0);
}
public function testGetSet() {
@@ -475,6 +547,16 @@ class Redis_Test extends TestSuite
$this->redis->incrbyfloat('key', -1.5);
$this->assertTrue("abc" === $this->redis->get('key'));
// Test with prefixing
$this->redis->setOption(Redis::OPT_PREFIX, 'someprefix:');
$this->redis->del('key');
$this->redis->incrbyfloat('key',1.8);
$this->assertEquals('1.8', $this->redis->get('key'));
$this->redis->setOption(Redis::OPT_PREFIX, '');
$this->assertTrue($this->redis->exists('someprefix:key'));
$this->redis->del('someprefix:key');
}
public function testDecr()
@@ -1144,6 +1226,28 @@ class Redis_Test extends TestSuite
break;
}
}
//
// With and without count, while serializing
//
$this->redis->delete('set0');
$this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
for($i=0;$i<5;$i++) {
$member = "member:$i";
$this->redis->sAdd('set0', $member);
$mems[] = $member;
}
$member = $this->redis->srandmember('set0');
$this->assertTrue(in_array($member, $mems));
$rmembers = $this->redis->srandmember('set0', $i);
foreach($rmembers as $reply_mem) {
$this->assertTrue(in_array($reply_mem, $mems));
}
$this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
}
public function testSRandMemberWithCount() {
@@ -1759,6 +1863,34 @@ class Redis_Test extends TestSuite
$this->assertFalse($this->redis->slowlog('notvalid'));
}
public function testWait() {
// Closest we can check based on redis commmit history
if(version_compare($this->version, '2.9.11', 'lt')) {
$this->markTestSkipped();
return;
}
// We could have slaves here, so determine that
$arr_slaves = $this->redis->info();
$i_slaves = $arr_slaves['connected_slaves'];
// Send a couple commands
$this->redis->set('wait-foo', 'over9000');
$this->redis->set('wait-bar', 'revo9000');
// Make sure we get the right replication count
$this->assertEquals($this->redis->wait($i_slaves, 100), $i_slaves);
// Pass more slaves than are connected
$this->redis->set('wait-foo','over9000');
$this->redis->set('wait-bar','revo9000');
$this->assertTrue($this->redis->wait($i_slaves+1, 100) < $i_slaves+1);
// Make sure when we pass with bad arguments we just get back false
$this->assertFalse($this->redis->wait(-1, -1));
$this->assertFalse($this->redis->wait(-1, 20));
}
public function testinfo() {
$info = $this->redis->info();
@@ -4356,6 +4488,34 @@ class Redis_Test extends TestSuite
$this->assertTrue(1 === $this->redis->evalsha($sha));
}
public function testSerialize() {
$vals = Array(1, 1.5, 'one', Array('here','is','an','array'));
// Test with no serialization at all
$this->assertTrue($this->redis->_serialize('test') === 'test');
$this->assertTrue($this->redis->_serialize(1) === '1');
$this->assertTrue($this->redis->_serialize(Array()) === 'Array');
$this->assertTrue($this->redis->_serialize(new stdClass) === 'Object');
$arr_serializers = Array(Redis::SERIALIZER_PHP);
if(defined('Redis::SERIALIZER_IGBINARY')) {
$arr_serializers[] = Redis::SERIALIZER_IGBINARY;
}
foreach($arr_serializers as $mode) {
$arr_enc = Array();
$arr_dec = Array();
foreach($vals as $k => $v) {
$enc = $this->redis->_serialize($v);
$dec = $this->redis->_unserialize($enc);
// They should be the same
$this->assertTrue($enc == $dec);
}
}
}
public function testUnserialize() {
$vals = Array(
1,1.5,'one',Array('this','is','an','array')
@@ -4462,6 +4622,190 @@ class Redis_Test extends TestSuite
$this->assertTrue($this->redis->getAuth() === self::AUTH);
}
/**
* Scan and variants
*/
protected function get_keyspace_count($str_db) {
$arr_info = $this->redis->info();
$arr_info = $arr_info[$str_db];
$arr_info = explode(',', $arr_info);
$arr_info = explode('=', $arr_info[0]);
return $arr_info[1];
}
public function testScan() {
if(version_compare($this->version, "2.8.0", "lt")) {
$this->markTestSkipped();
return;
}
// Key count
$i_key_count = $this->get_keyspace_count('db0');
// Have scan retry
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
// Scan them all
$it = NULL;
while($arr_keys = $this->redis->scan($it)) {
$i_key_count -= count($arr_keys);
}
// Should have iterated all keys
$this->assertEquals(0, $i_key_count);
// Unique keys, for pattern matching
$str_uniq = uniqid() . '-' . uniqid();
for($i=0;$i<10;$i++) {
$this->redis->set($str_uniq . "::$i", "bar::$i");
}
// Scan just these keys using a pattern match
$it = NULL;
while($arr_keys = $this->redis->scan($it, "*$str_uniq*")) {
$i -= count($arr_keys);
}
$this->assertEquals(0, $i);
}
public function testHScan() {
if(version_compare($this->version, "2.8.0", "lt")) {
$this->markTestSkipped();
return;
}
// Never get empty sets
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
$this->redis->del('hash');
$i_foo_mems = 0;
for($i=0;$i<100;$i++) {
if($i>3) {
$this->redis->hset('hash', "member:$i", "value:$i");
} else {
$this->redis->hset('hash', "foomember:$i", "value:$i");
$i_foo_mems++;
}
}
// Scan all of them
$it = NULL;
while($arr_keys = $this->redis->hscan('hash', $it)) {
$i -= count($arr_keys);
}
$this->assertEquals(0, $i);
// Scan just *foomem* (should be 4)
$it = NULL;
while($arr_keys = $this->redis->hscan('hash', $it, '*foomember*')) {
$i_foo_mems -= count($arr_keys);
foreach($arr_keys as $str_mem => $str_val) {
$this->assertTrue(strpos($str_mem, 'member')!==FALSE);
$this->assertTrue(strpos($str_val, 'value')!==FALSE);
}
}
$this->assertEquals(0, $i_foo_mems);
}
public function testSScan() {
if(version_compare($this->version, "2.8.0", "lt")) {
$this->markTestSkipped();
return;
}
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
$this->redis->del('set');
for($i=0;$i<100;$i++) {
$this->redis->sadd('set', "member:$i");
}
// Scan all of them
$it = NULL;
while($arr_keys = $this->redis->sscan('set', $it)) {
$i -= count($arr_keys);
foreach($arr_keys as $str_mem) {
$this->assertTrue(strpos($str_mem,'member')!==FALSE);
}
}
$this->assertEquals(0, $i);
// Scan just ones with zero in them (0, 10, 20, 30, 40, 50, 60, 70, 80, 90)
$it = NULL;
$i_w_zero = 0;
while($arr_keys = $this->redis->sscan('set', $it, '*0*')) {
$i_w_zero += count($arr_keys);
}
$this->assertEquals(10, $i_w_zero);
}
public function testZScan() {
if(version_compare($this->version, "2.8.0", "lt")) {
$this->markTestSkipped();
return;
}
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
$this->redis->del('zset');
$i_tot_score = 0;
$i_p_score = 0;
$i_p_count = 0;
for($i=0;$i<2000;$i++) {
if($i<10) {
$this->redis->zadd('zset', $i, "pmem:$i");
$i_p_score += $i;
$i_p_count += 1;
} else {
$this->redis->zadd('zset', $i, "mem:$i");
}
$i_tot_score += $i;
}
// Scan them all
$it = NULL;
while($arr_keys = $this->redis->zscan('zset', $it)) {
foreach($arr_keys as $str_mem => $f_score) {
$i_tot_score -= $f_score;
$i--;
}
}
$this->assertEquals(0, $i);
$this->assertEquals(0, $i_tot_score);
// Just scan "pmem" members
$it = NULL;
$i_p_score_old = $i_p_score;
$i_p_count_old = $i_p_count;
while($arr_keys = $this->redis->zscan('zset', $it, "*pmem*")) {
foreach($arr_keys as $str_mem => $f_score) {
$i_p_score -= $f_score;
$i_p_count -= 1;
}
}
$this->assertEquals(0, $i_p_score);
$this->assertEquals(0, $i_p_count);
// Turn off retrying and we should get some empty results
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
$i_skips = 0;
$i_p_score = $i_p_score_old;
$i_p_count = $i_p_count_old;
$it = NULL;
while(($arr_keys = $this->redis->zscan('zset', $it, "*pmem*")) !== FALSE) {
if(count($arr_keys) == 0) $i_skips++;
foreach($arr_keys as $str_mem => $f_score) {
$i_p_score -= $f_score;
$i_p_count -= 1;
}
}
// We should still get all the keys, just with several empty results
$this->assertTrue($i_skips > 0);
$this->assertEquals(0, $i_p_score);
$this->assertEquals(0, $i_p_count);
}
}
exit(TestSuite::run("Redis_Test"));