Sitelet https://github.com/phpredis/phpredis/commit/9e65c429
Skip to content

Commit 9e65c42

Browse files
Implement UNLINK command
This commit implements UNLINK for Redis, RedisCluster, and RedisArray. To a client library UNLINK behaves identically to DEL so we can use the same handlers for both.
1 parent 837dee4 commit 9e65c42

10 files changed

Lines changed: 154 additions & 46 deletions

‎php_redis.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ PHP_METHOD(Redis, renameNx);
4646
PHP_METHOD(Redis, getMultiple);
4747
PHP_METHOD(Redis, exists);
4848
PHP_METHOD(Redis, delete);
49+
PHP_METHOD(Redis, unlink);
4950
PHP_METHOD(Redis, incr);
5051
PHP_METHOD(Redis, incrBy);
5152
PHP_METHOD(Redis, incrByFloat);

‎redis.c‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ static zend_function_entry redis_functions[] = {
390390
PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC)
391391
PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC)
392392
PHP_ME(Redis, type, arginfo_key, ZEND_ACC_PUBLIC)
393+
PHP_ME(Redis, unlink, arginfo_nkeys, ZEND_ACC_PUBLIC)
393394
PHP_ME(Redis, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)
394395
PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
395396
PHP_ME(Redis, wait, arginfo_wait, ZEND_ACC_PUBLIC)
@@ -1176,6 +1177,13 @@ PHP_METHOD(Redis, delete)
11761177
}
11771178
/* }}} */
11781179

1180+
/* {{{ proto long Redis::unlink(string $key1, string $key2 [, string $key3...]) }}}
1181+
* {{{ proto long Redis::unlink(array $keys) */
1182+
PHP_METHOD(Redis, unlink)
1183+
1184+
REDIS_PROCESS_CMD(unlink, redis_long_response);
1185+
}
1186+
11791187
PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock)
11801188
{
11811189
redis_sock->watching = 1;
@@ -1512,6 +1520,7 @@ PHP_METHOD(Redis, sDiffStore) {
15121520
}
15131521
/* }}} */
15141522

1523+
15151524
/* {{{ proto array Redis::sort(string key, array options) */
15161525
PHP_METHOD(Redis, sort) {
15171526
char *cmd;

‎redis_array.c‎

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ zend_function_entry redis_array_functions[] = {
122122
PHP_ME(RedisArray, save, arginfo_void, ZEND_ACC_PUBLIC)
123123
PHP_ME(RedisArray, select, arginfo_select, ZEND_ACC_PUBLIC)
124124
PHP_ME(RedisArray, setOption,arginfo_setopt, ZEND_ACC_PUBLIC)
125+
PHP_ME(RedisArray, unlink, arginfo_void, ZEND_ACC_PUBLIC)
125126
PHP_ME(RedisArray, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
126127
PHP_MALIAS(RedisArray, delete, del, arginfo_del, ZEND_ACC_PUBLIC)
127128
PHP_MALIAS(RedisArray, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC)
@@ -849,7 +850,7 @@ PHP_METHOD(RedisArray, select)
849850
zval_dtor(&z_fun);
850851
}
851852
#if (PHP_MAJOR_VERSION < 7)
852-
#define HANDLE_MULTI_EXEC(ra, cmd) do { \
853+
#define HANDLE_MULTI_EXEC(ra, cmd, cmdlen) do { \
853854
if (ra && ra->z_multi_exec) { \
854855
int i, num_varargs;\
855856
zval ***varargs = NULL, *z_arg_array; \
@@ -867,7 +868,7 @@ PHP_METHOD(RedisArray, select)
867868
add_next_index_zval(z_arg_array, z_tmp); \
868869
}\
869870
/* call */\
870-
ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, sizeof(cmd) - 1, z_arg_array, NULL); \
871+
ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmdlen, z_arg_array, NULL); \
871872
zval_ptr_dtor(&z_arg_array); \
872873
if(varargs) {\
873874
efree(varargs);\
@@ -892,7 +893,7 @@ PHP_METHOD(RedisArray, select)
892893
add_next_index_zval(&z_arg_array, &z_tmp); \
893894
} \
894895
/* call */\
895-
ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, sizeof(cmd) - 1, &z_arg_array, NULL); \
896+
ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmdlen, &z_arg_array, NULL); \
896897
zval_dtor(&z_arg_array); \
897898
return; \
898899
} \
@@ -914,7 +915,7 @@ PHP_METHOD(RedisArray, mget)
914915
}
915916

