diff --git a/memcached-api.php b/memcached-api.php index ecc4072c..45641415 100644 --- a/memcached-api.php +++ b/memcached-api.php @@ -185,13 +185,13 @@ class Memcached { public function __construct( $persistent_id = '', $on_new_object_cb = null ) {} - public function get( $key, $cache_cb = null, &$cas_token = null ) {} + public function get( $key, $cache_cb = null, &$cas_token = null, &$udf_flags = null ) {} - public function getByKey( $server_key, $key, $cache_cb = null, &$cas_token = null ) {} + public function getByKey( $server_key, $key, $cache_cb = null, &$cas_token = null, &$udf_flags = null ) {} - public function getMulti( array $keys, &$cas_tokens = null, $flags = 0 ) {} + public function getMulti( array $keys, &$cas_tokens = null, $flags = 0, &$udf_flags = null ) {} - public function getMultiByKey( $server_key, array $keys, &$cas_tokens = null, $flags = 0 ) {} + public function getMultiByKey( $server_key, array $keys, &$cas_tokens = null, $flags = 0, &$udf_flags = null ) {} public function getDelayed( array $keys, $with_cas = null, $value_cb = null ) {} @@ -201,25 +201,25 @@ public function fetch( ) {} public function fetchAll( ) {} - public function set( $key, $value, $expiration = 0 ) {} + public function set( $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function touch( $key, $expiration = 0 ) {} public function touchbyKey( $key, $expiration = 0 ) {} - public function setByKey( $server_key, $key, $value, $expiration = 0 ) {} + public function setByKey( $server_key, $key, $value, $expiration = 0, $udf_flags = 0 ) {} - public function setMulti( array $items, $expiration = 0 ) {} + public function setMulti( array $items, $expiration = 0, $udf_flags = 0 ) {} - public function setMultiByKey( $server_key, array $items, $expiration = 0 ) {} + public function setMultiByKey( $server_key, array $items, $expiration = 0, $udf_flags = 0 ) {} - public function cas( $token, $key, $value, $expiration = 0 ) {} + public function cas( $token, $key, $value, $expiration = 0, $udf_flags = 0 ) {} - public function casByKey( $token, $server_key, $key, $value, $expiration = 0 ) {} + public function casByKey( $token, $server_key, $key, $value, $expiration = 0, $udf_flags = 0 ) {} - public function add( $key, $value, $expiration = 0 ) {} + public function add( $key, $value, $expiration = 0, $udf_flags = 0 ) {} - public function addByKey( $server_key, $key, $value, $expiration = 0 ) {} + public function addByKey( $server_key, $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function append( $key, $value ) {} @@ -229,9 +229,9 @@ public function prepend( $key, $value ) {} public function prependByKey( $server_key, $key, $value ) {} - public function replace( $key, $value, $expiration = 0 ) {} + public function replace( $key, $value, $expiration = 0, $udf_flags = 0 ) {} - public function replaceByKey( $server_key, $key, $value, $expiration = 0 ) {} + public function replaceByKey( $server_key, $key, $value, $expiration = 0, $udf_flags = 0 ) {} public function delete( $key, $time = 0 ) {} diff --git a/php_memcached.c b/php_memcached.c index 4074e591..5ad6e2b2 100644 --- a/php_memcached.c +++ b/php_memcached.c @@ -120,9 +120,14 @@ typedef unsigned long int uint32_t; /**************************************** Payload value flags ****************************************/ -#define MEMC_VAL_TYPE_MASK 0xf -#define MEMC_VAL_GET_TYPE(flags) ((flags) & MEMC_VAL_TYPE_MASK) -#define MEMC_VAL_SET_TYPE(flags, type) ((flags) |= ((type) & MEMC_VAL_TYPE_MASK)) +#define MEMC_CREATE_MASK(start, n_bits) (((1 << n_bits) - 1) << start) + +#define MEMC_MASK_TYPE MEMC_CREATE_MASK(0, 4) +#define MEMC_MASK_INTERNAL MEMC_CREATE_MASK(4, 12) +#define MEMC_MASK_USER MEMC_CREATE_MASK(16, 16) + +#define MEMC_VAL_GET_TYPE(flags) ((flags) & MEMC_MASK_TYPE) +#define MEMC_VAL_SET_TYPE(flags, type) ((flags) |= ((type) & MEMC_MASK_TYPE)) #define MEMC_VAL_IS_STRING 0 #define MEMC_VAL_IS_LONG 1 @@ -132,9 +137,21 @@ typedef unsigned long int uint32_t; #define MEMC_VAL_IS_IGBINARY 5 #define MEMC_VAL_IS_JSON 6 -#define MEMC_VAL_COMPRESSED (1<<4) -#define MEMC_VAL_COMPRESSION_ZLIB (1<<5) -#define MEMC_VAL_COMPRESSION_FASTLZ (1<<6) +#define MEMC_VAL_COMPRESSED (1<<0) +#define MEMC_VAL_COMPRESSION_ZLIB (1<<1) +#define MEMC_VAL_COMPRESSION_FASTLZ (1<<2) + +#define MEMC_VAL_GET_FLAGS(internal_flags) ((internal_flags & MEMC_MASK_INTERNAL) >> 4) +#define MEMC_VAL_SET_FLAG(internal_flags, internal_flag) ((internal_flags) |= ((internal_flag << 4) & MEMC_MASK_INTERNAL)) +#define MEMC_VAL_HAS_FLAG(internal_flags, internal_flag) ((MEMC_VAL_GET_FLAGS(internal_flags) & internal_flag) == internal_flag) +#define MEMC_VAL_DEL_FLAG(internal_flags, internal_flag) internal_flags &= ~((internal_flag << 4) & MEMC_MASK_INTERNAL) + +/**************************************** + User-defined flags +****************************************/ +#define MEMC_VAL_GET_USER_FLAGS(flags) ((flags & MEMC_MASK_USER) >> 16) +#define MEMC_VAL_SET_USER_FLAGS(flags, udf_flags) ((flags) |= ((udf_flags << 16) & MEMC_MASK_USER)) +#define MEMC_VAL_USER_FLAGS_MAX ((1 << 16) - 1) /**************************************** "get" operation flags @@ -507,7 +524,7 @@ static PHP_METHOD(Memcached, __construct) } /* }}} */ -/* {{{ Memcached::get(string key [, mixed callback [, double &cas_token ] ]) +/* {{{ Memcached::get(string key [, mixed callback [, double &cas_token [, int &udf_flags ] ] ]) Returns a value for the given key or false */ PHP_METHOD(Memcached, get) { @@ -515,7 +532,7 @@ PHP_METHOD(Memcached, get) } /* }}} */ -/* {{{ Memcached::getByKey(string server_key, string key [, mixed callback [, double &cas_token ] ]) +/* {{{ Memcached::getByKey(string server_key, string key [, mixed callback [, double &cas_token [, int &udf_flags ] ] ]) Returns a value for key from the server identified by the server key or false */ PHP_METHOD(Memcached, getByKey) { @@ -537,6 +554,7 @@ static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) const char* keys[1] = { NULL }; size_t key_lens[1] = { 0 }; zval *cas_token = NULL; + zval *udf_flags = NULL; zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcc = empty_fcall_info_cache; memcached_result_st result; @@ -544,13 +562,13 @@ static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) MEMC_METHOD_INIT_VARS; if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|f!z", &server_key, - &server_key_len, &key, &key_len, &fci, &fcc, &cas_token) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|f!zz", &server_key, + &server_key_len, &key, &key_len, &fci, &fcc, &cas_token, &udf_flags) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f!z", &key, &key_len, - &fci, &fcc, &cas_token) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f!zz", &key, &key_len, + &fci, &fcc, &cas_token, &udf_flags) == FAILURE) { return; } } @@ -572,13 +590,13 @@ static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) /* * Enable CAS support, but only if it is currently disabled. */ - if (cas_token && orig_cas_flag == 0) { + if (cas_token && PZVAL_IS_REF(cas_token) && orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); } status = memcached_mget_by_key(m_obj->memc, server_key, server_key_len, keys, key_lens, 1); - if (cas_token && orig_cas_flag == 0) { + if (cas_token && PZVAL_IS_REF(cas_token) && orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); } @@ -642,11 +660,16 @@ static void php_memc_get_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) ZVAL_DOUBLE(cas_token, (double)cas); } + if (udf_flags) { + zval_dtor(udf_flags); + ZVAL_LONG(udf_flags, MEMC_VAL_GET_USER_FLAGS(flags)); + } + memcached_result_free(&result); } /* }}} */ -/* {{{ Memcached::getMulti(array keys [, array &cas_tokens ]) +/* {{{ Memcached::getMulti(array keys [, array &cas_tokens [, array &udf_flags ] ]) Returns values for the given keys or false */ PHP_METHOD(Memcached, getMulti) { @@ -654,7 +677,7 @@ PHP_METHOD(Memcached, getMulti) } /* }}} */ -/* {{{ Memcached::getMultiByKey(string server_key, array keys [, array &cas_tokens ]) +/* {{{ Memcached::getMultiByKey(string server_key, array keys [, array &cas_tokens [, array &udf_flags ] ]) Returns values for the given keys from the server identified by the server key or false */ PHP_METHOD(Memcached, getMultiByKey) { @@ -679,6 +702,7 @@ static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke uint32_t flags; uint64_t cas = 0; zval *cas_tokens = NULL; + zval *udf_flags = NULL; uint64_t orig_cas_flag; zval *value; long get_flags = 0; @@ -689,12 +713,12 @@ static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke MEMC_METHOD_INIT_VARS; if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|zl", &server_key, - &server_key_len, &keys, &cas_tokens, &get_flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|zlz", &server_key, + &server_key_len, &keys, &cas_tokens, &get_flags, &udf_flags) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|zl", &keys, &cas_tokens, &get_flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/|zlz", &keys, &cas_tokens, &get_flags, &udf_flags) == FAILURE) { return; } } @@ -741,7 +765,7 @@ static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke /* * Enable CAS support, but only if it is currently disabled. */ - if (cas_tokens) { + if (cas_tokens && PZVAL_IS_REF(cas_tokens)) { orig_cas_flag = memcached_behavior_get(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS); if (orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, 1); @@ -755,7 +779,7 @@ static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke /* * Restore the CAS support flag, but only if we had to turn it on. */ - if (cas_tokens && orig_cas_flag == 0) { + if (cas_tokens && PZVAL_IS_REF(cas_tokens) && orig_cas_flag == 0) { memcached_behavior_set(m_obj->memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, orig_cas_flag); } @@ -767,8 +791,26 @@ static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke * returned as doubles, because we cannot store potential 64-bit values in longs. */ if (cas_tokens) { - zval_dtor(cas_tokens); - array_init(cas_tokens); + if (PZVAL_IS_REF(cas_tokens)) { + /* cas_tokens was passed by reference, we'll create an array for it. */ + zval_dtor(cas_tokens); + array_init(cas_tokens); + } else { + /* Not passed by reference, we allow this (eg.: if you specify null + to not enable cas but you want to use the udf_flags parameter). + We destruct it and set it to null for the peace of mind. */ + zval_dtor(cas_tokens); + cas_tokens = NULL; + } + } + + /* + * Iterate through the result set and create the result array. The flags are + * returned as longs. + */ + if (udf_flags) { + zval_dtor(udf_flags); + array_init(udf_flags); } memcached_result_create(m_obj->memc, &result); @@ -816,6 +858,9 @@ static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke cas = memcached_result_cas(&result); add_assoc_double_ex(cas_tokens, res_key, res_key_len+1, (double)cas); } + if (udf_flags) { + add_assoc_long_ex(udf_flags, res_key, res_key_len+1, MEMC_VAL_GET_USER_FLAGS(flags)); + } } memcached_result_free(&result); @@ -826,6 +871,10 @@ static void php_memc_getMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke zval_dtor(cas_tokens); ZVAL_NULL(cas_tokens); } + if (udf_flags) { + zval_dtor(udf_flags); + ZVAL_NULL(udf_flags); + } zval_dtor(return_value); RETURN_FALSE; } @@ -1022,6 +1071,9 @@ PHP_METHOD(Memcached, fetch) /* XXX: also check against ULLONG_MAX or memc_behavior */ add_assoc_double_ex(return_value, ZEND_STRS("cas"), (double)cas); } + if (MEMC_VAL_GET_USER_FLAGS(flags) != 0) { + add_assoc_long_ex(return_value, ZEND_STRS("flags"), MEMC_VAL_GET_USER_FLAGS(flags)); + } memcached_result_free(&result); } @@ -1078,6 +1130,9 @@ PHP_METHOD(Memcached, fetchAll) /* XXX: also check against ULLONG_MAX or memc_behavior */ add_assoc_double_ex(entry, ZEND_STRS("cas"), (double)cas); } + if (MEMC_VAL_GET_USER_FLAGS(flags) != 0) { + add_assoc_long_ex(entry, ZEND_STRS("flags"), MEMC_VAL_GET_USER_FLAGS(flags)); + } add_next_index_zval(return_value, entry); } @@ -1090,7 +1145,7 @@ PHP_METHOD(Memcached, fetchAll) } /* }}} */ -/* {{{ Memcached::set(string key, mixed value [, int expiration ]) +/* {{{ Memcached::set(string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key */ PHP_METHOD(Memcached, set) { @@ -1098,7 +1153,7 @@ PHP_METHOD(Memcached, set) } /* }}} */ -/* {{{ Memcached::setByKey(string server_key, string key, mixed value [, int expiration ]) +/* {{{ Memcached::setByKey(string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key on the server identified by the server key */ PHP_METHOD(Memcached, setByKey) { @@ -1125,7 +1180,7 @@ PHP_METHOD(Memcached, touchByKey) #endif -/* {{{ Memcached::setMulti(array items [, int expiration ]) +/* {{{ Memcached::setMulti(array items [, int expiration [, int udf_flags ] ]) Sets the keys/values specified in the items array */ PHP_METHOD(Memcached, setMulti) { @@ -1133,7 +1188,7 @@ PHP_METHOD(Memcached, setMulti) } /* }}} */ -/* {{{ Memcached::setMultiByKey(string server_key, array items [, int expiration ]) +/* {{{ Memcached::setMultiByKey(string server_key, array items [, int expiration [, int udf_flags ] ]) Sets the keys/values specified in the items array on the server identified by the given server key */ PHP_METHOD(Memcached, setMultiByKey) { @@ -1164,7 +1219,7 @@ PHP_METHOD(Memcached, setMultiByKey) } \ break; \ } \ - } \ + } /* {{{ -- php_memc_setMulti_impl */ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) @@ -1173,6 +1228,7 @@ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke char *server_key = NULL; int server_key_len = 0; time_t expiration = 0; + long udf_flags = 0; zval **entry; char *str_key; uint str_key_len; @@ -1186,12 +1242,12 @@ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke MEMC_METHOD_INIT_VARS; if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|l", &server_key, - &server_key_len, &entries, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|ll", &server_key, + &server_key_len, &entries, &expiration, &udf_flags) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &entries, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|ll", &entries, &expiration, &udf_flags) == FAILURE) { return; } } @@ -1199,6 +1255,16 @@ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke MEMC_METHOD_FETCH_OBJECT; i_obj->rescode = MEMCACHED_SUCCESS; + /* + * php_memcached uses 16 bits internally to store type, compression and serialization info. + * We use 16 upper bits to store user defined flags. + */ + if (udf_flags > 0) { + if ((uint32_t) udf_flags > MEMC_VAL_USER_FLAGS_MAX) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "udf_flags will be limited to %u", MEMC_VAL_USER_FLAGS_MAX); + } + } + for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(entries)); zend_hash_get_current_data(Z_ARRVAL_P(entries), (void**)&entry) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(entries))) { @@ -1218,7 +1284,11 @@ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke flags = 0; if (m_obj->compression) { - flags |= MEMC_VAL_COMPRESSED; + MEMC_VAL_SET_FLAG(flags, MEMC_VAL_COMPRESSED); + } + + if (udf_flags > 0) { + MEMC_VAL_SET_USER_FLAGS(flags, ((uint32_t) udf_flags)); } payload = php_memc_zval_to_payload(*entry, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); @@ -1246,7 +1316,7 @@ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke } /* }}} */ -/* {{{ Memcached::add(string key, mixed value [, int expiration ]) +/* {{{ Memcached::add(string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key, failing if the key already exists */ PHP_METHOD(Memcached, add) { @@ -1254,7 +1324,7 @@ PHP_METHOD(Memcached, add) } /* }}} */ -/* {{{ Memcached::addByKey(string server_key, string key, mixed value [, int expiration ]) +/* {{{ Memcached::addByKey(string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key on the server identified by the sever key, failing if the key already exists */ PHP_METHOD(Memcached, addByKey) { @@ -1294,7 +1364,7 @@ PHP_METHOD(Memcached, prependByKey) } /* }}} */ -/* {{{ Memcached::replace(string key, mixed value [, int expiration ]) +/* {{{ Memcached::replace(string key, mixed value [, int expiration [, int udf_flags ] ]) Replaces the value for the given key, failing if the key doesn't exist */ PHP_METHOD(Memcached, replace) { @@ -1302,7 +1372,7 @@ PHP_METHOD(Memcached, replace) } /* }}} */ -/* {{{ Memcached::replaceByKey(string server_key, string key, mixed value [, int expiration ]) +/* {{{ Memcached::replaceByKey(string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) Replaces the value for the given key on the server identified by the server key, failing if the key doesn't exist */ PHP_METHOD(Memcached, replaceByKey) { @@ -1323,6 +1393,7 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool zval s_zvalue; zval *value; long expiration = 0; + long udf_flags = 0; char *payload; size_t payload_len; uint32_t flags = 0; @@ -1345,8 +1416,8 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz|l", &server_key, - &server_key_len, &key, &key_len, &value, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssz|ll", &server_key, + &server_key_len, &key, &key_len, &value, &expiration, &udf_flags) == FAILURE) { return; } } @@ -1365,8 +1436,8 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &key, &key_len, - &value, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|ll", &key, &key_len, + &value, &expiration, &udf_flags) == FAILURE) { return; } } @@ -1390,7 +1461,18 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool php_error_docref(NULL TSRMLS_CC, E_WARNING, "cannot append/prepend with compression turned on"); return; } - flags |= MEMC_VAL_COMPRESSED; + MEMC_VAL_SET_FLAG(flags, MEMC_VAL_COMPRESSED); + } + + /* + * php_memcached uses 16 bits internally to store type, compression and serialization info. + * We use 16 upper bits to store user defined flags. + */ + if (udf_flags > 0) { + if ((uint32_t) udf_flags > MEMC_VAL_USER_FLAGS_MAX) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "udf_flags will be limited to %u", MEMC_VAL_USER_FLAGS_MAX); + } + MEMC_VAL_SET_USER_FLAGS(flags, ((uint32_t) udf_flags)); } if (op == MEMC_OP_TOUCH) { @@ -1492,6 +1574,7 @@ static void php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) int server_key_len = 0; zval *value; time_t expiration = 0; + long udf_flags = 0; char *payload; size_t payload_len; uint32_t flags = 0; @@ -1499,13 +1582,13 @@ static void php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) MEMC_METHOD_INIT_VARS; if (by_key) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dssz|l", &cas_d, &server_key, - &server_key_len, &key, &key_len, &value, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dssz|ll", &cas_d, &server_key, + &server_key_len, &key, &key_len, &value, &expiration, &udf_flags) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dsz|l", &cas_d, &key, &key_len, - &value, &expiration) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dsz|ll", &cas_d, &key, &key_len, + &value, &expiration, &udf_flags) == FAILURE) { return; } } @@ -1521,7 +1604,18 @@ static void php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) DVAL_TO_LVAL(cas_d, cas); if (m_obj->compression) { - flags |= MEMC_VAL_COMPRESSED; + MEMC_VAL_SET_FLAG(flags, MEMC_VAL_COMPRESSED); + } + + /* + * php_memcached uses 16 bits internally to store type, compression and serialization info. + * We use 16 upper bits to store user defined flags. + */ + if (udf_flags > 0) { + if ((uint32_t) udf_flags > MEMC_VAL_USER_FLAGS_MAX) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "udf_flags will be limited to %u", MEMC_VAL_USER_FLAGS_MAX); + } + MEMC_VAL_SET_USER_FLAGS(flags, ((uint32_t) udf_flags)); } payload = php_memc_zval_to_payload(value, &payload_len, &flags, m_obj->serializer, m_obj->compression_type TSRMLS_CC); @@ -1544,7 +1638,7 @@ static void php_memc_cas_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_key) } /* }}} */ -/* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration ]) +/* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration [, int udf_flags ] ]) Sets the value for the given key, failing if the cas_token doesn't match the one in memcache */ PHP_METHOD(Memcached, cas) { @@ -1552,7 +1646,7 @@ PHP_METHOD(Memcached, cas) } /* }}} */ -/* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration ]) +/* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration [, int udf_flags ] ]) 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) { @@ -2901,11 +2995,11 @@ static char *php_memc_zval_to_payload(zval *value, size_t *payload_len, uint32_t } /* turn off compression for values below the threshold */ - if ((*flags & MEMC_VAL_COMPRESSED) && l < MEMC_G(compression_threshold)) { - *flags &= ~MEMC_VAL_COMPRESSED; + if (MEMC_VAL_HAS_FLAG(*flags, MEMC_VAL_COMPRESSED) && l < MEMC_G(compression_threshold)) { + MEMC_VAL_DEL_FLAG(*flags, MEMC_VAL_COMPRESSED); } - if (*flags & MEMC_VAL_COMPRESSED) { + if (MEMC_VAL_HAS_FLAG(*flags, MEMC_VAL_COMPRESSED)) { /* status */ zend_bool compress_status = 0; @@ -2918,10 +3012,10 @@ static char *php_memc_zval_to_payload(zval *value, size_t *payload_len, uint32_t if (compression_type == COMPRESSION_TYPE_FASTLZ) { compress_status = ((payload_comp_len = fastlz_compress(p, l, payload_comp)) > 0); - *flags |= MEMC_VAL_COMPRESSION_FASTLZ; + MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSION_FASTLZ); } else if (compression_type == COMPRESSION_TYPE_ZLIB) { compress_status = (compress((Bytef *)payload_comp, &payload_comp_len, (Bytef *)p, l) == Z_OK); - *flags |= MEMC_VAL_COMPRESSION_ZLIB; + MEMC_VAL_SET_FLAG(*flags, MEMC_VAL_COMPRESSION_ZLIB); } if (!compress_status) { @@ -2939,7 +3033,7 @@ static char *php_memc_zval_to_payload(zval *value, size_t *payload_len, uint32_t payload[*payload_len] = 0; } else { /* Store plain value */ - *flags &= ~MEMC_VAL_COMPRESSED; + MEMC_VAL_DEL_FLAG(*flags, MEMC_VAL_COMPRESSED); *payload_len = l; memcpy(payload, p, l); payload[l] = 0; @@ -2964,7 +3058,7 @@ char *s_handle_decompressed (const char *payload, size_t *payload_len, uint32_t zend_bool decompress_status = 0; /* Stored with newer memcached extension? */ - if (flags & MEMC_VAL_COMPRESSION_FASTLZ || flags & MEMC_VAL_COMPRESSION_ZLIB) { + if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_FASTLZ) || MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_ZLIB)) { /* This is copied from Ilia's patch */ memcpy(&len, payload, sizeof(uint32_t)); buffer = emalloc(len + 1); @@ -2972,9 +3066,9 @@ char *s_handle_decompressed (const char *payload, size_t *payload_len, uint32_t payload += sizeof(uint32_t); length = len; - if (flags & MEMC_VAL_COMPRESSION_FASTLZ) { + if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_FASTLZ)) { decompress_status = ((length = fastlz_decompress(payload, *payload_len, buffer, len)) > 0); - } else if (flags & MEMC_VAL_COMPRESSION_ZLIB) { + } else if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSION_ZLIB)) { decompress_status = (uncompress((Bytef *)buffer, &length, (Bytef *)payload, *payload_len) == Z_OK); } } @@ -3029,7 +3123,7 @@ static int php_memc_zval_from_payload(zval *value, const char *payload_in, size_ return 0; } - if (flags & MEMC_VAL_COMPRESSED) { + if (MEMC_VAL_HAS_FLAG(flags, MEMC_VAL_COMPRESSED)) { char *datas = s_handle_decompressed (payload_in, &payload_len, flags TSRMLS_CC); if (!datas) { ZVAL_FALSE(value); @@ -3360,6 +3454,9 @@ static int php_memc_do_result_callback(zval *zmemc_obj, zend_fcall_info *fci, if (cas != 0) { add_assoc_double_ex(z_result, ZEND_STRS("cas"), (double)cas); } + if (MEMC_VAL_GET_USER_FLAGS(flags) != 0) { + add_assoc_long_ex(z_result, ZEND_STRS("flags"), MEMC_VAL_GET_USER_FLAGS(flags)); + } if (zend_call_function(fci, fcc TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not invoke result callback"); @@ -3391,27 +3488,31 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_get, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, cache_cb) - ZEND_ARG_INFO(1, cas_token) + ZEND_ARG_INFO(2, cas_token) + ZEND_ARG_INFO(1, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, cache_cb) - ZEND_ARG_INFO(1, cas_token) + ZEND_ARG_INFO(2, cas_token) + ZEND_ARG_INFO(1, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getMulti, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, keys, 0) - ZEND_ARG_INFO(1, cas_tokens) + ZEND_ARG_INFO(2, cas_tokens) ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(1, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getMultiByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_ARRAY_INFO(0, keys, 0) - ZEND_ARG_INFO(1, cas_tokens) + ZEND_ARG_INFO(2, cas_tokens) ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(1, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_getDelayed, 0, 0, 1) @@ -3437,6 +3538,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_set, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) + ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setByKey, 0, 0, 3) @@ -3444,6 +3546,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_setByKey, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) + ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_touch, 0, 0, 2) @@ -3460,18 +3563,21 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setMulti, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, items, 0) ZEND_ARG_INFO(0, expiration) + ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setMultiByKey, 0, 0, 2) ZEND_ARG_INFO(0, server_key) ZEND_ARG_ARRAY_INFO(0, items, 0) ZEND_ARG_INFO(0, expiration) + ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_add, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) + ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_addByKey, 0, 0, 3) @@ -3479,12 +3585,14 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_addByKey, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) + ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_replace, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) + ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_replaceByKey, 0, 0, 3) @@ -3492,6 +3600,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_replaceByKey, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) + ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_append, 0, 0, 2) @@ -3525,6 +3634,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_cas, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) + ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_casByKey, 0, 0, 4) @@ -3533,6 +3643,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_casByKey, 0, 0, 4) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, expiration) + ZEND_ARG_INFO(0, udf_flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_delete, 0, 0, 1) diff --git a/php_memcached.h b/php_memcached.h index 9eeb8a0e..49d5f041 100644 --- a/php_memcached.h +++ b/php_memcached.h @@ -91,7 +91,7 @@ ZEND_BEGIN_MODULE_GLOBALS(php_memcached) double compression_factor; #if HAVE_MEMCACHED_SASL - bool use_sasl; + zend_bool use_sasl; #endif ZEND_END_MODULE_GLOBALS(php_memcached) diff --git a/tests/cas.phpt b/tests/cas.phpt index fc14842b..09b436e0 100644 --- a/tests/cas.phpt +++ b/tests/cas.phpt @@ -30,6 +30,25 @@ if ($v !== 11) { echo "Wanted cas_test to be 11, value is: "; var_dump($v); } + +$v = $m->get('cas_test', null, 2); +if ($v != 11) { + echo "Failed to get the value with \$cas_token passed by value (2)\n"; + return; +} + +$v = $m->get('cas_test', null, null); +if ($v != 11) { + echo "Failed to get the value with \$cas_token passed by value (null)\n"; + return; +} + +$v = $m->get('cas_test', null, $data = array(2, 4)); +if ($v != 11 || $data !== array(2, 4)) { + echo "Failed to get the value with \$cas_token passed by value (\$data = array(2, 4))\n"; + return; +} + echo "OK\n"; ?> --EXPECT-- diff --git a/tests/cas_multi.phpt b/tests/cas_multi.phpt index d180d779..e4c9e0f3 100644 --- a/tests/cas_multi.phpt +++ b/tests/cas_multi.phpt @@ -45,6 +45,33 @@ foreach ($data as $key => $v) { } } +if (array_keys($actual) !== array_keys($data)) { + echo "missing value(s)\n"; + echo "data :"; + var_dump($data); + echo "actual data: "; + var_dump($actual); + return; +} + +$actual = $m->getMulti(array_keys($data), 2); +if (array_keys($actual) !== array_keys($data)) { + echo "Failed to getMulti \$cas_token passed by value (2)\n"; + return; +} + +$actual = $m->getMulti(array_keys($data), null); +if (array_keys($actual) !== array_keys($data)) { + echo "Failed to getMulti \$cas_token passed by value (null)\n"; + return; +} + +$actual = $m->getMulti(array_keys($data), $cas_tokens = array(2, 4)); +if (array_keys($actual) !== array_keys($data) || $cas_tokens !== array(2, 4)) { + echo "Failed to getMulti \$cas_token passed by value (\$cas_tokens = array(2, 4))\n"; + return; +} + echo "OK\n"; ?> diff --git a/tests/user-flags.phpt b/tests/user-flags.phpt new file mode 100644 index 00000000..4891bfd3 --- /dev/null +++ b/tests/user-flags.phpt @@ -0,0 +1,68 @@ +--TEST-- +Memcached user flags +--SKIPIF-- + +--FILE-- +setOption(Memcached::OPT_BINARY_PROTOCOL, true); +$m->addServer('127.0.0.1', 11211, 1); + +$key = uniqid ('udf_test_'); + +echo "stored with flags" . PHP_EOL; + +$m->set ($key, '1', 10, FLAG_1 | FLAG_4 | FLAG_64); +$udf_flags = 0; +$value = $m->get ($key, null, $x, $udf_flags); + +check_flags ($udf_flags, array (FLAG_1, FLAG_4, FLAG_64)); + +echo "stored without flags" . PHP_EOL; +$m->set ($key, '1'); +$value = $m->get ($key, null, $x, $udf_flags); + +var_dump ($udf_flags == 0); +$m->set ($key, '1', 10, FLAG_TOO_LARGE); + +$m->setOption(Memcached::OPT_COMPRESSION, true); +$m->setOption(Memcached::OPT_COMPRESSION_TYPE, Memcached::COMPRESSION_FASTLZ); + +$m->set ($key, str_repeat ("abcdef1234567890", 200), 10, FLAG_1 | FLAG_4 | FLAG_64); + +$udf_flags = 0; +$value_back = $m->get($key, null, null, $udf_flags); + +check_flags ($udf_flags, array (FLAG_1, FLAG_4, FLAG_64)); + +echo "DONE TEST\n"; +?> +--EXPECTF-- +stored with flags +Flags OK +stored without flags +bool(true) + +Warning: Memcached::set(): udf_flags will be limited to 65535 in %s on line %d +Flags OK +DONE TEST \ No newline at end of file