Skip to content

Commit 0b2a644

Browse files
committed
Added WATCH, UNWATCH, with doc and unit tests.
1 parent 46d5baa commit 0b2a644

File tree

4 files changed

+98
-4
lines changed

4 files changed

+98
-4
lines changed

README.markdown

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,23 @@ $ret == array(
167167
*/
168168
</pre>
169169

170+
## watch, unwatch
171+
##### Description
172+
Watches a key for modifications by another client. If the key is modified between `WATCH` and `EXEC`, the MULTI/EXEC transaction will fail (return `FALSE`). `unwatch` cancels all the watching of all keys by this client.
173+
##### Parameters
174+
*keys*: a list of keys
175+
##### Example
176+
<pre>
177+
$redis->watch('x');
178+
/* long code here during the execution of which other clients could well modify `x` */
179+
$ret = $redis->multi()
180+
->incr('x')
181+
->exec();
182+
/*
183+
$ret = FALSE if x has been modified between the call to WATCH and the call to EXEC.
184+
*/
185+
</pre>
186+
170187
## exists
171188
##### Description
172189
Verify if the specified key exists.

php_redis.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ PHP_METHOD(Redis, hMset);
113113
PHP_METHOD(Redis, multi);
114114
PHP_METHOD(Redis, discard);
115115
PHP_METHOD(Redis, exec);
116+
PHP_METHOD(Redis, watch);
117+
PHP_METHOD(Redis, unwatch);
116118

117119
PHP_METHOD(Redis, pipeline);
118120

redis.c

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ static zend_function_entry redis_functions[] = {
144144
PHP_ME(Redis, discard, NULL, ZEND_ACC_PUBLIC)
145145
PHP_ME(Redis, exec, NULL, ZEND_ACC_PUBLIC)
146146
PHP_ME(Redis, pipeline, NULL, ZEND_ACC_PUBLIC)
147+
PHP_ME(Redis, watch, NULL, ZEND_ACC_PUBLIC)
148+
PHP_ME(Redis, unwatch, NULL, ZEND_ACC_PUBLIC)
147149

148150
PHP_ME(Redis, publish, NULL, ZEND_ACC_PUBLIC)
149151
PHP_ME(Redis, subscribe, NULL, ZEND_ACC_PUBLIC)
@@ -1007,6 +1009,35 @@ PHP_METHOD(Redis, delete)
10071009
}
10081010
/* }}} */
10091011

1012+
/* {{{ proto boolean Redis::watch(string key1, string key2...)
1013+
*/
1014+
PHP_METHOD(Redis, watch)
1015+
{
1016+
RedisSock *redis_sock;
1017+
1018+
generic_multiple_args_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,
1019+
"WATCH", sizeof("WATCH") - 1,
1020+
1, &redis_sock);
1021+
zval * object = getThis();
1022+
1023+
IF_ATOMIC() {
1024+
redis_boolean_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL);
1025+
}
1026+
REDIS_PROCESS_RESPONSE(redis_boolean_response);
1027+
1028+
}
1029+
/* }}} */
1030+
1031+
/* {{{ proto boolean Redis::unwatch()
1032+
*/
1033+
PHP_METHOD(Redis, unwatch)
1034+
{
1035+
char cmd[] = "*1" _NL "$7" _NL "UNWATCH" _NL;
1036+
generic_empty_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, estrdup(cmd), sizeof(cmd)-1);
1037+
1038+
}
1039+
/* }}} */
1040+
10101041
/* {{{ proto array Redis::getKeys(string pattern)
10111042
*/
10121043
PHP_METHOD(Redis, getKeys)
@@ -4128,8 +4159,11 @@ PHPAPI int redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS,
41284159
/* number of responses */
41294160
int numElems = atoi(inbuf+1);
41304161

4131-
zval_dtor(return_value);
4162+
if(numElems < 0) {
4163+
return -1;
4164+
}
41324165

4166+
zval_dtor(return_value);
41334167
zval *z_tab;
41344168
MAKE_STD_ZVAL(z_tab);
41354169
array_init(z_tab);
@@ -4200,6 +4234,7 @@ PHP_METHOD(Redis, exec)
42004234
if (redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock) < 0) {
42014235
zval_dtor(return_value);
42024236
free_reply_callbacks(object);
4237+
set_flag(object, REDIS_ATOMIC TSRMLS_CC);
42034238
RETURN_FALSE;
42044239
}
42054240
free_reply_callbacks(object);

tests/TestRedis.php

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@ class Redis_Test extends PHPUnit_Framework_TestCase
1212

1313
public function setUp()
1414
{
15-
$this->redis = new Redis();
16-
$this->redis->connect('127.0.0.1', 6379);
15+
$this->redis = $this->newInstance();
16+
}
17+
18+
private function newInstance() {
19+
$r = new Redis();
20+
$r->connect('127.0.0.1', 6379);
1721

1822
// uncomment the following if you want to use authentication
19-
// $this->assertTrue($this->redis->auth('foobared'));
23+
// $this->assertTrue($r->auth('foobared'));
24+
25+
return $r;
2026
}
2127

2228
public function tearDown()
@@ -1592,6 +1598,37 @@ public function testHashes() {
15921598

15931599
public function testMultiExec() {
15941600
$this->sequence(Redis::MULTI);
1601+
1602+
$this->redis->set('x', '42');
1603+
1604+
$this->assertTrue(TRUE === $this->redis->watch('x'));
1605+
$ret = $this->redis->multi()
1606+
->get('x')
1607+
->exec();
1608+
1609+
// successful transaction
1610+
$this->assertTrue($ret === array('42'));
1611+
1612+
// failed transaction
1613+
$this->redis->watch('x');
1614+
1615+
$r = $this->newInstance(); // new instance, modifying `x'.
1616+
$r->incr('x');
1617+
1618+
$ret = $this->redis->multi()
1619+
->get('x')
1620+
->exec();
1621+
$this->assertTrue($ret === FALSE); // failed because another client changed our watched key between WATCH and EXEC.
1622+
1623+
// watch and unwatch
1624+
$this->redis->watch('x');
1625+
$r->incr('x'); // other instance
1626+
$this->redis->unwatch(); // cancel transaction watch
1627+
1628+
$ret = $this->redis->multi()
1629+
->get('x')
1630+
->exec();
1631+
$this->assertTrue($ret === array('44')); // succeeded since we've cancel the WATCH command.
15951632
}
15961633

15971634
public function testPipeline() {
@@ -1769,6 +1806,9 @@ protected function sequence($mode) {
17691806
->dbSize()
17701807
->lastsave()
17711808
->exec();
1809+
1810+
$this->redis->select(0); // back to normal
1811+
17721812
$i = 0;
17731813
$this->assertTrue(is_array($ret));
17741814
$this->assertTrue($ret[$i++] === TRUE); // select

0 commit comments

Comments
 (0)