916917
/* Multi/exec support */
917-
HANDLE_MULTI_EXEC(ra, "MGET");
918+
HANDLE_MULTI_EXEC(ra, "MGET", sizeof("MGET") - 1);
918919

919920
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
920921
&object, redis_array_ce, &z_keys) == FAILURE) {
@@ -1068,7 +1069,7 @@ PHP_METHOD(RedisArray, mset)
10681069
}
10691070

10701071
/* Multi/exec support */
1071-
HANDLE_MULTI_EXEC(ra, "MSET");
1072+
HANDLE_MULTI_EXEC(ra, "MSET", sizeof("MSET") - 1);
10721073

10731074
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
10741075
&object, redis_array_ce, &z_keys) == FAILURE)
@@ -1179,9 +1180,8 @@ PHP_METHOD(RedisArray, mset)
11791180
RETURN_TRUE;
11801181
}
11811182

1182-
/* DEL will distribute the call to several nodes and regroup the values. */
1183-
PHP_METHOD(RedisArray, del)
1184-
{
1183+
/* Generic handler for DEL or UNLINK which behave identically to phpredis */
1184+
static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
11851185
zval *object, z_keys, z_fun, *data, z_ret, *z_tmp, *z_args;
11861186
int i, n;
11871187
RedisArray *ra;
@@ -1194,8 +1194,9 @@ PHP_METHOD(RedisArray, del)
11941194
if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) {
11951195
RETURN_FALSE;
11961196
}
1197+
11971198
/* Multi/exec support */
1198-
HANDLE_MULTI_EXEC(ra, "DEL");
1199+
HANDLE_MULTI_EXEC(ra, kw, kw_len);
11991200

12001201
/* get all args in z_args */
12011202
z_args = emalloc(argc * sizeof(zval));
@@ -1259,7 +1260,7 @@ PHP_METHOD(RedisArray, del)
12591260
} ZEND_HASH_FOREACH_END();
12601261

12611262
/* prepare call */
1262-
ZVAL_STRINGL(&z_fun, "DEL", 3);
1263+
ZVAL_STRINGL(&z_fun, kw, kw_len);
12631264

12641265
/* calls */
12651266
for(n = 0; n < ra->count; ++n) { /* for each node */
@@ -1285,7 +1286,7 @@ PHP_METHOD(RedisArray, del)
12851286
found++;
12861287
}
12871288

1288-
if(!found) { /* don't run empty DELs */
1289+
if(!found) { /* don't run empty DEL or UNLINK commands */
12891290
zval_dtor(&z_argarray);
12901291
continue;
12911292
}
@@ -1322,6 +1323,16 @@ PHP_METHOD(RedisArray, del)
13221323
RETURN_LONG(total);
13231324
}
13241325

