@@ -135,7 +135,8 @@ typedef enum {
135135	MEMC_OP_ADD ,
136136	MEMC_OP_REPLACE ,
137137	MEMC_OP_APPEND ,
138- 	MEMC_OP_PREPEND 
138+ 	MEMC_OP_PREPEND ,
139+ 	MEMC_OP_CAS 
139140} php_memc_write_op ;
140141
141142typedef  struct  {
@@ -400,7 +401,7 @@ static
400401	memcached_return  s_server_cursor_version_cb (const  memcached_st  * ptr , php_memcached_instance_st  instance , void  * in_context );
401402
402403static 
403- 	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 );
404+ 	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 );
404405
405406static 
406407	void  php_memc_destroy (memcached_st  * memc , php_memc_user_data_t  * memc_user_data );
@@ -750,44 +751,32 @@ zend_bool s_invoke_cache_callback(zval *zobject, zend_fcall_info *fci, zend_fcal
750751	php_memc_object_t  * intern  =  Z_MEMC_OBJ_P (zobject );
751752
752753	/* Prepare params */ 
753- 	ZVAL_COPY (& params [0 ], zobject );
754+ 	ZVAL_COPY (& params [0 ], zobject );     /* memc */ 
754755	ZVAL_STR_COPY (& params [1 ], key );    /* key */ 
755756	ZVAL_NEW_REF (& params [2 ], value );   /* value */ 
757+ 	ZVAL_NEW_EMPTY_REF (& params [3 ]);    /* expiration */ 
758+ 	ZVAL_NULL (Z_REFVAL (params [3 ]));
756759
757- 	if  (with_cas ) {
758- 		fci -> param_count  =  3 ;
759- 	} else  {
760- 		ZVAL_NEW_EMPTY_REF (& params [3 ]);    /* expiration */ 
761- 		ZVAL_NULL (Z_REFVAL (params [3 ]));
762- 		fci -> param_count  =  4 ;
763- 	}
764- 
760+ 	fci -> param_count  =  4 ;
765761	fci -> retval  =  & retval ;
766762	fci -> params  =  params ;
767763
768- 	if  (zend_call_function (fci , fcc ) ==  SUCCESS ) {
769- 		if  (zend_is_true (& retval )) {
770- 			time_t  expiration ;
771- 			zval  * val  =  Z_REFVAL (params [2 ]);
772- 
773- 			if  (with_cas ) {
774- 				if  (Z_TYPE_P (val ) ==  IS_ARRAY ) {
775- 					zval  * rv  =  zend_hash_str_find (Z_ARRVAL_P (val ), "value" , sizeof ("value" ) -  1 );
776- 					if  (rv ) {
777- 						zval  * cas  =  zend_hash_str_find (Z_ARRVAL_P (val ), "cas" , sizeof ("cas" ) - 1 );
778- 						expiration  =  cas ? Z_LVAL_P (cas ) : 0 ;
779- 						status  =  s_memc_write_zval  (intern , MEMC_OP_SET , NULL , key , rv , expiration );
780- 					}
781- 					/* memleak?  zval_ptr_dtor(value); */ 
782- 					ZVAL_COPY (value , val );
783- 				}
784- 			} else  {
785- 				expiration  =  zval_get_long (Z_REFVAL (params [3 ]));
786- 				status  =  s_memc_write_zval  (intern , MEMC_OP_SET , NULL , key , val , expiration );
787- 				/* memleak?  zval_ptr_dtor(value); */ 
788- 				ZVAL_COPY (value , val );
789- 			}
764+ 	if  (zend_call_function (fci , fcc ) ==  SUCCESS  &&  zend_is_true (& retval )) {
765+ 		time_t  expiration ;
766+ 		zval  * val  =  Z_REFVAL (params [2 ]);
767+ 		zval  * rv  =  NULL ;
768+ 		zval  * zv_cas  =  NULL ;
769+ 		uint64_t  cas ;
770+ 
771+ 		if  (Z_TYPE_P (val ) ==  IS_ARRAY ) {
772+ 			rv  =  zend_hash_str_find (Z_ARRVAL_P (val ), "value" , sizeof ("value" ) -  1 );
773+ 			zv_cas  =  zend_hash_str_find (Z_ARRVAL_P (val ), "cas" , sizeof ("cas" ) -  1 );
790774		}
775+ 
776+ 		expiration  =  zval_get_long (Z_REFVAL (params [3 ]));
777+ 		cas  =  zv_cas  ? s_zval_to_uint64 (zv_cas ) : 0 ;
778+ 		status  =  s_memc_write_zval  (intern , MEMC_OP_SET , NULL , key , rv  ? rv  : val , expiration , cas );
779+ 		ZVAL_COPY (value , val );
791780	}
792781	else  {
793782		s_memc_set_status (intern , MEMCACHED_NOTFOUND , 0 );
@@ -1054,7 +1043,7 @@ zend_bool s_should_retry_write (php_memc_object_t *intern, memcached_return stat
10541043}
10551044
10561045static 
1057- 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 )
1046+ 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 )
10581047{
10591048	uint32_t  flags  =  0 ;
10601049	zend_string  * payload  =  NULL ;
@@ -1099,6 +1088,10 @@ zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, ze
10991088			case  MEMC_OP_PREPEND :
11001089				status  =  memc_write_using_fn_by_key (memcached_prepend_by_key );
11011090			break ;
1091+ 
1092+ 			case  MEMC_OP_CAS :
1093+ 				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 );
1094+ 			break ;
11021095		}
11031096
11041097		if  (status  ==  MEMCACHED_END ) {
@@ -1131,6 +1124,10 @@ zend_bool s_memc_write_zval (php_memc_object_t *intern, php_memc_write_op op, ze
11311124			case  MEMC_OP_PREPEND :
11321125				status  =  memc_write_using_fn (memcached_prepend );
11331126			break ;
1127+ 
1128+ 			case  MEMC_OP_CAS :
1129+ 				status  =  memcached_cas (intern -> memc , ZSTR_VAL (key ), ZSTR_LEN (key ), ZSTR_VAL (payload ), ZSTR_LEN (payload ), expiration , flags , cas );
1130+ 			break ;
11341131		}
11351132		if  (status  ==  MEMCACHED_END ) {
11361133			status  =  MEMCACHED_SUCCESS ;
@@ -1820,7 +1817,7 @@ static void php_memc_setMulti_impl(INTERNAL_FUNCTION_PARAMETERS, zend_bool by_ke
18201817			str_key  =  zend_string_init (tmp_key , tmp_len , 0 );
18211818		}
18221819
1823- 		if  (!s_memc_write_zval  (intern , MEMC_OP_SET , server_key , str_key , value , expiration )) {
1820+ 		if  (!s_memc_write_zval  (intern , MEMC_OP_SET , server_key , str_key , value , expiration ,  0 )) {
18241821			php_error_docref (NULL , E_WARNING , "failed to set key %s" , ZSTR_VAL (str_key ));
18251822		}
18261823
@@ -1898,6 +1895,22 @@ PHP_METHOD(Memcached, replaceByKey)
18981895}
18991896/* }}} */ 
19001897
1898+ /* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration ]) 
1899+    Sets the value for the given key, failing if the cas_token doesn't match the one in memcache */ 
1900+ PHP_METHOD (Memcached , cas )
1901+ {
1902+ 	php_memc_store_impl (INTERNAL_FUNCTION_PARAM_PASSTHRU , MEMC_OP_CAS , 0 );
1903+ }
1904+ /* }}} */ 
1905+ 
1906+ /* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration ]) 
1907+    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 */ 
1908+ PHP_METHOD (Memcached , casByKey )
1909+ {
1910+ 	php_memc_store_impl (INTERNAL_FUNCTION_PARAM_PASSTHRU , MEMC_OP_CAS , 1 );
1911+ }
1912+ /* }}} */ 
1913+ 
19011914/* {{{ -- php_memc_store_impl */ 
19021915static  void  php_memc_store_impl (INTERNAL_FUNCTION_PARAMETERS , int  op , zend_bool  by_key )
19031916{
@@ -1906,7 +1919,9 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool
19061919	zend_string  * s_value ;
19071920	zval  s_zvalue ;
19081921	zval  * value  =  NULL ;
1922+ 	zval  * zv_cas  =  NULL ;
19091923	zend_long  expiration  =  0 ;
1924+ 	uint64_t  cas  =  0 ;
19101925	MEMC_METHOD_INIT_VARS ;
19111926
19121927	if  (by_key ) {
@@ -1920,6 +1935,11 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool
19201935			if  (zend_parse_parameters (ZEND_NUM_ARGS (), "SS|l" , & server_key , & key , & expiration ) ==  FAILURE ) {
19211936				return ;
19221937			}
1938+ 		} else  if  (op  ==  MEMC_OP_CAS ) {
1939+ 			if  (zend_parse_parameters (ZEND_NUM_ARGS (), "zSSz|l" , & zv_cas , & server_key , & key , & value , & expiration ) ==  FAILURE ) {
1940+ 				return ;
1941+ 			}
1942+ 			cas  =  s_zval_to_uint64 (zv_cas );
19231943		} else  {
19241944			if  (zend_parse_parameters (ZEND_NUM_ARGS (), "SSz|l" , & server_key , & key , & value , & expiration ) ==  FAILURE ) {
19251945				return ;
@@ -1936,6 +1956,11 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool
19361956			if  (zend_parse_parameters (ZEND_NUM_ARGS (), "S|l" , & key , & expiration ) ==  FAILURE ) {
19371957				return ;
19381958			}
1959+ 		} else  if  (op  ==  MEMC_OP_CAS ) {
1960+ 			if  (zend_parse_parameters (ZEND_NUM_ARGS (), "zSz|l" , & zv_cas , & key , & value , & expiration ) ==  FAILURE ) {
1961+ 				return ;
1962+ 			}
1963+ 			cas  =  s_zval_to_uint64 (zv_cas );
19391964		} else  {
19401965			if  (zend_parse_parameters (ZEND_NUM_ARGS (), "Sz|l" , & key , & value , & expiration ) ==  FAILURE ) {
19411966				return ;
@@ -1959,82 +1984,13 @@ static void php_memc_store_impl(INTERNAL_FUNCTION_PARAMETERS, int op, zend_bool
19591984		}
19601985	}
19611986
1962- 	if  (!s_memc_write_zval  (intern , op , server_key , key , value , expiration )) {
1987+ 	if  (!s_memc_write_zval  (intern , op , server_key , key , value , expiration ,  cas )) {
19631988		RETURN_FALSE ;
19641989	}
19651990	RETURN_TRUE ;
19661991}
19671992/* }}} */ 
19681993
1969- /* {{{ -- php_memc_cas_impl */ 
1970- static  void  php_memc_cas_impl (INTERNAL_FUNCTION_PARAMETERS , zend_bool  by_key )
1971- {
1972- 	zval  * zv_cas ;
1973- 	uint64_t  cas ;
1974- 	zend_string  * key ;
1975- 	zend_string  * server_key  =  NULL ;
1976- 	zval  * value ;
1977- 	time_t  expiration  =  0 ;
1978- 	zend_string  * payload ;
1979- 	uint32_t  flags  =  0 ;
1980- 	memcached_return  status ;
1981- 	MEMC_METHOD_INIT_VARS ;
1982- 
1983- 	if  (by_key ) {
1984- 		if  (zend_parse_parameters (ZEND_NUM_ARGS (), "zSSz|ll" , & zv_cas , & server_key , & key ,
1985- 								  & value , & expiration ) ==  FAILURE ) {
1986- 			return ;
1987- 		}
1988- 	} else  {
1989- 		if  (zend_parse_parameters (ZEND_NUM_ARGS (), "zSz|ll" , & zv_cas , & key , & value ,
1990- 								  & expiration ) ==  FAILURE ) {
1991- 			return ;
1992- 		}
1993- 	}
1994- 
1995- 	MEMC_METHOD_FETCH_OBJECT ;
1996- 	s_memc_set_status (intern , MEMCACHED_SUCCESS , 0 );
1997- 	MEMC_CHECK_KEY (intern , key );
1998- 
1999- 	cas  =  s_zval_to_uint64 (zv_cas );
2000- 
2001- 	payload  =  s_zval_to_payload (intern , value , & flags );
2002- 	if  (payload  ==  NULL ) {
2003- 		intern -> rescode  =  MEMC_RES_PAYLOAD_FAILURE ;
2004- 		RETURN_FALSE ;
2005- 	}
2006- 
2007- 	if  (by_key ) {
2008- 		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 );
2009- 	} else  {
2010- 		status  =  memcached_cas (intern -> memc , ZSTR_VAL (key ), ZSTR_LEN (key ), ZSTR_VAL (payload ), ZSTR_LEN (payload ), expiration , flags , cas );
2011- 	}
2012- 
2013- 	zend_string_release (payload );
2014- 	if  (s_memc_status_handle_result_code (intern , status ) ==  FAILURE ) {
2015- 		RETURN_FALSE ;
2016- 	}
2017- 
2018- 	RETURN_TRUE ;
2019- }
2020- /* }}} */ 
2021- 
2022- /* {{{ Memcached::cas(double cas_token, string key, mixed value [, int expiration ]) 
2023-    Sets the value for the given key, failing if the cas_token doesn't match the one in memcache */ 
2024- PHP_METHOD (Memcached , cas )
2025- {
2026- 	php_memc_cas_impl (INTERNAL_FUNCTION_PARAM_PASSTHRU , 0 );
2027- }
2028- /* }}} */ 
2029- 
2030- /* {{{ Memcached::casByKey(double cas_token, string server_key, string key, mixed value [, int expiration ]) 
2031-    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 */ 
2032- PHP_METHOD (Memcached , casByKey )
2033- {
2034- 	php_memc_cas_impl (INTERNAL_FUNCTION_PARAM_PASSTHRU , 1 );
2035- }
2036- /* }}} */ 
2037- 
20381994/* {{{ Memcached::delete(string key [, int time ]) 
20391995   Deletes the given key */ 
20401996PHP_METHOD (Memcached , delete )
0 commit comments