Skip to content

Commit

Permalink
Consolidate get(...GET_EXTENDED) and cas into the store implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
sodabrew committed Feb 12, 2017
1 parent de250ed commit fe2d1f3
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 107 deletions.
166 changes: 61 additions & 105 deletions php_memcached.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ typedef enum {
MEMC_OP_ADD,
MEMC_OP_REPLACE,
MEMC_OP_APPEND,
MEMC_OP_PREPEND
MEMC_OP_PREPEND,
MEMC_OP_CAS
} php_memc_write_op;

typedef struct {
Expand Down Expand Up @@ -400,7 +401,7 @@ static
memcached_return s_server_cursor_version_cb(const memcached_st *ptr, php_memcached_instance_st instance, void *in_context);

static
zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, zend_string *server_key, zend_string *key, zval *value, time_t expiration);
zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, zend_string *server_key, zend_string *key, zval *value, time_t expiration, uint64_t cas_token);

static
void php_memc_destroy(memcached_st *memc, php_memc_user_data_t *memc_user_data);
Expand Down Expand Up @@ -750,44 +751,32 @@ zend_bool s_invoke_cache_callback(zval *zobject, zend_fcall_info *fci, zend_fcal
php_memc_object_t *intern = Z_MEMC_OBJ_P(zobject);

/* Prepare params */
ZVAL_COPY(&params[0], zobject);
ZVAL_COPY(&params[0], zobject); /* memc */
ZVAL_STR_COPY(&params[1], key); /* key */
ZVAL_NEW_REF(&params[2], value); /* value */
ZVAL_NEW_EMPTY_REF(&params[3]); /* expiration */
ZVAL_NULL(Z_REFVAL(params[3]));

if (with_cas) {
fci->param_count = 3;
} else {
ZVAL_NEW_EMPTY_REF(&params[3]); /* expiration */
ZVAL_NULL(Z_REFVAL(params[3]));
fci->param_count = 4;
}

fci->param_count = 4;
fci->retval = &retval;
fci->params = params;

if (zend_call_function(fci, fcc) == SUCCESS) {
if (zend_is_true(&retval)) {
time_t expiration;
zval *val = Z_REFVAL(params[2]);

if (with_cas) {
if (Z_TYPE_P(val) == IS_ARRAY) {
zval *rv = zend_hash_str_find(Z_ARRVAL_P(val), "value", sizeof("value") - 1);
if (rv) {
zval *cas = zend_hash_str_find(Z_ARRVAL_P(val), "cas", sizeof("cas") -1);
expiration = cas? Z_LVAL_P(cas) : 0;
status = s_memc_write_zval (intern, MEMC_OP_SET, NULL, key, rv, expiration);
}
/* memleak? zval_ptr_dtor(value); */
ZVAL_COPY(value, val);
}
} else {
expiration = zval_get_long(Z_REFVAL(params[3]));
status = s_memc_write_zval (intern, MEMC_OP_SET, NULL, key, val, expiration);
/* memleak? zval_ptr_dtor(value); */
ZVAL_COPY(value, val);
}
if (zend_call_function(fci, fcc) == SUCCESS && zend_is_true(&retval)) {
time_t expiration;
zval *val = Z_REFVAL(params[2]);
zval *rv = NULL;
zval *zv_cas = NULL;
uint64_t cas;

if (Z_TYPE_P(val) == IS_ARRAY) {
rv = zend_hash_str_find(Z_ARRVAL_P(val), "value", sizeof("value") - 1);
zv_cas = zend_hash_str_find(Z_ARRVAL_P(val), "cas", sizeof("cas") - 1);
}

expiration = zval_get_long(Z_REFVAL(params[3]));
cas = zv_cas ? s_zval_to_uint64(zv_cas) : 0;
status = s_memc_write_zval (intern, MEMC_OP_SET, NULL, key, rv ? rv : val, expiration, cas);
ZVAL_COPY(value, val);
}
else {
s_memc_set_status(intern, MEMCACHED_NOTFOUND, 0);
Expand Down Expand Up @@ -1054,7 +1043,7 @@ zend_bool s_should_retry_write (php_memc_object_t *intern, memcached_return stat
}

static
zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, zend_string *server_key, zend_string *key, zval *value, time_t expiration)
zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, zend_string *server_key, zend_string *key, zval *value, time_t expiration, uint64_t cas)
{
uint32_t flags = 0;
zend_string *payload = NULL;
Expand Down Expand Up @@ -1099,6 +1088,10 @@ zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, ze
case MEMC_OP_PREPEND:
status = memc_write_using_fn_by_key(memcached_prepend_by_key);
break;

case MEMC_OP_CAS:
status = memcached_cas_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags, cas);
break;
}

if (status == MEMCACHED_END) {
Expand Down Expand Up @@ -1131,6 +1124,10 @@ zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, ze
case MEMC_OP_PREPEND:
status = memc_write_using_fn(memcached_prepend);
break;

case MEMC_OP_CAS:
status = memcached_cas(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags, cas);
break;
}
if (status == MEMCACHED_END) {
status = MEMCACHED_SUCCESS;
Expand Down Expand Up @@ -1820,7 +1817,7 @@ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke
str_key = zend_string_init(tmp_key, tmp_len, 0);
}

if (!s_memc_write_zval (intern, MEMC_OP_SET, server_key, str_key, value, expiration)) {
if (!s_memc_write_zval (intern, MEMC_OP_SET, server_key, str_key, value, expiration, 0)) {
php_error_docref(NULL, E_WARNING, "failed to set key %s", ZSTR_VAL(str_key));
}

Expand Down Expand Up @@ -1898,6 +1895,22 @@ PHP_METHOD(Memcached, replaceByKey)
}
/* }}} */

/* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration ])
Sets the value for the given key, failing if the cas_token doesn't match the one in memcache */
PHP_METHOD(Memcached, cas)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_CAS, 0);
}
/* }}} */

/* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration ])
Sets the value for the given key on the server identified by the server_key, failing if the cas_token doesn't match the one in memcache */
PHP_METHOD(Memcached, casByKey)
{
php_memc_store_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, MEMC_OP_CAS, 1);
}
/* }}} */

/* {{{ -- php_memc_store_impl */
static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool by_key)
{
Expand All @@ -1906,7 +1919,9 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool
zend_string *s_value;
zval s_zvalue;
zval *value = NULL;
zval *zv_cas = NULL;
zend_long expiration = 0;
uint64_t cas = 0;
MEMC_METHOD_INIT_VARS;

if (by_key) {
Expand All @@ -1920,6 +1935,11 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool
if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|l", &server_key, &key, &expiration) == FAILURE) {
return;
}
} else if (op == MEMC_OP_CAS) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zSSz|l", &zv_cas, &server_key, &key, &value, &expiration) == FAILURE) {
return;
}
cas = s_zval_to_uint64(zv_cas);
} else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSz|l", &server_key, &key, &value, &expiration) == FAILURE) {
return;
Expand All @@ -1936,6 +1956,11 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|l", &key, &expiration) == FAILURE) {
return;
}
} else if (op == MEMC_OP_CAS) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zSz|l", &zv_cas, &key, &value, &expiration) == FAILURE) {
return;
}
cas = s_zval_to_uint64(zv_cas);
} else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sz|l", &key, &value, &expiration) == FAILURE) {
return;
Expand All @@ -1959,82 +1984,13 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool
}
}

if (!s_memc_write_zval (intern, op, server_key, key, value, expiration)) {
if (!s_memc_write_zval (intern, op, server_key, key, value, expiration, cas)) {
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */

/* {{{ -- php_memc_cas_impl */
static void php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key)
{
zval *zv_cas;
uint64_t cas;
zend_string *key;
zend_string *server_key = NULL;
zval *value;
time_t expiration = 0;
zend_string *payload;
uint32_t flags = 0;
memcached_return status;
MEMC_METHOD_INIT_VARS;

if (by_key) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zSSz|ll", &zv_cas, &server_key, &key,
&value, &expiration) == FAILURE) {
return;
}
} else {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zSz|ll", &zv_cas, &key, &value,
&expiration) == FAILURE) {
return;
}
}