1326+
/* DEL will distribute the call to several nodes and regroup the values. */
1327+
PHP_METHOD(RedisArray, del)
1328+
{
1329+
ra_generic_del(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL")-1);
1330+
}
1331+
1332+
PHP_METHOD(RedisArray, unlink) {
1333+
ra_generic_del(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1);
1334+
}
1335+
13251336
PHP_METHOD(RedisArray, multi)
13261337
{
13271338
zval *object;

‎redis_array.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ PHP_METHOD(RedisArray, flushall);
2525
PHP_METHOD(RedisArray, mget);
2626
PHP_METHOD(RedisArray, mset);
2727
PHP_METHOD(RedisArray, del);
28+
PHP_METHOD(RedisArray, unlink);
2829
PHP_METHOD(RedisArray, keys);
2930
PHP_METHOD(RedisArray, getOption);
3031
PHP_METHOD(RedisArray, setOption);

‎redis_cluster.c‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ zend_function_entry redis_cluster_functions[] = {
244244
PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC)
245245
PHP_ME(RedisCluster, type, arginfo_key, ZEND_ACC_PUBLIC)
246246
PHP_ME(RedisCluster, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)
247+
PHP_ME(RedisCluster, unlink, arginfo_del, ZEND_ACC_PUBLIC)
247248
PHP_ME(RedisCluster, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
248249
PHP_ME(RedisCluster, watch, arginfo_watch, ZEND_ACC_PUBLIC)
249250
PHP_ME(RedisCluster, zadd, arginfo_zadd, ZEND_ACC_PUBLIC)
@@ -708,7 +709,8 @@ static HashTable *method_args_to_ht(zval *z_args, int argc) {
708709
return ht_ret;
709710
}
710711

711-
/* Handler for both MGET and DEL */
712+
/* Convienience handler for commands that take multiple keys such as
713+
* MGET, DEL, and UNLINK */
712714
static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len,
713715
zval *z_ret, cluster_cb cb)
714716
{
@@ -935,8 +937,10 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len,
935937
return 0;
936938
}
937939

938-
/* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */
939-
PHP_METHOD(RedisCluster, del) {
940+
/* Generic passthru for DEL and UNLINK which act identically */
941+
static void cluster_generic_delete(INTERNAL_FUNCTION_PARAMETERS,
942+
char *kw, int kw_len)
943+
{
940944
zval *z_ret;
941945

942946
#if (PHP_MAJOR_VERSION < 7)
@@ -949,14 +953,24 @@ PHP_METHOD(RedisCluster, del) {
949953
ZVAL_LONG(z_ret, 0);
950954

951955
// Parse args, process
952-
if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL",
953-
sizeof("DEL")-1, z_ret, cluster_del_resp)<0)
956+
if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, kw, kw_len, z_ret,
957+
cluster_del_resp) < 0)
954958
{
955959
efree(z_ret);
956960
RETURN_FALSE;
957961
}
958962
}
959963

964+
/* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */
965+
PHP_METHOD(RedisCluster, del) {
966+
cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL") - 1);
967+
}
968+
969+
/* {{{ proto array RedisCluster::unlink(string key1, string key2, ... keyN) */
970+
PHP_METHOD(RedisCluster, unlink) {
971+
cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1);
972+
}
973+
960974
/* {{{ proto array RedisCluster::mget(array keys) */
961975
PHP_METHOD(RedisCluster, mget) {
962976
zval *z_ret;

‎redis_cluster.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ PHP_METHOD(RedisCluster, mset);
130130
PHP_METHOD(RedisCluster, msetnx);
131131
PHP_METHOD(RedisCluster, mset);
132132
PHP_METHOD(RedisCluster, del);
133+
PHP_METHOD(RedisCluster, unlink);
133134
PHP_METHOD(RedisCluster, dump);
134135
PHP_METHOD(RedisCluster, setex);
135136
PHP_METHOD(RedisCluster, psetex);

‎redis_commands.c‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2949,6 +2949,14 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
29492949
"SDIFFSTORE", sizeof("SDIFFSTORE")-1, 1, 0, cmd, cmd_len, slot);
29502950
}
29512951

2952+
/* UNLINK */
2953+
int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
2954+
char **cmd, int *cmd_len, short *slot, void **ctx)
2955+
{
2956+
return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
2957+
"UNLINK", sizeof("UNLINK")-1, 1, 0, cmd, cmd_len, slot);
2958+
}
2959+
29522960
/* COMMAND */
29532961
int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
29542962
char **cmd, int *cmd_len, short *slot, void **ctx)

‎redis_commands.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
236236
int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
237237
char **cmd, int *cmd_len, short *slot, void **ctx);
238238

239+
int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
240+
char **cmd, int *cmd_len, short *slot, void **ctx);
241+
239242
int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
240243
char **cmd, int *cmd_len, short *slot, void **ctx);
241244

‎tests/RedisArrayTest.php‎

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,30 @@ function parseHostPort($str, &$host, &$port) {
1818
$port = substr($str, $pos+1);
1919
}
2020

21+
function getRedisVersion($obj_r) {
22+
$arr_info = $obj_r->info();
23+
if (!$arr_info || !isset($arr_info['redis_version'])) {
24+
return "0.0.0";
25+
}
26+
return $arr_info['redis_version'];
27+
}
28+
29+
/* Determine the lowest redis version attached to this RedisArray object */
30+
function getMinVersion($obj_ra) {
31+
$min_version = "0.0.0";
32+
foreach ($obj_ra->_hosts() as $host) {
33+
$version = getRedisVersion($obj_ra->_instance($host));
34+
if (version_compare($version, $min_version) > 0) {
35+
$min_version = $version;
36+
}
37+
}
38+
39+
return $min_version;
40+
}
41+
2142
class Redis_Array_Test extends TestSuite
2243
{
44+
private $min_version;
2345
private $strings;
2446
public $ra = NULL;
2547
private $data = NULL;
@@ -34,6 +56,7 @@ public function setUp() {
3456

3557
global $newRing, $oldRing, $useIndex;
3658
$this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex));
59+
$this->min_version = getMinVersion($this->ra);
3760
}
3861

