Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 53560e0

Browse files
Merge branch 'feature/scan_commands' into develop
2 parents 77afbe3 + 84f3fb5 commit 53560e0

File tree

7 files changed

+609
-47
lines changed

7 files changed

+609
-47
lines changed

README.markdown

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,15 @@ $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // use built-in
268268
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // use igBinary serialize/unserialize
269269
270270
$redis->setOption(Redis::OPT_PREFIX, 'myAppName:'); // use custom prefix on all keys
271+
272+
/* Options for the SCAN family of commands, indicating whether to abstract
273+
empty results from the user. If set to SCAN_NORETRY (the default), phpredis
274+
will just issue one SCAN command at a time, sometimes returning an empty
275+
array of results. If set to SCAN_RETRY, phpredis will retry the scan command
276+
until keys come back OR Redis returns an iterator of zero
277+
*/
278+
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
279+
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
271280
~~~
272281

273282

@@ -607,6 +616,7 @@ $redis->slowlog('len');
607616
* [expire, setTimeout, pexpire](#expire-settimeout-pexpire) - Set a key's time to live in seconds
608617
* [expireAt, pexpireAt](#expireat-pexpireat) - Set the expiration for a key as a UNIX timestamp
609618
* [keys, getKeys](#keys-getkeys) - Find all keys matching the given pattern
619+
* [scan](#scan) - Scan for keys in the keyspace (Redis >= 2.8.0)
610620
* [migrate](#migrate) - Atomically transfer a key from a Redis instance to another one
611621
* [move](#move) - Move a key to another database
612622
* [object](#object) - Inspect the internals of Redis objects
@@ -953,7 +963,29 @@ $allKeys = $redis->keys('*'); // all keys will match this.
953963
$keyWithUserPrefix = $redis->keys('user*');
954964
~~~
955965

966+
### scan
967+
-----
968+
_**Description**_: Scan the keyspace for keys
969+
970+
##### *Parameters*
971+
*LONG (reference)*: Iterator, initialized to NULL
972+
*STRING, Optional*: Pattern to match
973+
*LONG, Optional)*: Count of keys per iteration (only a suggestion to Redis)
974+
975+
##### *Return value*
976+
*Array, boolean*: This function will return an array of keys or FALSE if there are no more keys
956977

978+
##### *Example*
979+
~~~
980+
$it = NULL; /* Initialize our iterator to NULL */
981+
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* retry when we get no keys back */
982+
while($arr_keys = $redis->scan($it)) {
983+
foreach($arr_keys as $str_key) {
984+
echo "Here is a key: $str_key\n";
985+
}
986+
echo "No more keys to scan!\n";
987+
}
988+
~~~
957989

958990
### object
959991
-----
@@ -1283,6 +1315,7 @@ $redis->migrate('backup', 6379, 'foo', 0, 3600);
12831315
* [hSet](#hset) - Set the string value of a hash field
12841316
* [hSetNx](#hsetnx) - Set the value of a hash field, only if the field does not exist
12851317
* [hVals](#hvals) - Get all the values in a hash
1318+
* [hScan](#hscan) - Scan a hash key for members
12861319

12871320
### hSet
12881321
-----
@@ -1542,7 +1575,28 @@ $redis->hSet('h', 'field2', 'value2');
15421575
$redis->hmGet('h', array('field1', 'field2')); /* returns array('field1' => 'value1', 'field2' => 'value2') */
15431576
~~~
15441577

1578+
### hScan
1579+
-----
1580+
_**Description**_: Scan a HASH value for members, with an optional pattern and count
1581+
##### *Parameters*
1582+
*key*: String
1583+
*iterator*: Long (reference)
1584+
*pattern*: Optional pattern to match against
1585+
*count*: How many keys to return in a go (only a sugestion to Redis)
1586+
##### *Return value*
1587+
*Array* An array of members that match our pattern
15451588

1589+
##### *Examples*
1590+
~~~
1591+
$it = NULL;
1592+
/* Don't ever return an empty array until we're done iterating */
1593+
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
1594+
while($arr_keys = $redis->hscan('hash', $it)) {
1595+
foreach($arr_keys as $str_field => $str_value) {
1596+
echo "$str_field => $str_value\n"; /* Print the hash member and value */
1597+
}
1598+
}
1599+
~~~
15461600

15471601
## Lists
15481602

@@ -1981,6 +2035,7 @@ $redis->lSize('key1');/* 2 */
19812035
* [sRem, sRemove](#srem-sremove) - Remove one or more members from a set
19822036
* [sUnion](#sunion) - Add multiple sets
19832037
* [sUnionStore](#sunionstore) - Add multiple sets and store the resulting set in a key
2038+
* [sScan](#sscan) - Scan a set for members
19842039

19852040
### sAdd
19862041
-----
@@ -2380,6 +2435,41 @@ array(4) {
23802435
}
23812436
~~~
23822437

2438+
### sScan
2439+
-----
2440+
_**Description**_: Scan a set for members
2441+
2442+
##### *Parameters*
2443+
*Key*: The set to search
2444+
*iterator*: LONG (reference) to the iterator as we go
2445+
*pattern*: String, optional pattern to match against
2446+
*count*: How many members to return at a time (Redis might return a different amount)
2447+
2448+
##### *Retur value*
2449+
*Array, boolean*: PHPRedis will return an array of keys or FALSE when we're done iterating
2450+
2451+
##### *Example*
2452+
~~~
2453+
$it = NULL;
2454+
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* don't return empty results until we're done */
2455+
while($arr_mems = $redis->sscan('set', $it, "*pattern*")) {
2456+
foreach($arr_mems as $str_mem) {
2457+
echo "Member: $str_mem\n";
2458+
}
2459+
}
2460+
2461+
$it = NULL;
2462+
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); /* return after each iteration, even if empty */
2463+
while(($arr_mems = $redis->sscan('set', $it, "*pattern*"))!==FALSE) {
2464+
if(count($arr_mems) > 0) {
2465+
foreach($arr_mems as $str_mem) {
2466+
echo "Member found: $str_mem\n";
2467+
}
2468+
} else {
2469+
echo "No members in this iteration, iterator value: $it\n");
2470+
}
2471+
}
2472+
~~~
23832473

23842474
## Sorted sets
23852475

@@ -2397,6 +2487,7 @@ array(4) {
23972487
* [zRevRange](#zrevrange) - Return a range of members in a sorted set, by index, with scores ordered from high to low
23982488
* [zScore](#zscore) - Get the score associated with the given member in a sorted set
23992489
* [zUnion](#zunion) - Add multiple sorted sets and store the resulting sorted set in a new key
2490+
* [zScan](#zscan) - Scan a sorted set for members
24002491

24012492
### zAdd
24022493
-----
@@ -2736,6 +2827,30 @@ $redis->zUnion('ko2', array('k1', 'k2'), array(1, 1)); /* 4, 'ko2' => array('val
27362827
$redis->zUnion('ko3', array('k1', 'k2'), array(5, 1)); /* 4, 'ko3' => array('val0', 'val2', 'val3', 'val1') */
27372828
~~~
27382829

2830+
### zScan
2831+
-----
2832+
_**Description**_: Scan a sorted set for members, with optional pattern and count
2833+
2834+
##### *Parameters*
2835+
*key*: String, the set to scan
2836+
*iterator*: Long (reference), initialized to NULL
2837+
*pattern*: String (optional), the pattern to match
2838+
*count*: How many keys to return per iteration (Redis might return a different number)
2839+
2840+
##### *Return value*
2841+
*Array, boolean* PHPReids will return matching keys from Redis, or FALSE when iteration is complete
2842+
2843+
##### *Example*
2844+
~~~
2845+
$it = NULL;
2846+
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
2847+
while($arr_matches = $redis->zscan('zset', $it, '*pattern*')) {
2848+
foreach($arr_matches as $str_mem => $f_score) {
2849+
echo "Key: $str_mem, Score: $f_score\n";
2850+
}
2851+
}
2852+
~~~
2853+
27392854
## Pub/sub
27402855

27412856
* [psubscribe](#psubscribe) - Subscribe to channels by pattern

common.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,30 @@ typedef enum _REDIS_REPLY_TYPE {
3737
TYPE_MULTIBULK = '*'
3838
} REDIS_REPLY_TYPE;
3939

40+
/* SCAN variants */
41+
typedef enum _REDIS_SCAN_TYPE {
42+
TYPE_SCAN,
43+
TYPE_SSCAN,
44+
TYPE_HSCAN,
45+
TYPE_ZSCAN
46+
} REDIS_SCAN_TYPE;
47+
4048
/* options */
4149
#define REDIS_OPT_SERIALIZER 1
4250
#define REDIS_OPT_PREFIX 2
4351
#define REDIS_OPT_READ_TIMEOUT 3
52+
#define REDIS_OPT_SCAN 4
4453

4554
/* serializers */
4655
#define REDIS_SERIALIZER_NONE 0
4756
#define REDIS_SERIALIZER_PHP 1
4857
#define REDIS_SERIALIZER_IGBINARY 2
4958

59+
/* SCAN options */
60+
61+
#define REDIS_SCAN_NORETRY 0
62+
#define REDIS_SCAN_RETRY 1
63+
5064
/* GETBIT/SETBIT offset range limits */
5165
#define BITOP_MIN_OFFSET 0
5266
#define BITOP_MAX_OFFSET 4294967295
@@ -57,6 +71,7 @@ typedef enum _REDIS_REPLY_TYPE {
5771
#define IF_MULTI_OR_PIPELINE() if(redis_sock->mode == MULTI || redis_sock->mode == PIPELINE)
5872
#define IF_PIPELINE() if(redis_sock->mode == PIPELINE)
5973
#define IF_NOT_MULTI() if(redis_sock->mode != MULTI)
74+
#define IF_NOT_ATOMIC() if(redis_sock->mode != ATOMIC)
6075
#define IF_ATOMIC() if(redis_sock->mode == ATOMIC)
6176
#define ELSE_IF_MULTI() else if(redis_sock->mode == MULTI) { \
6277
if(redis_response_enqueued(redis_sock TSRMLS_CC) == 1) {\
@@ -201,6 +216,8 @@ typedef struct {
201216
char *err;
202217
int err_len;
203218
zend_bool lazy_connect;
219+
220+
int scan;
204221
} RedisSock;
205222
/* }}} */
206223

library.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,54 @@ PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC)
100100
return 0;
101101
}
102102

103+
104+
PHPAPI int
105+
redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
106+
REDIS_SCAN_TYPE type, long *iter TSRMLS_DC)
107+
{
108+
REDIS_REPLY_TYPE reply_type;
109+
int reply_info;
110+
char *p_iter;
111+
112+
// Our response should have two multibulk replies
113+
if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0
114+
|| reply_type != TYPE_MULTIBULK || reply_info != 2)
115+
{
116+
return -1;
117+
}
118+
119+
// The BULK response iterator
120+
if(redis_read_reply_type(redis_sock, &reply_type, &reply_info TSRMLS_CC)<0
121+
|| reply_type != TYPE_BULK)
122+
{
123+
return -1;
124+
}
125+
126+
// Attempt to read the iterator
127+
if(!(p_iter = redis_sock_read_bulk_reply(redis_sock, reply_info TSRMLS_CC))) {
128+
return -1;
129+
}
130+
131+
// Push the iterator out to the caller
132+
*iter = atol(p_iter);
133+
efree(p_iter);
134+
135+
// Read our actual keys/members/etc differently depending on what kind of
136+
// scan command this is. They all come back in slightly different ways
137+
switch(type) {
138+
case TYPE_SCAN:
139+
return redis_sock_read_multibulk_reply_raw(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
140+
case TYPE_SSCAN:
141+
return redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
142+
case TYPE_ZSCAN:
143+
return redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
144+
case TYPE_HSCAN:
145+
return redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL);
146+
default:
147+
return -1;
148+
}
149+
}
150+
103151
PHPAPI zval *redis_sock_read_multibulk_reply_zval(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) {
104152
char inbuf[1024];
105153
int numElems;
@@ -1064,6 +1112,8 @@ PHPAPI RedisSock* redis_sock_create(char *host, int host_len, unsigned short por
10641112
redis_sock->err = NULL;
10651113
redis_sock->err_len = 0;
10661114

1115+
redis_sock->scan = REDIS_SCAN_NORETRY;
1116+
10671117
return redis_sock;
10681118
}
10691119

library.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ PHPAPI int redis_sock_read_multibulk_reply_loop(INTERNAL_FUNCTION_PARAMETERS, Re
3535
PHPAPI int redis_sock_read_multibulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
3636
PHPAPI int redis_sock_read_multibulk_reply_zipped_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
3737
PHPAPI int redis_sock_read_multibulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
38+
PHPAPI int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, long *iter TSRMLS_DC);
39+
3840
PHPAPI int redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz TSRMLS_DC);
3941
PHPAPI void redis_stream_close(RedisSock *redis_sock TSRMLS_DC);
4042
PHPAPI int redis_check_eof(RedisSock *redis_sock TSRMLS_DC);

php_redis.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,13 @@ PHP_METHOD(Redis, wait);
186186

187187
PHP_METHOD(Redis, client);
188188

189+
/* SCAN and friends */
190+
PHP_METHOD(Redis, scan);
191+
PHP_METHOD(Redis, hscan);
192+
PHP_METHOD(Redis, sscan);
193+
PHP_METHOD(Redis, zscan);
194+
195+
/* Reflection */
189196
PHP_METHOD(Redis, getHost);
190197
PHP_METHOD(Redis, getPort);
191198
PHP_METHOD(Redis, getDBNum);

0 commit comments

Comments
 (0)