MEMC_METHOD_FETCH_OBJECT;
s_memc_set_status(intern, MEMCACHED_SUCCESS, 0);
MEMC_CHECK_KEY(intern, key);

cas = s_zval_to_uint64(zv_cas);

payload = s_zval_to_payload(intern, value, &flags);
if (payload == NULL) {
intern->rescode = MEMC_RES_PAYLOAD_FAILURE;
RETURN_FALSE;
}

if (by_key) {
status = memcached_cas_by_key(intern->memc, ZSTR_VAL(server_key), ZSTR_LEN(server_key), ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags, cas);
} else {
status = memcached_cas(intern->memc, ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(payload), ZSTR_LEN(payload), expiration, flags, cas);
}

zend_string_release(payload);
if (s_memc_status_handle_result_code(intern, status) == FAILURE) {
RETURN_FALSE;
}

RETURN_TRUE;
}
/* }}} */

/* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration ])
Sets the value for the given key, failing if the cas_token doesn't match the one in memcache */
PHP_METHOD(Memcached, cas)
{
php_memc_cas_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */

/* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration ])
Sets the value for the given key on the server identified by the server_key, failing if the cas_token doesn't match the one in memcache */
PHP_METHOD(Memcached, casByKey)
{
php_memc_cas_impl(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */

/* {{{ Memcached::delete(string key [, int time ])
Deletes the given key */
PHP_METHOD(Memcached, delete)
Expand Down
84 changes: 82 additions & 2 deletions tests/cachecallback.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ $m->get ($first_key, function (Memcached $memc, $key, &$value, &$expiration) {

var_dump ($m->get ($first_key));

// Get the first key again, expecting that the callback is _not_ called a second timd
$m->get ($first_key, function (Memcached $memc, $key, &$value, &$expiration) {
throw new Exception ('This callback should not be called');
});

var_dump (
$m->get ($second_key, function (Memcached $memc, $key, &$value, &$expiration) {
$value = "hello";
Expand All @@ -50,14 +55,89 @@ try {

var_dump ($m->get ($third_key));

echo "OK" . PHP_EOL;

//
// Run through the scenarios again in GET_EXTENDED mode
//
$m->delete($first_key);
$m->delete($second_key);
$m->delete($third_key);

var_dump (
$m->get ($first_key, function (Memcached $memc, $key, &$value) {
$value = [
"value" => "first_ext",
"cas" => 12345,
"expiration" => 10 ];
return true;
}, Memcached::GET_EXTENDED)
);

var_dump ($m->get ($first_key, null, Memcached::GET_EXTENDED));

// Get the first key again, expecting that the callback is _not_ called a second timd
$m->get ($first_key, function (Memcached $memc, $key, &$value) {
throw new Exception ('This callback should not be called');
}, Memcached::GET_EXTENDED);

var_dump (
$m->get ($second_key, function (Memcached $memc, $key, &$value) {
$value = [
"value" => "second_ext",
"cas" => 12345,
"expiration" => 10 ];
return false;
}, Memcached::GET_EXTENDED)
);

var_dump ($m->get ($second_key));
var_dump ($m->get ($second_key, null, Memcached::GET_EXTENDED));

try {
$m->get ($third_key, function (Memcached $memc, $key, &$value) {
$value = [
"value" => "third_ext",
"cas" => "12345",
"expiration" => "10" ];
throw new Exception ('this is a test');
return true;
}, Memcached::GET_EXTENDED);
} catch (Exception $e) {
echo 'Got exception' . PHP_EOL;
}

var_dump ($m->get ($third_key));

echo "OK" . PHP_EOL;

--EXPECT--
--EXPECTF--
string(5) "hello"
string(5) "hello"
bool(false)
bool(false)
Got exception
bool(false)
OK
OK
array(3) {
["value"]=>
string(9) "first_ext"
["cas"]=>
int(%d)
["flags"]=>
int(0)
}
array(3) {
["value"]=>
string(9) "first_ext"
["cas"]=>
int(%d)
["flags"]=>
int(0)
}
bool(false)
bool(false)
bool(false)
Got exception
bool(false)
OK

0 comments on commit fe2d1f3

Please sign in to comment.