3962
public function testMSet() {
@@ -141,6 +164,8 @@ class Redis_Rehashing_Test extends TestSuite
141164
public $ra = NULL;
142165
private $useIndex;
143166

167+
private $min_version;
168+
144169
// data
145170
private $strings;
146171
private $sets;
@@ -185,6 +210,7 @@ public function setUp() {
185210

186211
// create array
187212
$this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex));
213+
$this->min_version = getMinVersion($this->ra);
188214
}
189215

190216
public function testFlush() {
@@ -206,12 +232,12 @@ private function distributeKeys() {
206232
foreach($this->strings as $k => $v) {
207233
$this->ra->set($k, $v);
208234
}
209-
235+
210236
// sets
211237
foreach($this->sets as $k => $v) {
212238
call_user_func_array(array($this->ra, 'sadd'), array_merge(array($k), $v));
213239
}
214-
240+
215241
// lists
216242
foreach($this->lists as $k => $v) {
217243
call_user_func_array(array($this->ra, 'rpush'), array_merge(array($k), $v));
@@ -221,7 +247,7 @@ private function distributeKeys() {
221247
foreach($this->hashes as $k => $v) {
222248
$this->ra->hmset($k, $v);
223249
}
224-
250+
225251
// sorted sets
226252
foreach($this->zsets as $k => $v) {
227253
call_user_func_array(array($this->ra, 'zadd'), array_merge(array($k), $v));
@@ -314,6 +340,7 @@ public function testReadRedistributedKeys() {
314340
class Redis_Auto_Rehashing_Test extends TestSuite {
315341

316342
public $ra = NULL;
343+
private $min_version;
317344

318345
// data
319346
private $strings;
@@ -330,6 +357,7 @@ public function setUp() {
330357

331358
// create array
332359
$this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE));
360+
$this->min_version = getMinVersion($this->ra);
333361
}
334362

335363
public function testDistribute() {
@@ -378,11 +406,14 @@ public function testAllKeysHaveBeenMigrated() {
378406
// Test node-specific multi/exec
379407
class Redis_Multi_Exec_Test extends TestSuite {
380408
public $ra = NULL;
409+
private $min_version;
381410

382411
public function setUp() {
383412
global $newRing, $oldRing, $useIndex;
413+
384414
// create array
385415
$this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex));
416+
$this->min_version = getMinVersion($this->ra);
386417
}
387418

388419
public function testInit() {
@@ -464,6 +495,22 @@ public function testMultiExecDel() {
464495
$this->assertEquals(0, $this->ra->exists('1_{employee:joe}_salary'));
465496
}
466497

498+
public function testMutliExecUnlink() {
499+
if (version_compare($this->min_version, "4.0.0", "lt")) {
500+
var_dump($this->min_version);
501+
$this->markTestSkipped();
502+
}
503+
504+
$this->ra->set('{unlink}:key1', 'bar');
505+
$this->ra->set('{unlink}:key2', 'bar');
506+
507+
$out = $this->ra->multi($this->ra->_target('{unlink}'))
508+
->del('{unlink}:key1', '{unlink}:key2')
509+
->exec();
510+
511+
$this->assertTrue($out[0] === 2);
512+
}
513+
467514
public function testDiscard() {
468515
/* phpredis issue #87 */
469516
$key = 'test_err';
@@ -502,11 +549,13 @@ public function testDiscard() {
502549
class Redis_Distributor_Test extends TestSuite {
503550

504551
public $ra = NULL;
552+
private $min_version;
505553

506554
public function setUp() {
507555
global $newRing, $oldRing, $useIndex;
508556
// create array
509557
$this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'distributor' => array($this, 'distribute')));
558+
$this->min_version = getMinVersion($this->ra);
510559
}
511560

512561
public function testInit() {

0 commit comments

Comments
 (0)